//
// $Id: Algorithm.m,v 1.30 2005/05/18 06:46:52 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 "Algorithm.h"
#import "Macros.h"
#import <pthread.h>
#import <Foundation/NSValue.h>
#import <Foundation/NSString.h>
#if defined(GNUSTEP)
#import "Comparison.h"
#else
#import <Foundation/NSScriptWhoseTests.h>
#endif
#import <stdlib.h>

// Macros {{{
#define ALG_ALLOC [OLAlgorithm allocator]
#define ALG_COND_AUTORELEASE(obj) [ alloc autorelease: obj ]
#define OL_SORT_THRESHOLD 16
#define OL_CHUNK_SIZE 7
// }}}

// Static data {{{
static NSString* const __bufferJunk = @"will_mason's_mas_vale_cholo_buffer_junk";
static pthread_key_t __algoAllocatorKey;
static pthread_once_t __algoOnce = PTHREAD_ONCE_INIT;
// }}}

// Static functions {{{
static void __releaseAlgoAllocator(void* alloc)
{
    OBJ_RELEASE((OLAllocator*)alloc);
}

static void __initializeAlgoAllocatorKey()
{
    pthread_key_create(&__algoAllocatorKey, __releaseAlgoAllocator);
}
// }}}

// interface OLTempBuf {{{
@interface OLTempBuf : NSObject
{
@private
    id*                 buffer;
    OLArrayIterator*    begin;
    OLArrayIterator*    end;
    unsigned            size;
}

- (id) initWithFirst: (OLForwardIterator*)first last: (OLForwardIterator*)last;
- (void) dealloc;

- (OLArrayIterator*) begin;
- (OLArrayIterator*) end;
- (unsigned) size;

@end
// }}}

// interface OLAlgorithm (PrivateMethods) {{{
@interface OLAlgorithm (PrivateMethods)

+ (void) adjustHeapFirst: (OLRandomAccessIterator*)first hole: (unsigned)hole len: (unsigned)len value: (id)object predicate: (id<OLBoolBinaryFunction>)pred;

+ (OLForwardIterator*) boundImplFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last value: (id)object predicate: (id<OLBoolBinaryFunction>)pred lower: (BOOL)lwr;

+ (void) chunkInsertionSortFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last chunkSize: (unsigned)size predicate: (id<OLBoolBinaryFunction>)pred;

+ (OLBidirectionalIterator*) copyBackwardImplFrom: (OLBidirectionalIterator*)first to: (OLBidirectionalIterator*)last destination: (OLBidirectionalIterator*)dest needItor: (BOOL)needItor;

+ (OLForwardIterator*) copyImplFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last destination: (OLForwardIterator*)dest needItor: (BOOL)needItor;

+ (void) finalInsertionSortFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last predicate: (id<OLBoolBinaryFunction>)pred;

+ (OLForwardIterator*) findIfImplFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last predicate: (id<OLBoolUnaryFunction>)pred;

+ (OLForwardIterator*) findImplFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last value: (id)object;

+ (void) insertionSortFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last predicate: (id<OLBoolBinaryFunction>)pred;

+ (void) introSortLoopFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last depthLimit: (unsigned)depthLimit predicate: (id<OLBoolBinaryFunction>)pred;

+ (void) linearInsertFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last value: (id)object predicate: (id<OLBoolBinaryFunction>)pred;

+ (id) medianFromOne: (id)one two: (id)two three: (id)three predicate: (id<OLBoolBinaryFunction>)pred;

+ (void) mergeAdaptiveFrom: (OLBidirectionalIterator*)first
                            middle: (OLBidirectionalIterator*)middle
                            last: (OLBidirectionalIterator*)last
                            len1: (unsigned)len1
                            len2: (unsigned)len2
                            buffer: (OLTempBuf*)buf
                            predicate: (id<OLBoolBinaryFunction>)pred;

+ (OLBidirectionalIterator*) mergeBackwardSeries1From: (OLBidirectionalIterator*)first1 series1To: (OLBidirectionalIterator*)last1 series2From: (OLBidirectionalIterator*)first2 series2To: (OLBidirectionalIterator*)last2 destination: (OLBidirectionalIterator*)result predicate: (id<OLBoolBinaryFunction>)pred needItor: (BOOL)needItor;

+ (OLForwardIterator*) mergeImplSeries1From: (OLForwardIterator*)first1 series1To: (OLForwardIterator*)last1 series2From: (OLForwardIterator*)first2 series2To: (OLForwardIterator*)last2 destination: (OLForwardIterator*)dest predicate: (id<OLBoolBinaryFunction>)pred needItor: (BOOL)needItor;

+ (void) mergeSortLoopFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last destination: (OLRandomAccessIterator*)dest stepSize: (unsigned)stepSize predicate: (id<OLBoolBinaryFunction>)pred;

+ (void) mergeSortWithBufferFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last buffer: (OLTempBuf*)buf predicate: (id<OLBoolBinaryFunction>)pred;

+ (void) popHeapImplFirst: (OLRandomAccessIterator*)first last: (OLRandomAccessIterator*)last result: (OLRandomAccessIterator*)result value: (id)object predicate: (id<OLBoolBinaryFunction>)pred;

+ (void) pushHeapImplFirst: (OLRandomAccessIterator*)first hole: (unsigned)hole top: (unsigned)top value: (id)object predicate: (id<OLBoolBinaryFunction>)pred;

+ (OLBidirectionalIterator*) rotateAdaptiveFirst: (OLBidirectionalIterator*)first
        middle: (OLBidirectionalIterator*)middle
        last: (OLBidirectionalIterator*)last
        len1: (unsigned)len1
        len2: (unsigned)len2
        buffer: (OLTempBuf*)buf;

+ (OLForwardIterator*) rotateImplFrom: (OLForwardIterator*)first middle: (OLForwardIterator*)mid to: (OLForwardIterator*)last;

+ (OLForwardIterator*) stablePartitionAdaptiveFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last predicate: (id<OLBoolUnaryFunction>)pred length: (unsigned)len buffer: (OLTempBuf*)buf;

+ (void) stableSortAdaptiveFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last buffer: (OLTempBuf*)buf predicate: (id<OLBoolBinaryFunction>)pred;

+ (void) unguardedLinearInsertAt: (OLRandomAccessIterator*)last value: (id)object predicate: (id<OLBoolBinaryFunction>)pred;

+ (void) unguardedInsertionSortFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last predicate: (id<OLBoolBinaryFunction>)pred;

+ (OLRandomAccessIterator*) unguardedPartitionFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last pivot: (id)object predicate: (id<OLBoolBinaryFunction>)pred;

@end
// }}}

// implementation OLAlgorithm {{{
@implementation OLAlgorithm

// adjacentFindFrom:to: {{{
+ (OLForwardIterator*) adjacentFindFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last
{
    OLAllocator* alloc = ALG_ALLOC;
    OLEqualTo* equal = [[alloc allocateObject: [OLEqualTo class]] init];
    OLForwardIterator* result = [OLAlgorithm adjacentFindFrom: first to: last predicate: equal];

    [alloc deallocateObject: equal];
    return result;
}
// }}}

// adjacentFindFrom:to:predicate: {{{
+ (OLForwardIterator*) adjacentFindFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last predicate: (id<OLBoolBinaryFunction>) pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* result = nil;
    OLForwardIterator* cur;
    OLForwardIterator* next;

    if ([first isEqual: last])
    {
        result = [last copyWithAllocator: alloc];
    }
    else
    {
        cur = [first copyWithAllocator: alloc];
        next = [first copyWithAllocator: alloc];
        while (![[next advance] isEqual: last])
        {
            if ([pred performBinaryFunctionWithArg: [cur dereference]
                    andArg: [next dereference]])
            {
                result = cur;
                break;
            }
            [cur advance];
        }
        if (result == nil)
        {
            result = [last copyWithAllocator: alloc];
            [alloc deallocateObject: cur];
        }
        [alloc deallocateObject: next];
    }
    return ALG_COND_AUTORELEASE(result);
}
// }}}

// allocator {{{
+ (OLAllocator*) allocator
{
    OLAllocator* alloc;

    alloc = pthread_getspecific(__algoAllocatorKey);
    if (alloc == NULL)
    {
        alloc = [[[OLAllocator defaultAllocator] class] new];
        pthread_setspecific(__algoAllocatorKey, alloc);
    }
    return alloc;
}
// }}}

// binarySearchFrom:to:value: {{{
+ (BOOL) binarySearchFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last value: (id)object
{
    OLAllocator* alloc = ALG_ALLOC;
    OLLess* less = [[alloc allocateObject: [OLLess class]] init];
    BOOL result = [OLAlgorithm binarySearchFrom: first to: last value: object predicate: less];

    [alloc deallocateObject: less];
    return result;
}
// }}}

// binarySearchFrom:to:value:predicate: {{{
+ (BOOL) binarySearchFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last value: (id)object predicate: (id<OLBoolBinaryFunction>)pred
{
    OLForwardIterator* itor =
        [OLAlgorithm boundImplFrom: first to: last value: object predicate: pred lower: YES];
    BOOL result = ![itor isEqual: last] &&
        ![pred performBinaryFunctionWithArg: object andArg: [itor dereference]];

    [ALG_ALLOC deallocateObject: itor];
    return result;
}
// }}}

// copyBackwardFrom:to:destination: {{{
+ (OLBidirectionalIterator*) copyBackwardFrom: (OLBidirectionalIterator*)first to: (OLBidirectionalIterator*)last destination: (OLBidirectionalIterator*)dest
{
    return [ALG_ALLOC autorelease: 
        [OLAlgorithm copyBackwardImplFrom: first to: last destination: dest needItor: YES]];
}
// }}}

// copyFrom:to:destination: {{{
+ (OLForwardIterator*) copyFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last destination: (OLForwardIterator*)dest
{
    return [ALG_ALLOC autorelease:
        [OLAlgorithm copyImplFrom: first to: last destination: dest needItor: YES]];
}
// }}}

// countFrom:to:predicate: {{{
+ (unsigned) countFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last predicate: (id<OLBoolUnaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* src = [first copyWithAllocator: alloc];
    unsigned result = 0;

    for ( ; ![src isEqual: last]; [src advance])
    {
        if ([pred performUnaryFunctionWithArg: [src dereference]])
            result++;
    }
    [alloc deallocateObject: src];
    return result;
}
// }}}

// countFrom:to:value: {{{
+ (unsigned) countFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last value: (id)object
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* src = [first copyWithAllocator: alloc];
    unsigned result = 0;

    for ( ; ![src isEqual: last]; [src advance])
    {
        if ([[src dereference] isEqual: object])
            result++;
    }
    [alloc deallocateObject: src];
    return result;
}
// }}}

// equalFrom:to:with: {{{
+ (BOOL) equalFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last with: (OLForwardIterator*)first2
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* one = [first copyWithAllocator: alloc];
    OLForwardIterator* two = [first2 copyWithAllocator: alloc];
    BOOL equal = YES;

    for ( ; ![one isEqual: last]; [one advance], [two advance])
    {
        if (![[one dereference] isEqual: [two dereference]])
        {
            equal = NO;
            break;
        }
    }
    [alloc deallocateObject: one];
    [alloc deallocateObject: two];
    return equal;
}
// }}}

// equalFrom:to:with:predicate: {{{
+ (BOOL) equalFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last with: (OLForwardIterator*)first2 predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* one = [first copyWithAllocator: alloc];
    OLForwardIterator* two = [first2 copyWithAllocator: alloc];
    BOOL equal = YES;

    for ( ; ![one isEqual: last]; [one advance], [two advance])
    {
        if (![pred performBinaryFunctionWithArg: [one dereference] andArg: [two dereference]])
        {
            equal = NO;
            break;
        }
    }
    [alloc deallocateObject: one];
    [alloc deallocateObject: two];
    return equal;
}
// }}}

// equalRangeFrom:to:value: {{{
+ (OLPair*) equalRangeFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last value: (id)object
{
    OLAllocator* alloc = ALG_ALLOC;
    OLLess* less = [[alloc allocateObject: [OLLess class]] init];
    OLPair* result = [OLAlgorithm equalRangeFrom: first to: last value: object predicate: less];

    [alloc deallocateObject: less];
    return result;
}
// }}}

// equalRangeFrom:to:value:predicate: {{{
+ (OLPair*) equalRangeFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last value: (id)object predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* cur = [first copyWithAllocator: alloc];
    OLForwardIterator* middle = [first copyWithAllocator: alloc];
    OLForwardIterator* left;
    OLForwardIterator* right;
    OLPair* result = nil;
    unsigned len = [OLIterator distanceFrom: first to: last allocator: alloc];
    unsigned half;
    BOOL changeMiddle = NO;

    while (len > 0)
    {
        half = len >> 1;
        if (changeMiddle)
        {
            [alloc deallocateObject: middle];
            middle = [cur copyWithAllocator: alloc];
            changeMiddle = NO;
        }
        [OLIterator advanceIterator: middle distance: half];
        if ([pred performBinaryFunctionWithArg: [middle dereference] andArg: object])
        {
            [alloc deallocateObject: cur];
            cur = [middle copyWithAllocator: alloc];
            [cur advance];
            len = len - half - 1;
            changeMiddle = YES;
        }
        else if ([pred performBinaryFunctionWithArg: object andArg: [middle dereference]])
        {
            len = half;
        }
        else
        {
            left = [OLAlgorithm boundImplFrom: cur to: middle
                value: object predicate: pred lower: YES];
            [OLIterator advanceIterator: cur distance: len];
            right = [OLAlgorithm boundImplFrom: [middle advance] to: cur
                value: object predicate: pred lower: NO];
            result = [[OLPair alloc] initWithFirst: left second: right];
            [alloc deallocateObject: left];
            [alloc deallocateObject: right];
            break;
        }
    }
    if (result == nil)
        result = [[OLPair alloc] initWithFirst: cur second: cur];
    [alloc deallocateObject: cur];
    [alloc deallocateObject: middle];
    return ALG_COND_AUTORELEASE(result);
}
// }}}

// fillFrom:count:value: {{{
+ (void) fillFrom: (OLForwardIterator*)first count: (unsigned)num value: (id)object
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* dst = [first copyWithAllocator: alloc];
    unsigned i;

    for (i = 0; i < num; i++, [dst advance])
        [dst assign: object];
    [alloc deallocateObject: dst];
}
// }}}

// fillFrom:to:value: {{{
+ (void) fillFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last value: (id)object
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* dst = [first copyWithAllocator: alloc];

    for ( ; ![dst isEqual: last]; [dst advance])
        [dst assign: object];
    [alloc deallocateObject: dst];
}
// }}}

// findEndFrom:to:subFrom:subTo: {{{
+ (OLForwardIterator*) findEndFrom: (OLForwardIterator*)first1 to: (OLForwardIterator*)last1 subFrom: (OLForwardIterator*)first2 subTo: (OLForwardIterator*)last2
{
    OLAllocator* alloc = ALG_ALLOC;
    OLEqualTo* equal = [[alloc allocateObject: [OLEqualTo class]] init];
    OLForwardIterator* result =
        [OLAlgorithm findEndFrom: first1 to: last1
        subFrom: first2 subTo: last2 predicate: equal];

    [alloc deallocateObject: equal];
    return result;
}
// }}}

// findEndFrom:to:subFrom:subTo:predicate: {{{
+ (OLForwardIterator*) findEndFrom: (OLForwardIterator*)first1 to: (OLForwardIterator*)last1 subFrom: (OLForwardIterator*)first2 subTo: (OLForwardIterator*)last2 predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* result;
    OLForwardIterator* newResult;
    OLForwardIterator* f1;

    if ([first2 isEqual: last2])
        return ALG_COND_AUTORELEASE([last1 copyWithAllocator: alloc]);
    result = nil;
    f1 = [first1 copyWithAllocator: alloc];
    while (YES)
    {
        newResult = [OLAlgorithm searchFrom: f1 to: last1
            subFrom: first2 subTo: last2 predicate: pred];
        if ([newResult isEqual: last1])
        {
            if (result == nil)
                result = ALG_COND_AUTORELEASE([last1 copyWithAllocator: alloc]);
            break;
        }
        else
        {
            result = newResult;
            [alloc deallocateObject: f1];
            f1 = [newResult copyWithAllocator: alloc];
            [f1 advance];
        }
    }
    [alloc deallocateObject: f1];
    return result;
}
// }}}

// findFirstFrom:to:ofFrom:ofTo: {{{
+ (OLForwardIterator*) findFirstFrom: (OLForwardIterator*)first1 to: (OLForwardIterator*)last1 ofFrom: (OLForwardIterator*)first2 ofTo: (OLForwardIterator*)last2
{
    OLAllocator* alloc = ALG_ALLOC;
    OLEqualTo* equal = [[alloc allocateObject: [OLEqualTo class]] init];
    OLForwardIterator* result =
        [OLAlgorithm findFirstFrom: first1 to: last1
        ofFrom: first2 ofTo: last2 predicate: equal];

    [alloc deallocateObject: equal];
    return result;
}
// }}}

// findFirstFrom:to:ofFrom:ofTo:predicate: {{{
+ (OLForwardIterator*) findFirstFrom: (OLForwardIterator*)first1 to: (OLForwardIterator*)last1 ofFrom: (OLForwardIterator*)first2 ofTo: (OLForwardIterator*)last2 predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* f;
    id* series;
    unsigned count;
    unsigned i;

    if ([first1 isEqual: last1] || [first2 isEqual: last2])
        return ALG_COND_AUTORELEASE([last1 copyWithAllocator: alloc]);

    count = [OLIterator distanceFrom: first2 to: last2 allocator: alloc];
    series = [alloc allocateBytes: count * sizeof(id)];
    for (f = [first2 copyWithAllocator: alloc], i = 0; i < count; [f advance], i++)
        series[i] = [f dereference];
    [alloc deallocateObject: f];
    for (f = [first1 copyWithAllocator: alloc]; ![f isEqual: last1]; [f advance])
    {
        for (i = 0; i < count; i++)
        {
            if ([pred performBinaryFunctionWithArg: [f dereference]
                    andArg: series[i]])
            {
                goto leaveFindFirstOf;
            }
        }
    }
leaveFindFirstOf:
    [alloc deallocateBytes: series];
    return ALG_COND_AUTORELEASE(f);
}
// }}}

// findFrom:to:if: {{{
+ (OLForwardIterator*) findFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last if: (id<OLBoolUnaryFunction>)pred
{
    return [ALG_ALLOC autorelease:
        [OLAlgorithm findIfImplFrom: first to: last predicate: pred]];
}
// }}}

// findFrom:to:value: {{{
+ (OLForwardIterator*) findFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last value: (id)object
{
    return [ALG_ALLOC autorelease:
        [OLAlgorithm findImplFrom: first to: last value: object]];
}
// }}}

// forEachFrom:to:function: {{{
+ (void) forEachFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last function: (OLAllocFunctor<OLUnaryFunction>*)func
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* src = [first copyWithAllocator: alloc];
    OLAllocator* funcAlloc = [func allocator];
    id result;
    BOOL funcState = [func autoreleasesResults];

    [func setAutoreleasesResults: NO];
    for ( ; ![src isEqual: last]; [src advance])
    {
        result = [func performUnaryFunctionWithArg: [src dereference]];
        if (result != nil)
            [funcAlloc deallocateObject: result];
    }
    [alloc deallocateObject: src];
    [func setAutoreleasesResults: funcState];
}
// }}}

// includesFrom:to:subFrom:subTo: {{{
+ (BOOL) includesFrom: (OLForwardIterator*)first1 to: (OLForwardIterator*)last1 subFrom: (OLForwardIterator*)first2 subTo: (OLForwardIterator*)last2
{
    OLAllocator* alloc = ALG_ALLOC;
    OLLess* less = [[alloc allocateObject: [OLLess class]] init];
    BOOL result = [OLAlgorithm includesFrom: first1 to: last1
        subFrom: first2 subTo: last2 predicate: less];

    [alloc deallocateObject: less];
    return result;
}
// }}}

// includesFrom:to:subFrom:subTo:predicate: {{{
+ (BOOL) includesFrom: (OLForwardIterator*)first1 to: (OLForwardIterator*)last1 subFrom: (OLForwardIterator*)first2 subTo: (OLForwardIterator*)last2 predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* f1;
    OLForwardIterator* f2;
    BOOL returnFalse = NO;
    BOOL result;

    if ([first2 isEqual: last2])
        return YES;
    if ([first1 isEqual: last1])
        return NO;
    f1 = [first1 copyWithAllocator: alloc];
    f2 = [first2 copyWithAllocator: alloc];
    while (![f1 isEqual: last1] && ![f2 isEqual: last2])
    {
        if ([pred performBinaryFunctionWithArg: [f2 dereference]
                andArg: [f1 dereference]])
        {
            returnFalse = YES;
            break;
        }
        else if ([pred performBinaryFunctionWithArg: [f1 dereference]
                    andArg: [f2 dereference]])
        {
            [f1 advance];
        }
        else
        {
            [f1 advance];
            [f2 advance];
        }
    }
    result = returnFalse ? NO : [f2 isEqual: last2];
    [alloc deallocateObject: f1];
    [alloc deallocateObject: f2];
    return result;
}
// }}}

// initialize {{{
+ (void) initialize
{
    if (self == [OLAlgorithm class])
    {
        pthread_once(&__algoOnce, __initializeAlgoAllocatorKey);
        pthread_setspecific(__algoAllocatorKey, OBJ_RETAIN([OLAllocator defaultAllocator]));
    }
}
// }}}

// inPlaceMergeFrom:middle:to: {{{
+ (void) inPlaceMergeFrom: (OLBidirectionalIterator*)first middle: (OLBidirectionalIterator*)middle to: (OLBidirectionalIterator*)last
{
    OLAllocator* alloc = ALG_ALLOC;
    OLLess* less = [[alloc allocateObject: [OLLess class]] init];

    [OLAlgorithm inPlaceMergeFrom: first middle: middle to: last predicate: less];
    [alloc deallocateObject: less];
}
// }}}

// inPlaceMergeFrom:middle:to:predicate: {{{
+ (void) inPlaceMergeFrom: (OLBidirectionalIterator*)first middle: (OLBidirectionalIterator*)middle to: (OLBidirectionalIterator*)last predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLTempBuf* buf;
    unsigned len1;
    unsigned len2;

    if (!([first isEqual: middle] || [middle isEqual: last]))
    {
        buf = [[alloc allocateObject: [OLTempBuf class]]
            initWithFirst: first last: last];
        len1 = [OLIterator distanceFrom: first to: middle allocator: alloc];
        len2 = [OLIterator distanceFrom: middle to: last allocator: alloc];
        [OLAlgorithm mergeAdaptiveFrom: first middle: middle last: last
            len1: len1 len2: len2 buffer: buf predicate: pred];
        [alloc deallocateObject: buf];
    }
}
// }}}

// isHeapFrom:to: {{{
+ (BOOL) isHeapFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last
{
    OLAllocator* alloc = ALG_ALLOC;
    OLLess* less = [[alloc allocateObject: [OLLess class]] init];
    BOOL result = [OLAlgorithm isHeapFrom: first to: last predicate: less];

    [alloc deallocateObject: less];
    return result;
}
// }}}

// isHeapFrom:to:predicate: {{{
+ (BOOL) isHeapFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* parent = [first copyWithAllocator: alloc];
    OLForwardIterator* child = [first copyWithAllocator: alloc];
    unsigned counter;
    BOOL isHeap = YES;

    for ([child advance], counter = 1; ![child isEqual: last]; [child advance], counter++)
    {
        if ([pred performBinaryFunctionWithArg: [parent dereference]
                andArg: [child dereference]])
        {
            isHeap = NO;
            break;
        }
        if ((counter & 1) == 0)
            [parent advance];
    }
    [alloc deallocateObject: parent];
    [alloc deallocateObject: child];
    return isHeap;
}
// }}}

// isSortedFrom:to: {{{
+ (BOOL) isSortedFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last
{
    OLAllocator* alloc = ALG_ALLOC;
    OLLess* less = [[alloc allocateObject: [OLLess class]] init];
    BOOL result = [OLAlgorithm isSortedFrom: first to: last predicate: less];

    [alloc deallocateObject: less];
    return result;
}
// }}}

// isSortedFrom:to:predicate: {{{
+ (BOOL) isSortedFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* next;
    OLForwardIterator* cur;
    BOOL isSorted;

    if ([first isEqual: last])
        return YES;
    cur = [first copyWithAllocator: alloc];
    next = [first copyWithAllocator: alloc];
    isSorted = YES;
    for ([next advance]; ![next isEqual: last]; [cur advance], [next advance])
    {
        if ([pred performBinaryFunctionWithArg: [next dereference]
                andArg: [cur dereference]])
        {
            isSorted = NO;
            break;
        }
    }
    [alloc deallocateObject: cur];
    [alloc deallocateObject: next];
    return isSorted;
}
// }}}

// lexicographicalCompareFrom:to:andFrom:andTo: {{{
+ (BOOL) lexicographicalCompareFrom: (OLForwardIterator*)first1 to: (OLForwardIterator*)last1 andFrom: (OLForwardIterator*)first2 andTo: (OLForwardIterator*)last2
{
    OLAllocator* alloc = ALG_ALLOC;
    OLLess* less = [[alloc allocateObject: [OLLess class]] init];
    BOOL result = [OLAlgorithm lexicographicalCompareFrom: first1 to: last1
        andFrom: first2 andTo: last2 predicate: less];

    [alloc deallocateObject: less];
    return result;
}
// }}}

// lexicographicalCompareFrom:to:andFrom:andTo:predicate: {{{
+ (BOOL) lexicographicalCompareFrom: (OLForwardIterator*)first1 to: (OLForwardIterator*)last1 andFrom: (OLForwardIterator*)first2 andTo: (OLForwardIterator*)last2 predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* f1 = [first1 copyWithAllocator: alloc];
    OLForwardIterator* f2 = [first2 copyWithAllocator: alloc];
    BOOL result = NO;
    BOOL gotResult = NO;

    for ( ; ![f1 isEqual: last1] && ![f2 isEqual: last2]; [f1 advance], [f2 advance])
    {
        if ([pred performBinaryFunctionWithArg: [f1 dereference]
                andArg: [f2 dereference]])
        {
            result = YES;
            gotResult = YES;
            break;
        }
        else if ([pred performBinaryFunctionWithArg: [f2 dereference]
                    andArg: [f1 dereference]])
        {
            gotResult = YES;
            break;
        }
    }
    if (!gotResult)
        result = [f1 isEqual: last1] && ![f2 isEqual: last2];
    [alloc deallocateObject: f1];
    [alloc deallocateObject: f2];
    return result;
}
// }}}

// lowerBoundFrom:to:value: {{{
+ (OLForwardIterator*) lowerBoundFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last value: (id)object
{
    OLAllocator* alloc = ALG_ALLOC;
    OLLess* less = [[alloc allocateObject: [OLLess class]] init];
    OLForwardIterator* result =
        [OLAlgorithm lowerBoundFrom: first to: last value: object predicate: less];

    [alloc deallocateObject: less];
    return result;
}
// }}}

// lowerBoundFrom:to:value:predicate: {{{
+ (OLForwardIterator*) lowerBoundFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last value: (id)object predicate: (id<OLBoolBinaryFunction>) pred
{
    return [ALG_ALLOC autorelease: [OLAlgorithm boundImplFrom: first to: last
        value: object predicate: pred lower: YES]];
}
// }}}

// makeHeapFrom:to: {{{
+ (void) makeHeapFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last
{
    OLAllocator* alloc = ALG_ALLOC;
    OLLess* less = [[alloc allocateObject: [OLLess class]] init];

    [OLAlgorithm makeHeapFrom: first to: last predicate: less];
    [alloc deallocateObject: less];
}
// }}}

// makeHeapFrom:to:predicate: {{{
+ (void) makeHeapFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLRandomAccessIterator* f;
    unsigned len = [last difference: first];
    unsigned parent;

    if (len >= 2)
    {
        f = [first copyWithAllocator: alloc];
        parent = (len - 2) / 2;
        [f advanceBy: parent];
        while (YES)
        {
            [OLAlgorithm adjustHeapFirst: first hole: parent len: len
                value: [f dereference] predicate: pred];
            if (parent == 0)
                break;
            [f reverse];
            parent--;
        }
        [alloc deallocateObject: f];
    }
}
// }}}

// max:or: {{{
+ (id) max: (id)left or: (id)right
{
    return [left isLessThan: right] ? right : left;
}
// }}}

// max:or:predicate: {{{
+ (id) max: (id)left or: (id)right predicate: (id<OLBoolBinaryFunction>)pred
{
    return [pred performBinaryFunctionWithArg: left andArg: right] ?
        right : left;
}
// }}}

// maxElementFrom:to: {{{
+ (OLForwardIterator*) maxElementFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last
{
    OLAllocator* alloc = ALG_ALLOC;
    OLLess* less = [[alloc allocateObject: [OLLess class]] init];
    OLForwardIterator* result =
        [OLAlgorithm maxElementFrom: first to: last predicate: less];

    [alloc deallocateObject: less];
    return result;
}
// }}}

// maxElementFrom:to:predicate: {{{
+ (OLForwardIterator*) maxElementFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* f;
    OLForwardIterator* result;
    id current;
    unsigned index = 0;
    unsigned currentIndex = 0;

    if (![first isEqual: last])
    {
        f = [first copyWithAllocator: alloc];
        current = [f dereference];
        while (![[f advance] isEqual: last])
        {
            index++;
            if ([pred performBinaryFunctionWithArg: current
                    andArg: [f dereference]])
            {
                current = [f dereference];
                currentIndex = index;
            }
        }
        [alloc deallocateObject: f];
        result = [first copyWithAllocator: alloc];
        [OLIterator advanceIterator: result distance: currentIndex];
    }
    else
    {
        result = [last copyWithAllocator: alloc];
    }
    return ALG_COND_AUTORELEASE(result);
}
// }}}

// mergeFrom:to:andFrom:andTo:destination: {{{
+ (OLForwardIterator*) mergeFrom: (OLForwardIterator*)first1 to: (OLForwardIterator*)last1 andFrom: (OLForwardIterator*)first2 andTo: (OLForwardIterator*)last2 destination: (OLForwardIterator*)dest
{
    OLAllocator* alloc = ALG_ALLOC;
    OLLess* less = [[alloc allocateObject: [OLLess class]] init];
    OLForwardIterator* result =
        [OLAlgorithm mergeFrom: first1 to: last1
            andFrom: first2 andTo: last2 destination: dest predicate: less];

    [alloc deallocateObject: less];
    return result;
}
// }}}

// mergeFrom:to:andFrom:andTo:destination:predicate: {{{
+ (OLForwardIterator*) mergeFrom: (OLForwardIterator*)first1 to: (OLForwardIterator*)last1 andFrom: (OLForwardIterator*)first2 andTo: (OLForwardIterator*)last2 destination: (OLForwardIterator*)dest predicate: (id<OLBoolBinaryFunction>)pred
{
    return [ALG_ALLOC autorelease: [OLAlgorithm mergeImplSeries1From: first1 series1To: last1
        series2From: first2 series2To: last2 destination: dest predicate: pred needItor: YES]];
}
// }}}

// min:or: {{{
+ (id) min: (id)left or: (id)right
{
    return [right isLessThan: left] ? right : left;
}
// }}}

// min:or:predicate: {{{
+ (id) min: (id)left or: (id)right predicate: (id<OLBoolBinaryFunction>)pred
{
    return [pred performBinaryFunctionWithArg: right andArg: left] ?
        right : left;
}
// }}}

// minElementFrom:to: {{{
+ (OLForwardIterator*) minElementFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last
{
    OLAllocator* alloc = ALG_ALLOC;
    OLLess* less = [[alloc allocateObject: [OLLess class]] init];
    OLForwardIterator* result =
        [OLAlgorithm minElementFrom: first to: last predicate: less];

    [alloc deallocateObject: less];
    return result;
}
// }}}

// minElementFrom:to:predicate: {{{
+ (OLForwardIterator*) minElementFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* f;
    OLForwardIterator* result;
    id current;
    unsigned index = 0;
    unsigned currentIndex = 0;

    if (![first isEqual: last])
    {
        f = [first copyWithAllocator: alloc];
        current = [f dereference];
        while (![[f advance] isEqual: last])
        {
            index++;
            if ([pred performBinaryFunctionWithArg: [f dereference]
                    andArg: current])
            {
                current = [f dereference];
                currentIndex = index;
            }
        }
        [alloc deallocateObject: f];
        result = [first copyWithAllocator: alloc];
        [OLIterator advanceIterator: result distance: currentIndex];
    }
    else
    {
        result = [last copyWithAllocator: alloc];
    }
    return ALG_COND_AUTORELEASE(result);
}
// }}}

// mismatchFrom:to:with: {{{
+ (OLPair*) mismatchFrom: (OLForwardIterator*)first1 to: (OLForwardIterator*)last1 with: (OLForwardIterator*)first2
{
    OLAllocator* alloc = ALG_ALLOC;
    OLEqualTo* equal = [[alloc allocateObject: [OLEqualTo class]] init];
    OLPair* result = [OLAlgorithm mismatchFrom: first1 to: last1 with: first2 predicate: equal];

    [alloc deallocateObject: equal];
    return result;
}
// }}}

// mismatchFrom:to:with:predicate: {{{
+ (OLPair*) mismatchFrom: (OLForwardIterator*)first1 to: (OLForwardIterator*)last1 with: (OLForwardIterator*)first2 predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* f1 = [first1 copyWithAllocator: alloc];
    OLForwardIterator* f2 = [first2 copyWithAllocator: alloc];
    OLPair* result;

    while (![f1 isEqual: last1] &&
           [pred performBinaryFunctionWithArg: [f1 dereference]
                andArg: [f2 dereference]])
    {
        [f1 advance];
        [f2 advance];
    }
    result = [[alloc allocateObject: [OLPair class]] initWithFirst: f1 second: f2];
    [alloc deallocateObject: f1];
    [alloc deallocateObject: f2];
    return ALG_COND_AUTORELEASE(result);
}
// }}}

// nextPermutationFrom:to: {{{
+ (BOOL) nextPermutationFrom: (OLBidirectionalIterator*)first to: (OLBidirectionalIterator*)last
{
    OLAllocator* alloc = ALG_ALLOC;
    OLLess* less = [[alloc allocateObject: [OLLess class]] init];
    BOOL result = [OLAlgorithm nextPermutationFrom: first to: last predicate: less];

    [alloc deallocateObject: less];
    return result;
}
// }}}

// nextPermutationFrom:to:predicate: {{{
+ (BOOL) nextPermutationFrom: (OLBidirectionalIterator*)first to: (OLBidirectionalIterator*)last predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLBidirectionalIterator* i;
    OLBidirectionalIterator* ii;
    OLBidirectionalIterator* j = nil;
    BOOL result;

    if ([first isEqual: last] ||
        [OLIterator distanceFrom: first to: last allocator: alloc] == 1)
    {
        return NO;
    }
    i = [last copyWithAllocator: alloc];
    [i reverse];
    ii = [i copyWithAllocator: alloc];
    while (YES)
    {
        [i reverse];
        if ([pred performBinaryFunctionWithArg: [i dereference]
                andArg: [ii dereference]])
        {
            j = [last copyWithAllocator: alloc];
            do
            {
                [j reverse];
            } while (![pred performBinaryFunctionWithArg: [i dereference]
                            andArg: [j dereference]]);
            [OLAlgorithm swapIterators: i and: j];
            [OLAlgorithm reverseFrom: ii to: last];
            result = YES;
            break;
        }
        if ([i isEqual: first])
        {
            [OLAlgorithm reverseFrom: first to: last];
            result = NO;
            break;
        }
        [ii reverse];
    }
    [alloc deallocateObject: i];
    [alloc deallocateObject: ii];
    if (j != nil)
        [alloc deallocateObject: j];
    return result;
}
// }}}

// nthElementFrom:nth:to: {{{
+ (void) nthElementFrom: (OLRandomAccessIterator*)first nth: (OLRandomAccessIterator*)mid to: (OLRandomAccessIterator*)last
{
    OLAllocator* alloc = ALG_ALLOC;
    OLLess* less = [[alloc allocateObject: [OLLess class]] init];

    [OLAlgorithm nthElementFrom: first nth: mid to: last predicate: less];
    [alloc deallocateObject: less];
}
// }}}

// nthElementFrom:nth:to:predicate: {{{
+ (void) nthElementFrom: (OLRandomAccessIterator*)first nth: (OLRandomAccessIterator*)mid to: (OLRandomAccessIterator*)last predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLRandomAccessIterator* f = [first copyWithAllocator: alloc];
    OLRandomAccessIterator* l = [last copyWithAllocator: alloc];
    OLRandomAccessIterator* cut;
    id med;
    id fcur;
    int advance;

    while ([l difference: f] > 3)
    {
        advance = [l difference: f] / 2;
        fcur = [f dereference];
        med = [OLAlgorithm medianFromOne: fcur two: [[f advanceBy: advance] dereference]
            three: [[l reverse] dereference] predicate: pred];
        [f advanceBy: -advance];
        [l advance];
        cut = [OLAlgorithm unguardedPartitionFrom: f to: l pivot: med predicate: pred];
        if ([mid difference: cut] > 0)
        {
            [alloc deallocateObject: f];
            f = cut;
        }
        else
        {
            [alloc deallocateObject: l];
            l = cut;
        }
    }
    [OLAlgorithm insertionSortFrom: f to: l predicate: pred];
    [alloc deallocateObject: f];
    [alloc deallocateObject: l];
}
// }}}

// partialSortCopyFrom:to:destinationFrom:destinationTo: {{{
+ (OLRandomAccessIterator*) partialSortCopyFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last destinationFrom: (OLRandomAccessIterator*)rFirst destinationTo: (OLRandomAccessIterator*)rLast
{
    OLAllocator* alloc = ALG_ALLOC;
    OLLess* less = [[alloc allocateObject: [OLLess class]] init];
    OLRandomAccessIterator* result =
        [OLAlgorithm partialSortCopyFrom: first to: last
            destinationFrom: rFirst destinationTo: rLast predicate: less];

    [alloc deallocateObject: less];
    return result;
}
// }}}

// partialSortCopyFrom:to:destinationFrom:destinationTo:predicate: {{{
+ (OLRandomAccessIterator*) partialSortCopyFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last destinationFrom: (OLRandomAccessIterator*)rFirst destinationTo: (OLRandomAccessIterator*)rLast predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* f;
    OLRandomAccessIterator* rlReal;

    if ([rFirst isEqual: rLast])
        return ALG_COND_AUTORELEASE([rLast copyWithAllocator: alloc]);
    f = [first copyWithAllocator: alloc];
    rlReal = [rFirst copyWithAllocator: alloc];
    while (![f isEqual: last] && ![rlReal isEqual: rLast])
    {
        [rlReal assign: [f dereference]];
        [rlReal advance];
        [f advance];
    }
    [OLAlgorithm makeHeapFrom: rFirst to: rlReal predicate: pred];
    while (![f isEqual: last])
    {
        if ([pred performBinaryFunctionWithArg: [f dereference]
                andArg: [rFirst dereference]])
        {
            [OLAlgorithm adjustHeapFirst: rFirst hole: 0 len: [rlReal difference: rFirst]
                value: [f dereference] predicate: pred];
        }
        [f advance];
    }
    [alloc deallocateObject: f];
    [OLAlgorithm sortHeapFrom: rFirst to: rlReal predicate: pred];
    return ALG_COND_AUTORELEASE(rlReal);
}
// }}}

// partialSortFrom:middle:to: {{{
+ (void) partialSortFrom: (OLRandomAccessIterator*)first middle: (OLRandomAccessIterator*)mid to: (OLRandomAccessIterator*)last
{
    OLAllocator* alloc = ALG_ALLOC;
    OLLess* less = [[alloc allocateObject: [OLLess class]] init];

    [OLAlgorithm partialSortFrom: first middle: mid to: last predicate: less];
    [alloc deallocateObject: less];
}
// }}}

// partialSortFrom:middle:to:predicate: {{{
+ (void) partialSortFrom: (OLRandomAccessIterator*)first middle: (OLRandomAccessIterator*)mid to: (OLRandomAccessIterator*)last predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLRandomAccessIterator* i = [mid copyWithAllocator: alloc];

    [OLAlgorithm makeHeapFrom: first to: mid predicate: pred];
    for ( ; ![i isEqual: last]; [i advance])
    {
        if ([pred performBinaryFunctionWithArg: [i dereference]
                andArg: [first dereference]])
        {
            [OLAlgorithm popHeapImplFirst: first last: mid result: i
                value: [i dereference] predicate: pred];
        }
    }
    [OLAlgorithm sortHeapFrom: first to: mid predicate: pred];
    [alloc deallocateObject: i];
}
// }}}

// partitionFrom:to:predicate: {{{
+ (OLBidirectionalIterator*) partitionFrom: (OLBidirectionalIterator*)first to: (OLBidirectionalIterator*)last predicate: (id<OLBoolUnaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLBidirectionalIterator* f = [first copyWithAllocator: alloc];
    OLBidirectionalIterator* l = [last copyWithAllocator: alloc];

    while (YES)
    {
        while (YES)
        {
            if ([f isEqual: l])
                goto leavePartitionFrom;
            else if ([pred performUnaryFunctionWithArg: [f dereference]])
                [f advance];
            else
                break;
        }
        [l reverse];
        while (YES)
        {
            if ([f isEqual: l])
                goto leavePartitionFrom;
            else if (![pred performUnaryFunctionWithArg: [l dereference]])
                [l reverse];
            else
                break;
        }
        [OLAlgorithm swapIterators: f and: l];
        [f advance];
    }
leavePartitionFrom:
    [alloc deallocateObject: l];
    return ALG_COND_AUTORELEASE(f);
}
// }}}

// popHeapFrom:to: {{{
+ (void) popHeapFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last
{
    OLAllocator* alloc = ALG_ALLOC;
    OLLess* less = [[alloc allocateObject: [OLLess class]] init];

    [OLAlgorithm popHeapFrom: first to: last predicate: less];
    [alloc deallocateObject: less];
}
// }}}

// popHeapFrom:to:predicate: {{{
+ (void) popHeapFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLRandomAccessIterator* l = [last copyWithAllocator: alloc];

    [l reverse];
    [OLAlgorithm popHeapImplFirst: first last: l result: l value: [l dereference] predicate: pred];
    [alloc deallocateObject: l];
}
// }}}

// prevPermutationFrom:to: {{{
+ (BOOL) prevPermutationFrom: (OLBidirectionalIterator*)first to: (OLBidirectionalIterator*)last
{
    OLAllocator* alloc = ALG_ALLOC;
    OLLess* less = [[alloc allocateObject: [OLLess class]] init];
    BOOL result = [OLAlgorithm prevPermutationFrom: first to: last predicate: less];

    [alloc deallocateObject: less];
    return result;
}
// }}}

// prevPermutationFrom:to:predicate: {{{
+ (BOOL) prevPermutationFrom: (OLBidirectionalIterator*)first to: (OLBidirectionalIterator*)last predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLBidirectionalIterator* i;
    OLBidirectionalIterator* ii;
    OLBidirectionalIterator* j = nil;
    BOOL result;

    if ([first isEqual: last] ||
        [OLIterator distanceFrom: first to: last allocator: alloc] == 1)
    {
        return NO;
    }
    i = [last copyWithAllocator: alloc];
    [i reverse];
    ii = [i copyWithAllocator: alloc];
    while (YES)
    {
        [i reverse];
        if ([pred performBinaryFunctionWithArg: [ii dereference]
                andArg: [i dereference]])
        {
            j = [last copyWithAllocator: alloc];
            do
            {
                [j reverse];
            } while (![pred performBinaryFunctionWithArg: [j dereference]
                            andArg: [i dereference]]);
            [OLAlgorithm swapIterators: i and: j];
            [OLAlgorithm reverseFrom: ii to: last];
            result = YES;
            break;
        }
        if ([i isEqual: first])
        {
            [OLAlgorithm reverseFrom: first to: last];
            result = NO;
            break;
        }
        [ii reverse];
    }
    [alloc deallocateObject: i];
    [alloc deallocateObject: ii];
    if (j != nil)
        [alloc deallocateObject: j];
    return result;
}
// }}}

// pushHeapFrom:to: {{{
+ (void) pushHeapFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last
{
    OLAllocator* alloc = ALG_ALLOC;
    OLLess* less = [[alloc allocateObject: [OLLess class]] init];

    [OLAlgorithm pushHeapFrom: first to: last predicate: less];
    [alloc deallocateObject: less];
}
// }}}

// pushHeapFrom:to:predicate: {{{
+ (void) pushHeapFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLRandomAccessIterator* l = [last copyWithAllocator: alloc];

    [OLAlgorithm pushHeapImplFirst: first hole: [last difference: first] - 1 top: 0
        value: [[l reverse] dereference] predicate: pred];
    [alloc deallocateObject: l];
}
// }}}

// randomShuffleFrom:to: {{{
+ (void) randomShuffleFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last
{
    OLAllocator* alloc = ALG_ALLOC;
    OLRandomAccessIterator* i;
    OLRandomAccessIterator* ii;
    long int advance;

    if (![first isEqual: last])
    {
        i = [first copyWithAllocator: alloc];
        ii = [first copyWithAllocator: alloc];
        [i advance];
        for ( ; ![i isEqual: last]; [i advance])
        {
            advance = lrand48() % ([i difference: first] + 1);
            [ii advanceBy: advance];
            [OLAlgorithm swapIterators: i and: ii];
            [ii advanceBy: -advance];
        }
        [alloc deallocateObject: i];
        [alloc deallocateObject: ii];
    }
}
// }}}

// randomShuffleFrom:to:randGen: {{{
+ (void) randomShuffleFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last randGen: (OLAllocFunctor<OLUnaryFunction>*)gen
{
    OLAllocator* alloc = ALG_ALLOC;
    OLRandomAccessIterator* i;
    OLRandomAccessIterator* ii;
    NSNumber* val;
    OLAllocator* genAlloc;
    id result;
    unsigned advance;
    BOOL genState;

    if (![first isEqual: last])
    {
        genAlloc = [gen allocator];
        genState = [gen autoreleasesResults];
        [gen setAutoreleasesResults: NO];
        i = [first copyWithAllocator: alloc];
        ii = [first copyWithAllocator: alloc];
        [i advance];
        for ( ; ![i isEqual: last]; [i advance])
        {
            val = [[alloc allocateObject: [NSNumber class]] initWithUnsignedInt:
                [i difference: first] + 1];
            result = [gen performUnaryFunctionWithArg: val];
            advance = [result unsignedIntValue];
            [genAlloc deallocateObject: result];
            [alloc deallocateObject: val];
            [ii advanceBy: advance];
            [OLAlgorithm swapIterators: i and: ii];
            [ii advanceBy: -advance];
        }
        [alloc deallocateObject: i];
        [alloc deallocateObject: ii];
        [gen setAutoreleasesResults: genState];
    }
}
// }}}

// removeCopyFrom:to:destination:if: {{{
+ (OLForwardIterator*) removeCopyFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last destination: (OLForwardIterator*)dest if: (id<OLBoolUnaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* f = [first copyWithAllocator: alloc];
    OLForwardIterator* r = [dest copyWithAllocator: alloc];

    for ( ; ![f isEqual: last]; [f advance])
    {
        if (![pred performUnaryFunctionWithArg: [f dereference]])
        {
            [r assign: [f dereference]];
            [r advance];
        }
    }
    [alloc deallocateObject: f];
    return ALG_COND_AUTORELEASE(r);
}
// }}}

// removeCopyFrom:to:destination:value: {{{
+ (OLForwardIterator*) removeCopyFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last destination: (OLForwardIterator*)dest value: (id)object
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* f = [first copyWithAllocator: alloc];
    OLForwardIterator* r = [dest copyWithAllocator: alloc];

    for ( ; ![f isEqual: last]; [f advance])
    {
        if (![[f dereference] isEqual: object])
        {
            [r assign: [f dereference]];
            [r advance];
        }
    }
    [alloc deallocateObject: f];
    return ALG_COND_AUTORELEASE(r);
}
// }}}

// removeFrom:to:if: {{{
+ (OLForwardIterator*) removeFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last if: (id<OLBoolUnaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* f =
        (OLForwardIterator*)[OLAlgorithm findIfImplFrom: first
            to: last predicate: pred];
    OLForwardIterator* next;
    OLForwardIterator* result;

    if ([f isEqual: last])
    {
        result = ALG_COND_AUTORELEASE(f);
    }
    else
    {
        next = [f copyWithAllocator: alloc];
        result = [OLAlgorithm removeCopyFrom: [next advance] to: last destination: f if: pred];
        [alloc deallocateObject: next];
        [alloc deallocateObject: f];
    }
    return result;
}
// }}}

// removeFrom:to:value: {{{
+ (OLForwardIterator*) removeFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last value: (id)object
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* f =
        (OLForwardIterator*)[OLAlgorithm findImplFrom: first
            to: last value: object];
    OLForwardIterator* next;
    OLForwardIterator* result;

    if ([f isEqual: last])
    {
        result = ALG_COND_AUTORELEASE(f);
    }
    else
    {
        next = [f copyWithAllocator: alloc];
        result = [OLAlgorithm removeCopyFrom: [next advance] to: last
            destination: f value: object];
        [alloc deallocateObject: next];
        [alloc deallocateObject: f];
    }
    return result;
}
// }}}

// replaceCopyFrom:to:destination:if:newValue: {{{
+ (OLForwardIterator*) replaceCopyFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last destination: (OLForwardIterator*)dest if: (id<OLBoolUnaryFunction>)pred newValue: (id)newv
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* f = [first copyWithAllocator: alloc];
    OLForwardIterator* r = [dest copyWithAllocator: alloc];

    for ( ; ![f isEqual: last]; [f advance], [r advance])
    {
        [r assign: [pred performUnaryFunctionWithArg: [f dereference]] ?
            newv : [f dereference]];
    }
    [alloc deallocateObject: f];
    return ALG_COND_AUTORELEASE(r);
}
// }}}

// replaceCopyFrom:to:destination:oldValue:newValue: {{{
+ (OLForwardIterator*) replaceCopyFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last destination: (OLForwardIterator*)dest oldValue: (id)oldv newValue: (id)newv
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* f = [first copyWithAllocator: alloc];
    OLForwardIterator* r = [dest copyWithAllocator: alloc];

    for ( ; ![f isEqual: last]; [f advance], [r advance])
        [r assign: ([[f dereference] isEqual: oldv]) ? newv : [f dereference]];
    [alloc deallocateObject: f];
    return ALG_COND_AUTORELEASE(r);
}
// }}}

// replaceFrom:to:if:newValue: {{{
+ (void) replaceFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last if: (id<OLBoolUnaryFunction>)pred newValue: (id)newv
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* f = [first copyWithAllocator: alloc];

    for ( ; ![f isEqual: last]; [f advance])
    {
        if ([pred performUnaryFunctionWithArg: [f dereference]])
            [f assign: newv];
    }
    [alloc deallocateObject: f];
}
// }}}

// replaceFrom:to:oldValue:newValue: {{{
+ (void) replaceFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last oldValue: (id)oldv newValue: (id)newv
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* f = [first copyWithAllocator: alloc];

    for ( ; ![f isEqual: last]; [f advance])
    {
        if ([[f dereference] isEqual: oldv])
            [f assign: newv];
    }
    [alloc deallocateObject: f];
}
// }}}

// reverseCopyFrom:to:destination: {{{
+ (OLForwardIterator*) reverseCopyFrom: (OLBidirectionalIterator*)first to: (OLBidirectionalIterator*)last destination: (OLForwardIterator*)dest
{
    OLAllocator* alloc = ALG_ALLOC;
    OLBidirectionalIterator* l = [last copyWithAllocator: alloc];
    OLForwardIterator* r = [dest copyWithAllocator: alloc];

    while (![first isEqual: l])
    {
        [l reverse];
        [r assign: [l dereference]];
        [r advance];
    }
    [alloc deallocateObject: l];
    return ALG_COND_AUTORELEASE(r);
}
// }}}

// reverseFrom:to: {{{
+ (void) reverseFrom: (OLBidirectionalIterator*)first to: (OLBidirectionalIterator*)last
{
    OLAllocator* alloc = ALG_ALLOC;
    OLBidirectionalIterator* f = [first copyWithAllocator: alloc];
    OLBidirectionalIterator* l = [last copyWithAllocator: alloc];

    for ( ; ![f isEqual: l] && ![f isEqual: [l reverse]]; [f advance])
        [OLAlgorithm swapIterators: f and: l];
    [alloc deallocateObject: f];
    [alloc deallocateObject: l];
}
// }}}

// rotateCopyFrom:middle:to:destination: {{{
+ (OLForwardIterator*) rotateCopyFrom: (OLForwardIterator*)first middle: (OLForwardIterator*)mid to: (OLForwardIterator*)last destination: (OLForwardIterator*)dest
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* tmp =
        [OLAlgorithm copyImplFrom: mid to: last destination: dest needItor: YES];
    OLForwardIterator* result = 
        [OLAlgorithm copyImplFrom: first to: mid destination: tmp needItor: YES];

    [alloc deallocateObject: tmp];
    return ALG_COND_AUTORELEASE(result);
}
// }}}

// rotateFrom:middle:to: {{{
+ (OLForwardIterator*) rotateFrom: (OLForwardIterator*)first middle: (OLForwardIterator*)mid to: (OLForwardIterator*)last
{
    return [ALG_ALLOC autorelease:
        [OLAlgorithm rotateImplFrom: first middle: mid to: last]];
}
// }}}

// searchFrom:to:count:value: {{{
+ (OLForwardIterator*) searchFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last count: (unsigned)num value: (id)object
{
    OLAllocator* alloc = ALG_ALLOC;
    OLEqualTo* equal = [[alloc allocateObject: [OLEqualTo class]] init];
    OLForwardIterator* result = [OLAlgorithm searchFrom: first to: last count: num
        value: object predicate: equal];

    [alloc deallocateObject: equal];
    return result;
}
// }}}

// searchFrom:to:count:value:predicate: {{{
+ (OLForwardIterator*) searchFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last count: (unsigned)num value: (id)object predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* f;
    OLForwardIterator* i;
    OLForwardIterator* result = nil;
    unsigned n;

    if (num == 0)
    {
        result = [last copyWithAllocator: alloc];
    }
    else
    {
        f = [first copyWithAllocator: alloc];
        while (![f isEqual: last])
        {
            if ([pred performBinaryFunctionWithArg: [f dereference]
                    andArg: object])
            {
                break;
            }
            [f advance];
        }
        i = [f copyWithAllocator: alloc];
        while (![f isEqual: last])
        {
            n = num - 1;
            [i advance];
            while (![i isEqual: last] &&
                   n != 0 &&
                   [pred performBinaryFunctionWithArg: [i dereference]
                        andArg: object])
            {
                [i advance];
                n--;
            }
            if (n == 0)
            {
                result = f;
                break;
            }
            else
            {
                while (![i isEqual: last])
                {
                    if ([pred performBinaryFunctionWithArg: [i dereference]
                            andArg: object])
                    {
                        break;
                    }
                    [i advance];
                }
                [alloc deallocateObject: f];
                f = [i copyWithAllocator: alloc];
            }
            [alloc deallocateObject: i];
            i = [f copyWithAllocator: alloc];
        }
        if (result == nil)
            result = [last copyWithAllocator: alloc];
        [alloc deallocateObject: i];
        if (result != f)
            [alloc deallocateObject: f];
    }
    return ALG_COND_AUTORELEASE(result);
}
// }}}

// searchFrom:to:subFrom:subTo: {{{
+ (OLForwardIterator*) searchFrom: (OLForwardIterator*)first1 to: (OLForwardIterator*)last1 subFrom: (OLForwardIterator*)first2 subTo: (OLForwardIterator*)last2
{
    OLAllocator* alloc = ALG_ALLOC;
    OLEqualTo* equal = [[alloc allocateObject: [OLEqualTo class]] init];
    OLForwardIterator* result =
        [OLAlgorithm searchFrom: first1 to: last1
        subFrom: first2 subTo: last2 predicate: equal];

    [alloc deallocateObject: equal];
    return result;
}
// }}}

// searchFrom:to:subFrom:subTo:predicate: {{{
+ (OLForwardIterator*) searchFrom: (OLForwardIterator*)first1 to: (OLForwardIterator*)last1 subFrom: (OLForwardIterator*)first2 subTo: (OLForwardIterator*)last2 predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* f1;
    OLForwardIterator* tmp;
    OLForwardIterator* tmp1;
    OLForwardIterator* cur;
    BOOL result;

    if ([first1 isEqual: last1] || [first2 isEqual: last2])
        return ALG_COND_AUTORELEASE([first1 copyWithAllocator: alloc]);
    tmp = [first2 copyWithAllocator: alloc];
    result = [[tmp advance] isEqual: last2];
    [alloc deallocateObject: tmp];
    f1 = [first1 copyWithAllocator: alloc];
    if (result)
    {
        while (![f1 isEqual: last1] &&
               ![pred performBinaryFunctionWithArg: [f1 dereference]
                    andArg: [first2 dereference]])
        {
            [f1 advance];
        }
        return ALG_COND_AUTORELEASE(f1);
    }
    tmp1 = [first2 copyWithAllocator: alloc];
    [tmp1 advance];
    while (![f1 isEqual: last1])
    {
        while (![f1 isEqual: last1])
        {
            if ([pred performBinaryFunctionWithArg: [f1 dereference]
                    andArg: [first2 dereference]])
            {
                break;
            }
            [f1 advance];
        }
        while (![f1 isEqual: last1] &&
               ![pred performBinaryFunctionWithArg: [f1 dereference]
                    andArg: [first2 dereference]])
        {
            [f1 advance];
        }
        if ([f1 isEqual: last1])
        {
            [alloc deallocateObject: tmp1];
            return ALG_COND_AUTORELEASE(f1);
        }
        cur = [f1 copyWithAllocator: alloc];
        if ([[cur advance] isEqual: last1])
        {
            [alloc deallocateObject: tmp1];
            [alloc deallocateObject: f1];
            return ALG_COND_AUTORELEASE(cur);
        }
        tmp = [tmp1 copyWithAllocator: alloc];
        while ([pred performBinaryFunctionWithArg: [cur dereference]
                    andArg: [tmp dereference]])
        {
            if ([[tmp advance] isEqual: last2])
            {
                [alloc deallocateObject: tmp];
                [alloc deallocateObject: tmp1];
                [alloc deallocateObject: cur];
                return ALG_COND_AUTORELEASE(f1);
            }
            if ([[cur advance] isEqual: last1])
            {
                [alloc deallocateObject: tmp];
                [alloc deallocateObject: tmp1];
                [alloc deallocateObject: f1];
                return ALG_COND_AUTORELEASE(cur);
            }
        }
        [f1 advance];
        [alloc deallocateObject: cur];
        [alloc deallocateObject: tmp];
    }
    [alloc deallocateObject: tmp1];
    return ALG_COND_AUTORELEASE(f1);
}
// }}}

// setAllocator: {{{
+ (void) setAllocator: (OLAllocator*)alloc
{
    OLAllocator* oldAlloc;

    oldAlloc = pthread_getspecific(__algoAllocatorKey);
    if (oldAlloc != NULL)
        OBJ_RELEASE(oldAlloc);
    pthread_setspecific(__algoAllocatorKey, OBJ_RETAIN(alloc));
}
// }}}

// setDifferenceFrom:to:andFrom:andTo:destination: {{{
+ (OLForwardIterator*) setDifferenceFrom: (OLForwardIterator*)first1 to: (OLForwardIterator*)last1 andFrom: (OLForwardIterator*)first2 andTo: (OLForwardIterator*)last2 destination: (OLForwardIterator*)dest
{
    OLAllocator* alloc = ALG_ALLOC;
    OLLess* less = [[alloc allocateObject: [OLLess class]] init];
    OLForwardIterator* result =
        [OLAlgorithm setDifferenceFrom: first1 to: last1
            andFrom: first2 andTo: last2 destination: dest predicate: less];

    [alloc deallocateObject: less];
    return result;
}
// }}}

// setDifferenceFrom:to:andFrom:andTo:destination:predicate: {{{
+ (OLForwardIterator*) setDifferenceFrom: (OLForwardIterator*)first1 to: (OLForwardIterator*)last1 andFrom: (OLForwardIterator*)first2 andTo: (OLForwardIterator*)last2 destination: (OLForwardIterator*)dest predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* f1 = [first1 copyWithAllocator: alloc];
    OLForwardIterator* f2 = [first2 copyWithAllocator: alloc];
    OLForwardIterator* d = [dest copyWithAllocator: alloc];
    OLForwardIterator* result;

    while (![f1 isEqual: last1] && ![f2 isEqual: last2])
    {
        if ([pred performBinaryFunctionWithArg: [f1 dereference]
                andArg: [f2 dereference]])
        {
            [d assign: [f1 dereference]];
            [f1 advance];
            [d advance];
        }
        else if ([pred performBinaryFunctionWithArg: [f2 dereference]
                    andArg: [f1 dereference]])
        {
            [f2 advance];
        }
        else
        {
            [f1 advance];
            [f2 advance];
        }
    }
    result = [OLAlgorithm copyImplFrom: f1 to: last1 destination: d needItor: YES];
    [alloc deallocateObject: f1];
    [alloc deallocateObject: f2];
    [alloc deallocateObject: d];
    return ALG_COND_AUTORELEASE(result);
}
// }}}

// setIntersectionFrom:to:andFrom:andTo:destination: {{{
+ (OLForwardIterator*) setIntersectionFrom: (OLForwardIterator*)first1 to: (OLForwardIterator*)last1 andFrom: (OLForwardIterator*)first2 andTo: (OLForwardIterator*)last2 destination: (OLForwardIterator*)dest
{
    OLAllocator* alloc = ALG_ALLOC;
    OLLess* less = [[alloc allocateObject: [OLLess class]] init];
    OLForwardIterator* result =
        [OLAlgorithm setIntersectionFrom: first1 to: last1
            andFrom: first2 andTo: last2 destination: dest predicate: less];

    [alloc deallocateObject: less];
    return result;
}
// }}}

// setIntersectionFrom:to:andFrom:andTo:destination:predicate: {{{
+ (OLForwardIterator*) setIntersectionFrom: (OLForwardIterator*)first1 to: (OLForwardIterator*)last1 andFrom: (OLForwardIterator*)first2 andTo: (OLForwardIterator*)last2 destination: (OLForwardIterator*)dest predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* f1 = [first1 copyWithAllocator: alloc];
    OLForwardIterator* f2 = [first2 copyWithAllocator: alloc];
    OLForwardIterator* d = [dest copyWithAllocator: alloc];

    while (![f1 isEqual: last1] && ![f2 isEqual: last2])
    {
        if ([pred performBinaryFunctionWithArg: [f1 dereference]
                andArg: [f2 dereference]])
        {
            [f1 advance];
        }
        else if ([pred performBinaryFunctionWithArg: [f2 dereference]
                    andArg: [f1 dereference]])
        {
            [f2 advance];
        }
        else
        {
            [d assign: [f1 dereference]];
            [f1 advance];
            [f2 advance];
            [d advance];
        }
    }
    [alloc deallocateObject: f1];
    [alloc deallocateObject: f2];
    return ALG_COND_AUTORELEASE(d);
}
// }}}

// setSymmetricDifferenceFrom:to:andFrom:andTo:destination: {{{
+ (OLForwardIterator*) setSymmetricDifferenceFrom: (OLForwardIterator*)first1 to: (OLForwardIterator*)last1 andFrom: (OLForwardIterator*)first2 andTo: (OLForwardIterator*)last2 destination: (OLForwardIterator*)dest
{
    OLAllocator* alloc = ALG_ALLOC;
    OLLess* less = [[alloc allocateObject: [OLLess class]] init];
    OLForwardIterator* result =
        [OLAlgorithm setSymmetricDifferenceFrom: first1 to: last1
            andFrom: first2 andTo: last2 destination: dest predicate: less];

    [alloc deallocateObject: less];
    return result;
}
// }}}

// setSymmetricDifferenceFrom:to:andFrom:andTo:destination:predicate: {{{
+ (OLForwardIterator*) setSymmetricDifferenceFrom: (OLForwardIterator*)first1 to: (OLForwardIterator*)last1 andFrom: (OLForwardIterator*)first2 andTo: (OLForwardIterator*)last2 destination: (OLForwardIterator*)dest predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* f1 = [first1 copyWithAllocator: alloc];
    OLForwardIterator* f2 = [first2 copyWithAllocator: alloc];
    OLForwardIterator* d = [dest copyWithAllocator: alloc];
    OLForwardIterator* tmp;
    OLForwardIterator* result;

    while (![f1 isEqual: last1] && ![f2 isEqual: last2])
    {
        if ([pred performBinaryFunctionWithArg: [f1 dereference]
                andArg: [f2 dereference]])
        {
            [d assign: [f1 dereference]];
            [f1 advance];
            [d advance];
        }
        else if ([pred performBinaryFunctionWithArg: [f2 dereference]
                    andArg: [f1 dereference]])
        {
            [d assign: [f2 dereference]];
            [f2 advance];
            [d advance];
        }
        else
        {
            [f1 advance];
            [f2 advance];
        }
    }
    tmp = [OLAlgorithm copyImplFrom: f1 to: last1 destination: d needItor: YES];
    result = [OLAlgorithm copyImplFrom: f2 to: last2 destination: tmp needItor: YES];
    [alloc deallocateObject: f1];
    [alloc deallocateObject: f2];
    [alloc deallocateObject: d];
    [alloc deallocateObject: tmp];
    return ALG_COND_AUTORELEASE(result);
}
// }}}

// setUnionFrom:to:andFrom:andTo:destination: {{{
+ (OLForwardIterator*) setUnionFrom: (OLForwardIterator*)first1 to: (OLForwardIterator*)last1 andFrom: (OLForwardIterator*)first2 andTo: (OLForwardIterator*)last2 destination: (OLForwardIterator*)dest
{
    OLAllocator* alloc = ALG_ALLOC;
    OLLess* less = [[alloc allocateObject: [OLLess class]] init];
    OLForwardIterator* result =
        [OLAlgorithm setUnionFrom: first1 to: last1
            andFrom: first2 andTo: last2 destination: dest predicate: less];

    [alloc deallocateObject: less];
    return result;
}
// }}}

// setUnionFrom:to:andFrom:andTo:destination:predicate: {{{
+ (OLForwardIterator*) setUnionFrom: (OLForwardIterator*)first1 to: (OLForwardIterator*)last1 andFrom: (OLForwardIterator*)first2 andTo: (OLForwardIterator*)last2 destination: (OLForwardIterator*)dest predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* f1 = [first1 copyWithAllocator: alloc];
    OLForwardIterator* f2 = [first2 copyWithAllocator: alloc];
    OLForwardIterator* d = [dest copyWithAllocator: alloc];
    OLForwardIterator* tmp;
    OLForwardIterator* result;

    while (![f1 isEqual: last1] && ![f2 isEqual: last2])
    {
        if ([pred performBinaryFunctionWithArg: [f1 dereference]
                andArg: [f2 dereference]])
        {
            [d assign: [f1 dereference]];
            [f1 advance];
        }
        else if ([pred performBinaryFunctionWithArg: [f2 dereference]
                    andArg: [f1 dereference]])
        {
            [d assign: [f2 dereference]];
            [f2 advance];
        }
        else
        {
            [d assign: [f1 dereference]];
            [f1 advance];
            [f2 advance];
        }
        [d advance];
    }
    tmp = [OLAlgorithm copyImplFrom: f1 to: last1 destination: d needItor: YES];
    result = [OLAlgorithm copyImplFrom: f2 to: last2 destination: tmp needItor: YES];
    [alloc deallocateObject: f1];
    [alloc deallocateObject: f2];
    [alloc deallocateObject: d];
    [alloc deallocateObject: tmp];
    return ALG_COND_AUTORELEASE(result);
}
// }}}

// sortFrom:to: {{{
+ (void) sortFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last
{
    OLAllocator* alloc = ALG_ALLOC;
    OLLess* less = [[alloc allocateObject: [OLLess class]] init];

    [OLAlgorithm sortFrom: first to: last predicate: less];
    [alloc deallocateObject: less];
}
// }}}

// sortFrom:to:predicate: {{{
+ (void) sortFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last predicate: (id<OLBoolBinaryFunction>)pred
{
    unsigned depthLimit;
    unsigned len;

    if (![first isEqual: last])
    {
        len = [last difference: first];
        for (depthLimit = 0; len != 1; len >>= 1)
            depthLimit++;
        [OLAlgorithm introSortLoopFrom: first to: last
            depthLimit: depthLimit * 2 predicate: pred];
        [OLAlgorithm finalInsertionSortFrom: first to: last predicate: pred];
    }
}
// }}}

// sortHeapFrom:to: {{{
+ (void) sortHeapFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last
{
    OLAllocator* alloc = ALG_ALLOC;
    OLLess* less = [[alloc allocateObject: [OLLess class]] init];

    [OLAlgorithm sortHeapFrom: first to: last predicate: less];
    [alloc deallocateObject: less];
}
// }}}

// sortHeapFrom:to:predicate: {{{
+ (void) sortHeapFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLRandomAccessIterator* l = [last copyWithAllocator: alloc];

    while ([l difference: first] > 1)
    {
        [OLAlgorithm popHeapFrom: first to: l predicate: pred];
        [l reverse];
    }
    [alloc deallocateObject: l];
}
// }}}

// stablePartitionFrom:to:predicate: {{{
+ (OLForwardIterator*) stablePartitionFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last predicate: (id<OLBoolUnaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLTempBuf* buf = [[alloc allocateObject: [OLTempBuf class]]
        initWithFirst: first last: last];
    OLForwardIterator* result = [OLAlgorithm stablePartitionAdaptiveFrom: first to: last
        predicate: pred length: [buf size] buffer: buf];

    [alloc deallocateObject: buf];
    return ALG_COND_AUTORELEASE(result);
}
// }}}

// stableSortFrom:to: {{{
+ (void) stableSortFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last
{
    OLAllocator* alloc = ALG_ALLOC;
    OLLess* less = [[alloc allocateObject: [OLLess class]] init];

    [OLAlgorithm stableSortFrom: first to: last predicate: less];
    [alloc deallocateObject: less];
}
// }}}

// stableSortFrom:to:predicate: {{{
+ (void) stableSortFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLTempBuf* buf = [[alloc allocateObject: [OLTempBuf class]]
        initWithFirst: first last: last];

    [OLAlgorithm stableSortAdaptiveFrom: first to: last buffer: buf predicate: pred];
    [alloc deallocateObject: buf];
}
// }}}

// swap:and {{{
+ (void) swap: (id*)left and: (id*)right
{
    OL_PREPARE_SLOW_SWAP;

    if (left != right)
        OL_SLOW_SWAP(*left, *right);
}
// }}}

// swapIterators:and: {{{
+ (void) swapIterators: (OLIterator*)left and: (OLIterator*)right
{
    id tmp = OBJ_RETAIN([left dereference]);

    [left assign: [right dereference]];
    [right assign: tmp];
    OBJ_RELEASE(tmp);
}
// }}}

// swapRangesFrom:to:with: {{{
+ (OLForwardIterator*) swapRangesFrom: (OLForwardIterator*)first1 to: (OLForwardIterator*)last1 with: (OLForwardIterator*)first2
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* f1 = [first1 copyWithAllocator: alloc];
    OLForwardIterator* f2 = [first2 copyWithAllocator: alloc];

    for ( ; ![f1 isEqual: last1]; [f1 advance], [f2 advance])
        [OLAlgorithm swapIterators: f1 and: f2];
    [alloc deallocateObject: f1];
    return ALG_COND_AUTORELEASE(f2);
}
// }}}

// transformFrom:to:destination:function: {{{
+ (OLForwardIterator*) transformFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last destination: (OLForwardIterator*)dest function: (OLAllocFunctor<OLUnaryFunction>*)func
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* f = [first copyWithAllocator: alloc];
    OLForwardIterator* d = [dest copyWithAllocator: alloc];
    OLAllocator* funcAlloc = [func allocator];
    id result;
    BOOL funcState = [func autoreleasesResults];

    [func setAutoreleasesResults: NO];
    for ( ; ![f isEqual: last]; [f advance], [d advance])
    {
        result = [func performUnaryFunctionWithArg: [f dereference]];
        [d assign: result];
        [funcAlloc deallocateObject: result];
    }
    [alloc deallocateObject: f];
    [func setAutoreleasesResults: funcState];
    return ALG_COND_AUTORELEASE(d);
}
// }}}

// transformFrom:to:withArgsFrom:destination:function: {{{
+ (OLForwardIterator*) transformFrom: (OLForwardIterator*)first1 to: (OLForwardIterator*)last withArgsFrom: (OLForwardIterator*)first2 destination: (OLForwardIterator*)dest function: (OLAllocFunctor<OLBinaryFunction>*)func
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* f = [first1 copyWithAllocator: alloc];
    OLForwardIterator* f2 = [first2 copyWithAllocator: alloc];
    OLForwardIterator* d = [dest copyWithAllocator: alloc];
    OLAllocator* funcAlloc = [func allocator];
    id result;
    BOOL funcState = [func autoreleasesResults];

    [func setAutoreleasesResults: NO];
    for ( ; ![f isEqual: last]; [f advance], [f2 advance], [d advance])
    {
        result = [func performBinaryFunctionWithArg: [f dereference]
            andArg: [f2 dereference]];
        [d assign: result];
        [funcAlloc deallocateObject: result];
    }
    [alloc deallocateObject: f];
    [alloc deallocateObject: f2];
    [func setAutoreleasesResults: funcState];
    return ALG_COND_AUTORELEASE(d);
}
// }}}

// uniqueCopyFrom:to:destination: {{{
+ (OLForwardIterator*) uniqueCopyFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last destination: (OLForwardIterator*)dest
{
    OLAllocator* alloc = ALG_ALLOC;
    OLEqualTo* equal = [[alloc allocateObject: [OLEqualTo class]] init];
    OLForwardIterator* result = [OLAlgorithm uniqueCopyFrom: first to: last
        destination: dest predicate: equal];

    [alloc deallocateObject: equal];
    return result;
}
// }}}

// uniqueCopyFrom:to:destination:predicate: {{{
+ (OLForwardIterator*) uniqueCopyFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last destination: (OLForwardIterator*)dest predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* f = [first copyWithAllocator: alloc];
    OLForwardIterator* d = [dest copyWithAllocator: alloc];
    id val = [f dereference];

    [d assign: val];
    while (![[f advance] isEqual: last])
    {
        if (![pred performBinaryFunctionWithArg: val andArg: [f dereference]])
        {
            val = [f dereference];
            [[d advance] assign: val];
        }
    }
    [alloc deallocateObject: f];
    return ALG_COND_AUTORELEASE([d advance]);
}
// }}}

// uniqueFrom:to: {{{
+ (OLForwardIterator*) uniqueFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last
{
    return [OLAlgorithm uniqueCopyFrom: first to: last destination: first];
}
// }}}

// uniqueFrom:to:predicate: {{{
+ (OLForwardIterator*) uniqueFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last predicate: (id<OLBoolBinaryFunction>)pred
{
    return [OLAlgorithm uniqueCopyFrom: first to: last destination: first predicate: pred];
}
// }}}

// upperBoundFrom:to:value: {{{
+ (OLForwardIterator*) upperBoundFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last value: (id)object
{
    OLAllocator* alloc = ALG_ALLOC;
    OLLess* less = [[alloc allocateObject: [OLLess class]] init];
    OLForwardIterator* result =
        [OLAlgorithm upperBoundFrom: first to: last value: object predicate: less];

    [alloc deallocateObject: less];
    return result;
}
// }}}

// upperBoundFrom:to:value:predicate: {{{
+ (OLForwardIterator*) upperBoundFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last value: (id)object predicate: (id<OLBoolBinaryFunction>) pred
{
    return [ALG_ALLOC autorelease: [OLAlgorithm boundImplFrom: first to: last
        value: object predicate: pred lower: NO]];
}
// }}}

@end
// }}}

// implementation OLTempBuf {{{
@implementation OLTempBuf

// initWithFirst:last: {{{
- (id) initWithFirst: (OLForwardIterator*)first last: (OLForwardIterator*)last
{
    OLAllocator* alloc = ALG_ALLOC;
    int i;

    [super init];
    size = [OLIterator distanceFrom: first to: last allocator: alloc];
    buffer = [alloc allocateBytes: size * sizeof(id)];
    for (i = 0; i < size; i++)
        buffer[i] = OBJ_RETAIN(__bufferJunk);
    begin = [[alloc allocateObject: [OLArrayIterator class]]
        initWithPointer: buffer];
    end = [[alloc allocateObject: [OLArrayIterator class]]
        initWithPointer: buffer + size];
    return self;
}
// }}}

// dealloc {{{
- (void) dealloc
{
    OLAllocator* alloc = ALG_ALLOC;
    int i;

    for (i = 0; i < size; i++)
        OBJ_RELEASE(buffer[i]);
    [alloc deallocateObject: begin];
    [alloc deallocateObject: end];
    [alloc deallocateBytes: buffer];
    [super dealloc];
}
// }}}

// begin {{{
- (OLArrayIterator*) begin
{
    return begin;
}
// }}}

// end {{{
- (OLArrayIterator*) end
{
    return end;
}
// }}}

// size {{{
- (unsigned) size
{
    return size;
}
// }}}

@end
// }}}

// implementation OLAlgorithm (PrivateMethods) {{{
@implementation OLAlgorithm (PrivateMethods)

// adjustHeapFirst:hole:len:value:predicate: {{{
+ (void) adjustHeapFirst: (OLRandomAccessIterator*)first hole: (unsigned)hole len: (unsigned)len value: (id)object predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLRandomAccessIterator* f = [first copyWithAllocator: alloc];
    OLRandomAccessIterator* f2 = [first copyWithAllocator: alloc];
    unsigned top = hole;
    unsigned secondChild = 2 * hole + 2;
    BOOL result;

    OBJ_RETAIN(object);
    while (secondChild < len)
    {
        [f advanceBy: secondChild];
        [f2 advanceBy: secondChild - 1];
        result = [pred performBinaryFunctionWithArg: [f dereference]
                    andArg: [f2 dereference]];
        [f advanceBy: -secondChild];
        [f2 advanceBy: -(secondChild - 1)];
        if (result)
            secondChild--;
        [f advanceBy: hole];
        [f2 advanceBy: secondChild];
        [f assign: [f2 dereference]];
        [f advanceBy: -hole];
        [f2 advanceBy: -secondChild];
        hole = secondChild;
        secondChild = 2 * (secondChild + 1);
    }
    if (secondChild == len)
    {
        [[f advanceBy: hole] assign: [[f2 advanceBy: secondChild - 1] dereference]];
        hole = secondChild - 1;
    }
    [alloc deallocateObject: f];
    [alloc deallocateObject: f2];
    [OLAlgorithm pushHeapImplFirst: first hole: hole top: top value: object predicate: pred];
    OBJ_RELEASE(object);
}
// }}}

// boundImplFrom:to:value:predicate:lower: {{{
+ (OLForwardIterator*) boundImplFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last value: (id)object predicate: (id<OLBoolBinaryFunction>)pred lower: (BOOL)lwr
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* cur = [first copyWithAllocator: alloc];
    OLForwardIterator* middle = nil;
    unsigned len = [OLIterator distanceFrom: first to: last allocator: alloc];
    unsigned half;

    while (len > 0)
    {
        half = len >> 1;
        if (middle != nil)
            [alloc deallocateObject: middle];
        middle = [cur copyWithAllocator: alloc];
        [OLIterator advanceIterator: middle distance: half];
        if (lwr)
        {
            if ([pred performBinaryFunctionWithArg: [middle dereference] andArg: object])
            {
                [alloc deallocateObject: cur];
                cur = [middle copyWithAllocator: alloc];
                [cur advance];
                len = len - half - 1;
            }
            else
            {
                len = half;
            }
        }
        else
        {
            if ([pred performBinaryFunctionWithArg: object andArg: [middle dereference]])
            {
                len = half;
            }
            else
            {
                [alloc deallocateObject: cur];
                cur = [middle copyWithAllocator: alloc];
                [cur advance];
                len = len - half - 1;
            }
        }
    }
    if (middle != nil)
        [alloc deallocateObject: middle];
    return cur;
}
// }}}

// chunkInsertionSortFrom:to:chunkSize:predicate: {{{
+ (void) chunkInsertionSortFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last chunkSize: (unsigned)size predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLRandomAccessIterator* f = [first copyWithAllocator: alloc];
    OLRandomAccessIterator* l = [first copyWithAllocator: alloc];

    [l advanceBy: size];
    while ([last difference: f] >= size)
    {
        [OLAlgorithm insertionSortFrom: f to: l predicate: pred];
        [f advanceBy: size];
        [l advanceBy: size];
    }
    [OLAlgorithm insertionSortFrom: f to: last predicate: pred];
    [alloc deallocateObject: l];
    [alloc deallocateObject: f];
}
// }}}

// copyBackwardImplFrom:to:destination:needItor: {{{
+ (OLBidirectionalIterator*) copyBackwardImplFrom: (OLBidirectionalIterator*)first to: (OLBidirectionalIterator*)last destination: (OLBidirectionalIterator*)dest needItor: (BOOL)needItor
{
    OLAllocator* alloc = ALG_ALLOC;
    OLBidirectionalIterator* src = [last copyWithAllocator: alloc];
    OLBidirectionalIterator* dst = [dest copyWithAllocator: alloc];

    while (![src isEqual: first])
        [[dst reverse] assign: [[src reverse] dereference]];
    [alloc deallocateObject: src];
    if (needItor)
        return dst;
    [alloc deallocateObject: dst];
    return nil;
}
// }}}

// copyImplFrom:to:destination:needItor: {{{
+ (OLForwardIterator*) copyImplFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last destination: (OLForwardIterator*)dest needItor: (BOOL)needItor
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* src = [first copyWithAllocator: alloc];
    OLForwardIterator* dst = [dest copyWithAllocator: alloc];

    for ( ; ![src isEqual: last]; [src advance], [dst advance])
        [dst assign: [src dereference]];
    [alloc deallocateObject: src];
    if (needItor)
        return dst;
    [alloc deallocateObject: dst];
    return nil;
}
// }}}

// finalInsertionSortFrom:to:predicate: {{{
+ (void) finalInsertionSortFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLRandomAccessIterator* tmp;

    if ([last difference: first] > OL_SORT_THRESHOLD)
    {
        tmp = [first copyWithAllocator: alloc];
        [tmp advanceBy: OL_SORT_THRESHOLD];
        [OLAlgorithm insertionSortFrom: first to: tmp predicate: pred];
        [OLAlgorithm unguardedInsertionSortFrom: tmp to: last predicate: pred];
        [alloc deallocateObject: tmp];
    }
    else
    {
        [OLAlgorithm insertionSortFrom: first to: last predicate: pred];
    }
}
// }}}

// findIfImplFrom:to:predicate: {{{
+ (OLForwardIterator*) findIfImplFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last predicate: (id<OLBoolUnaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* src = [first copyWithAllocator: alloc];

    for ( ; ![src isEqual: last]; [src advance])
    {
        if ([pred performUnaryFunctionWithArg: [src dereference]])
            break;
    }
    return src;
}
// }}}

// findImplFrom:to:value: {{{
+ (OLForwardIterator*) findImplFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last value: (id)object
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* src = [first copyWithAllocator: alloc];

    for ( ; ![src isEqual: last]; [src advance])
    {
        if ([[src dereference] isEqual: object])
            break;
    }
    return src;
}
// }}}

// insertionSortFrom:to:predicate: {{{
+ (void) insertionSortFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLRandomAccessIterator* i;

    if (![first isEqual: last])
    {
        i = [first copyWithAllocator: alloc];
        for ([i advance]; ![i isEqual: last]; [i advance])
            [OLAlgorithm linearInsertFrom: first to: i value: [i dereference] predicate: pred];
        [alloc deallocateObject: i];
    }
}
// }}}

// introSortLoopFrom:to:depthLimit:predicate: {{{
+ (void) introSortLoopFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last depthLimit: (unsigned)depthLimit predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLRandomAccessIterator* l = [last copyWithAllocator: alloc];
    OLRandomAccessIterator* cut;
    OLRandomAccessIterator* tmp = [first copyWithAllocator: alloc];
    id med;
    int advance;

    while ([l difference: first] > OL_SORT_THRESHOLD)
    {
        if (depthLimit == 0)
        {
            [OLAlgorithm partialSortFrom: first middle: l to: l predicate: pred];
            break;
        }
        depthLimit--;
        advance = [l difference: first] / 2;
        [tmp advanceBy: advance];
        [l reverse];
        med = [OLAlgorithm medianFromOne: [first dereference] two: [tmp dereference]
            three: [l dereference] predicate: pred];
        [tmp advanceBy: -advance];
        [l advance];
        cut = [OLAlgorithm unguardedPartitionFrom: first to: l pivot: med predicate: pred];
        [OLAlgorithm introSortLoopFrom: cut to: l depthLimit: depthLimit predicate: pred];
        [alloc deallocateObject: l];
        l = cut;
    }
    [alloc deallocateObject: l];
    [alloc deallocateObject: tmp];
}
// }}}

// linearInsertFrom:to:value:predicate: {{{
+ (void) linearInsertFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last value: (id)object predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLRandomAccessIterator* dest;
    id obj;

    if ([pred performBinaryFunctionWithArg: object andArg: [first dereference]])
    {
        obj = OBJ_RETAIN(object);
        dest = [last copyWithAllocator: alloc];
        [OLAlgorithm copyBackwardImplFrom: first to: last
            destination: [dest advance] needItor: NO];
        [alloc deallocateObject: dest];
        [first assign: obj];
        OBJ_RELEASE(obj);
    }
    else
    {
        [OLAlgorithm unguardedLinearInsertAt: last value: object predicate: pred];
    }
}
// }}}

// medianFromOne:two:three:predicate: {{{
+ (id) medianFromOne: (id)one two: (id)two three: (id)three predicate: (id<OLBoolBinaryFunction>)pred
{
    if ([pred performBinaryFunctionWithArg: one andArg: two])
    {
        if ([pred performBinaryFunctionWithArg: two andArg: three])
            return two;
        else if ([pred performBinaryFunctionWithArg: one andArg: three])
            return three;
        else
            return one;
    }
    else if ([pred performBinaryFunctionWithArg: one andArg: three])
    {
        return one;
    }
    else if ([pred performBinaryFunctionWithArg: two andArg: three])
    {
        return three;
    }
    return two;
}
// }}}

// mergeAdaptiveFrom:middle:last:len1:len2:buffer:predicate: {{{
+ (void) mergeAdaptiveFrom: (OLBidirectionalIterator*)first
                            middle: (OLBidirectionalIterator*)middle
                            last: (OLBidirectionalIterator*)last
                            len1: (unsigned)len1
                            len2: (unsigned)len2
                            buffer: (OLTempBuf*)buf
                            predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLBidirectionalIterator* firstCut;
    OLBidirectionalIterator* secondCut;
    OLBidirectionalIterator* newMiddle;
    OLBidirectionalIterator* bufEnd;
    unsigned len11;
    unsigned len22;

    if (len1 <= len2 && len1 <= [buf size])
    {
        bufEnd = (OLBidirectionalIterator*)[OLAlgorithm copyImplFrom: first to: middle
            destination: [buf begin] needItor: YES];
        [OLAlgorithm mergeImplSeries1From: [buf begin] series1To: bufEnd
            series2From: middle series2To: last destination: first
            predicate: pred needItor: NO];
        [alloc deallocateObject: bufEnd];
    }
    else if (len2 <= [buf size])
    {
        bufEnd = (OLBidirectionalIterator*)[OLAlgorithm copyImplFrom: middle to: last
            destination: [buf begin] needItor: YES];
        [OLAlgorithm mergeBackwardSeries1From: first series1To: middle
            series2From: [buf begin] series2To: bufEnd destination: last
            predicate: pred needItor: NO];
        [alloc deallocateObject: bufEnd];
    }
    else
    {
        if (len1 > len2)
        {
            firstCut = [first copyWithAllocator: alloc];
            len11 = len1 / 2;
            [OLIterator advanceIterator: firstCut distance: len11];
            secondCut = (OLBidirectionalIterator*)[OLAlgorithm boundImplFrom: middle
                to: last value: [firstCut dereference] predicate: pred lower: YES];
            len22 = [OLIterator distanceFrom: middle to: secondCut allocator: alloc];
        }
        else
        {
            secondCut = [middle copyWithAllocator: alloc];
            len22 = len2 / 2;
            [OLIterator advanceIterator: secondCut distance: len22];
            firstCut = (OLBidirectionalIterator*)[OLAlgorithm boundImplFrom: first
                to: middle value: [secondCut dereference] predicate: pred lower: NO];
            len11 = [OLIterator distanceFrom: middle to: secondCut allocator: alloc];
        }
        newMiddle = [OLAlgorithm rotateAdaptiveFirst: firstCut middle: middle last: secondCut
            len1: len1 - len11 len2: len22 buffer: buf];
        [OLAlgorithm mergeAdaptiveFrom: first middle: firstCut last: newMiddle
            len1: len11 len2: len22 buffer: buf predicate: pred];
        [OLAlgorithm mergeAdaptiveFrom: newMiddle middle: secondCut last: last
            len1: len1 - len11 len2: len2 - len22 buffer: buf predicate: pred];
        [alloc deallocateObject: firstCut];
        [alloc deallocateObject: secondCut];
        [alloc deallocateObject: newMiddle];
    }
}
// }}}

// mergeBackwardSeries1From:series1To:series2From:series2To:destination:predicate: {{{
+ (OLBidirectionalIterator*) mergeBackwardSeries1From: (OLBidirectionalIterator*)first1 series1To: (OLBidirectionalIterator*)last1 series2From: (OLBidirectionalIterator*)first2 series2To: (OLBidirectionalIterator*)last2 destination: (OLBidirectionalIterator*)result predicate: (id<OLBoolBinaryFunction>)pred needItor: (BOOL)needItor
{
    OLAllocator* alloc = ALG_ALLOC;
    OLBidirectionalIterator* l1;
    OLBidirectionalIterator* l2;
    OLBidirectionalIterator* res;
    OLBidirectionalIterator* rc;

    if ([first1 isEqual: last1])
    {
        return [OLAlgorithm copyBackwardImplFrom: first2 to: last2
            destination: result needItor: needItor];
    }
    if ([first2 isEqual: last2])
    {
        return [OLAlgorithm copyBackwardImplFrom: first1 to: last1
            destination: result needItor: needItor];
    }
    l1 = [last1 copyWithAllocator: alloc];
    l2 = [last2 copyWithAllocator: alloc];
    res = [result copyWithAllocator: alloc];
    [l1 reverse];
    [l2 reverse];
    while (YES)
    {
        if ([pred performBinaryFunctionWithArg: [l2 dereference]
                andArg: [l1 dereference]])
        {
            [[res reverse] assign: [l1 dereference]];
            if ([first1 isEqual: l1])
            {
                rc = [OLAlgorithm copyBackwardImplFrom: first2 to: [l2 advance]
                    destination: res needItor: needItor];
                break;
            }
            [l1 reverse];
        }
        else
        {
            [[res reverse] assign: [l2 dereference]];
            if ([first2 isEqual: l2])
            {
                rc = [OLAlgorithm copyBackwardImplFrom: first1 to: [l1 advance]
                    destination: res needItor: needItor];
                break;
            }
            [l2 reverse];
        }
    }
    [alloc deallocateObject: l1];
    [alloc deallocateObject: l2];
    [alloc deallocateObject: res];
    return rc;
}
// }}}

// mergeImplSeries1From:series1To:series2From:series2To:destination:predicate:needItor: {{{
+ (OLForwardIterator*) mergeImplSeries1From: (OLForwardIterator*)first1 series1To: (OLForwardIterator*)last1 series2From: (OLForwardIterator*)first2 series2To: (OLForwardIterator*)last2 destination: (OLForwardIterator*)dest predicate: (id<OLBoolBinaryFunction>)pred needItor: (BOOL)needItor
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* f1 = [first1 copyWithAllocator: alloc];
    OLForwardIterator* f2 = [first2 copyWithAllocator: alloc];
    OLForwardIterator* result = [dest copyWithAllocator: alloc];
    OLForwardIterator* tmp1;
    OLForwardIterator* tmp2;

    while (![f1 isEqual: last1] && ![f2 isEqual: last2])
    {
        if ([pred performBinaryFunctionWithArg: [f2 dereference]
                andArg: [f1 dereference]])
        {
            [result assign: [f2 dereference]];
            [f2 advance];
        }
        else
        {
            [result assign: [f1 dereference]];
            [f1 advance];
        }
        [result advance];
    }
    tmp1 = [OLAlgorithm copyImplFrom: f1 to: last1 destination: result needItor: YES];
    tmp2 = [OLAlgorithm copyImplFrom: f2 to: last2 destination: tmp1 needItor: needItor];
    [alloc deallocateObject: tmp1];
    [alloc deallocateObject: f1];
    [alloc deallocateObject: f2];
    [alloc deallocateObject: result];
    return tmp2;
}
// }}}

// mergeSortLoopFrom:to:destination:stepSize:predicate: {{{
+ (void) mergeSortLoopFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last destination: (OLRandomAccessIterator*)dest stepSize: (unsigned)stepSize predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLRandomAccessIterator* f = [first copyWithAllocator: alloc];
    OLRandomAccessIterator* f2 = [first copyWithAllocator: alloc];
    OLRandomAccessIterator* f3 = [first copyWithAllocator: alloc];
    OLForwardIterator* dst = [dest copyWithAllocator: alloc];
    OLForwardIterator* tmp;
    unsigned twoStep = stepSize * 2;

    [f2 advanceBy: stepSize];
    [f3 advanceBy: twoStep];
    while ([last difference: f] >= twoStep)
    {
        tmp = [OLAlgorithm mergeImplSeries1From: f series1To: f2
            series2From: f2 series2To: f3 destination: dst
            predicate: pred needItor: YES];
        [alloc deallocateObject: dst];
        dst = tmp;
        [f advanceBy: twoStep];
        [f2 advanceBy: twoStep];
        [f3 advanceBy: twoStep];
    }
    [f2 advanceBy: -stepSize];
    stepSize = MIN([last difference: f], stepSize);
    [f2 advanceBy: stepSize];
    [OLAlgorithm mergeImplSeries1From: f series1To: f2
        series2From: f2 series2To: last destination: dst
        predicate: pred needItor: NO];
    [alloc deallocateObject: f];
    [alloc deallocateObject: f2];
    [alloc deallocateObject: f3];
    [alloc deallocateObject: dst];
}
// }}}

// mergeSortWithBufferFrom:to:buffer:predicate: {{{
+ (void) mergeSortWithBufferFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last buffer: (OLTempBuf*)buf predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    unsigned len = [last difference: first];
    unsigned stepSize = OL_CHUNK_SIZE;
    OLArrayIterator* bufEnd = [[buf begin] copyWithAllocator: alloc];

    [bufEnd advanceBy: len];
    [OLAlgorithm chunkInsertionSortFrom: first to: last chunkSize: stepSize predicate: pred];
    while (stepSize < len)
    {
        [OLAlgorithm mergeSortLoopFrom: first to: last destination: [buf begin]
            stepSize: stepSize predicate: pred];
        stepSize *= 2;
        [OLAlgorithm mergeSortLoopFrom: [buf begin] to: bufEnd destination: first
            stepSize: stepSize predicate: pred];
        stepSize *= 2;
    }
    [alloc deallocateObject: bufEnd];
}
// }}}

// popHeapImplFirst:last:result:value:predicate: {{{
+ (void) popHeapImplFirst: (OLRandomAccessIterator*)first last: (OLRandomAccessIterator*)last result: (OLRandomAccessIterator*)result value: (id)object predicate: (id<OLBoolBinaryFunction>)pred
{
    OBJ_RETAIN(object);
    [result assign: [first dereference]];
    [OLAlgorithm adjustHeapFirst: first hole: 0 len: [last difference: first]
        value: object predicate: pred];
    OBJ_RELEASE(object);
}
// }}}

// pushHeapImplFirst:hole:top:value:predicate: {{{
+ (void) pushHeapImplFirst: (OLRandomAccessIterator*)first hole: (unsigned)hole top: (unsigned)top value: (id)object predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLRandomAccessIterator* f = [first copyWithAllocator: alloc];
    OLRandomAccessIterator* f2 = [first copyWithAllocator: alloc];
    unsigned parent = (hole - 1) / 2;

    OBJ_RETAIN(object);
    while (hole > top)
    {
        [f advanceBy: parent];
        if (![pred performBinaryFunctionWithArg: [f dereference] andArg: object])
        {
            [f advanceBy: -parent];
            break;
        }
        [f2 advanceBy: hole];
        [f2 assign: [f dereference]];
        [f advanceBy: -parent];
        [f2 advanceBy: -hole];
        hole = parent;
        parent = (hole - 1) / 2;
    }
    [[f advanceBy: hole] assign: object];
    OBJ_RELEASE(object);
    [alloc deallocateObject: f];
    [alloc deallocateObject: f2];
}
// }}}

// rotateAdaptiveFirst:middle:last:len1:len2:buffer: {{{
+ (OLBidirectionalIterator*) rotateAdaptiveFirst: (OLBidirectionalIterator*)first
        middle: (OLBidirectionalIterator*)middle
        last: (OLBidirectionalIterator*)last
        len1: (unsigned)len1
        len2: (unsigned)len2
        buffer: (OLTempBuf*)buf
{
    if (len1 > len2 && len2 <= [buf size])
    {
        [OLAlgorithm copyImplFrom: middle to: last destination: [buf begin] needItor: NO];
        [OLAlgorithm copyBackwardImplFrom: first to: middle destination: last needItor: NO];
        return [OLAlgorithm copyBackwardImplFrom: [buf begin] to: [buf end]
            destination: last needItor: YES];
    }
    else if (len1 <= [buf size])
    {
        [OLAlgorithm copyImplFrom: first to: middle destination: [buf begin] needItor: NO];
        [OLAlgorithm copyImplFrom: middle to: last destination: first needItor: NO];
        return [OLAlgorithm copyBackwardImplFrom: [buf begin] to: [buf end]
            destination: last needItor: YES];
    }
    else
    {
        return (OLBidirectionalIterator*)[OLAlgorithm rotateFrom: first
            middle: middle to: last];
    }
}
// }}}

// rotateImplFrom:middle:to:last: {{{
+ (OLForwardIterator*) rotateImplFrom: (OLForwardIterator*)first middle: (OLForwardIterator*)mid to: (OLForwardIterator*)last
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* f1;
    OLForwardIterator* f2;
    OLForwardIterator* mid1;
    OLForwardIterator* newMid;

    if ([first isEqual: mid])
        return last;
    if ([last isEqual: mid])
        return first;
    f1 = [first copyWithAllocator: alloc];
    f2 = [mid copyWithAllocator: alloc];
    mid1 = [mid copyWithAllocator: alloc];
    do
    {
        [OLAlgorithm swapIterators: f1 and: f2];
        [f1 advance];
        [f2 advance];
        if ([f1 isEqual: mid1])
        {
            [alloc deallocateObject: mid1];
            mid1 = [f2 copyWithAllocator: alloc];
        }
    } while (![f2 isEqual: last]);
    newMid = [f1 copyWithAllocator: alloc];
    [alloc deallocateObject: f2];
    f2 = [mid1 copyWithAllocator: alloc];
    while (![f2 isEqual: last])
    {
        [OLAlgorithm swapIterators: f1 and: f2];
        [f1 advance];
        [f2 advance];
        if ([f1 isEqual: mid1])
        {
            [alloc deallocateObject: mid1];
            mid1 = [f2 copyWithAllocator: alloc];
        }
        else if ([f2 isEqual: last])
        {
            [alloc deallocateObject: f2];
            f2 = [mid1 copyWithAllocator: alloc];
        }
    }
    [alloc deallocateObject: f1];
    [alloc deallocateObject: f2];
    [alloc deallocateObject: mid1];
    return newMid;
}
// }}}

// stablePartitionAdaptiveFrom:to:predicate:length:buffer: {{{
+ (OLForwardIterator*) stablePartitionAdaptiveFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last predicate: (id<OLBoolUnaryFunction>)pred length: (unsigned)len buffer: (OLTempBuf*)buf
{
    OLAllocator* alloc = ALG_ALLOC;
    OLForwardIterator* middle;
    OLForwardIterator* rfirst;
    OLForwardIterator* rlast;
    OLForwardIterator* result1;
    OLArrayIterator* result2;
    OLForwardIterator* f;

    if (len <= [buf size])
    {
        result1 = [first copyWithAllocator: alloc];
        result2 = [[buf begin] copyWithAllocator: alloc];
        f = [first copyWithAllocator: alloc];
        for ( ; ![f isEqual: last]; [f advance])
        {
            if ([pred performUnaryFunctionWithArg: [f dereference]])
            {
                [result1 assign: [f dereference]];
                [result1 advance];
            }
            else
            {
                [result2 assign: [f dereference]];
                [result2 advance];
            }
        }
        [OLAlgorithm copyImplFrom: [buf begin] to: result2 destination: result1 needItor: NO];
        [alloc deallocateObject: f];
        [alloc deallocateObject: result2];
    }
    else
    {
        middle = [first copyWithAllocator: alloc];
        [OLIterator advanceIterator: middle distance: [buf size] / 2];
        rfirst = [OLAlgorithm stablePartitionAdaptiveFrom: first to: middle predicate: pred
            length: len / 2 buffer: buf];
        rlast = [OLAlgorithm stablePartitionAdaptiveFrom: middle to: last predicate: pred
            length: len - len / 2 buffer: buf];
        result1 = [OLAlgorithm rotateImplFrom: rfirst middle: middle to: rlast];
        [alloc deallocateObject: middle];
        [alloc deallocateObject: rfirst];
        [alloc deallocateObject: rlast];
    }
    return result1;
}
// }}}

// stableSortAdaptiveFrom:to:buffer:predicate: {{{
+ (void) stableSortAdaptiveFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last buffer: (OLTempBuf*)buf predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLRandomAccessIterator* middle = [first copyWithAllocator: alloc];
    unsigned len = ([last difference: first] + 1) / 2;

    [middle advanceBy: len];
    if (len > [buf size])
    {
        [OLAlgorithm stableSortAdaptiveFrom: first to: middle buffer: buf predicate: pred];
        [OLAlgorithm stableSortAdaptiveFrom: middle to: last buffer: buf predicate: pred];
    }
    else
    {
        [OLAlgorithm mergeSortWithBufferFrom: first to: middle buffer: buf predicate: pred];
        [OLAlgorithm mergeSortWithBufferFrom: middle to: last buffer: buf predicate: pred];
    }
    [OLAlgorithm mergeAdaptiveFrom: first middle: middle last: last
        len1: [middle difference: first] len2: [last difference: middle]
        buffer: buf predicate: pred];
    [alloc deallocateObject: middle];
}
// }}}

// unguardedLinearInsertAt:value:predicate: {{{
+ (void) unguardedLinearInsertAt: (OLRandomAccessIterator*)last value: (id)object predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLRandomAccessIterator* next = [last copyWithAllocator: alloc];
    OLRandomAccessIterator* l = [last copyWithAllocator: alloc];

    [next reverse];
    OBJ_RETAIN(object);
    while ([pred performBinaryFunctionWithArg: object andArg: [next dereference]])
    {
        [l assign: [next dereference]];
        [l reverse];
        [next reverse];
    }
    [l assign: object];
    OBJ_RELEASE(object);
    [alloc deallocateObject: l];
    [alloc deallocateObject: next];
}
// }}}

// unguardedInsertionSortFrom:to:predicate: {{{
+ (void) unguardedInsertionSortFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLRandomAccessIterator* i = [first copyWithAllocator: alloc];

    for ( ; ![i isEqual: last]; [i advance])
        [OLAlgorithm unguardedLinearInsertAt: i value: [i dereference] predicate: pred];
    [alloc deallocateObject: i];
}
// }}}

// unguardedPartitionFrom:to:pivot:predicate: {{{
+ (OLRandomAccessIterator*) unguardedPartitionFrom: (OLRandomAccessIterator*)first to: (OLRandomAccessIterator*)last pivot: (id)object predicate: (id<OLBoolBinaryFunction>)pred
{
    OLAllocator* alloc = ALG_ALLOC;
    OLRandomAccessIterator* f = [first copyWithAllocator: alloc];
    OLRandomAccessIterator* l = [last copyWithAllocator: alloc];

    while (YES)
    {
        while ([pred performBinaryFunctionWithArg: [f dereference]
                    andArg: object])
        {
            [f advance];
        }
        [l reverse];
        while ([pred performBinaryFunctionWithArg: object
                    andArg: [l dereference]])
        {
            [l reverse];
        }
        if ([f difference: l] >= 0)
        {
            [alloc deallocateObject: l];
            return f;
        }
        [OLAlgorithm swapIterators: f and: l];
        [f advance];
    }
}
// }}}

@end
// }}}
