//
// $Id: Deque.m,v 1.18 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 "Deque.h"
#import "Macros.h"
#import "ContainerCoder.h"
#import <limits.h>
#import <string.h>
#import <Foundation/NSCoder.h>

#define OL_BUFFER_SIZE (256 / sizeof(id))
#define OL_INITIAL_MAP_SIZE 8

@interface OLDequeIterator (PrivateMethods)

- (id) init;
- (id) initWithCurrent: (id*)cur node: (id**)nde;
- (id) initWithDequeIterator: (OLDequeIterator*)right;
- (id*) current;
- (id*) first;
- (id**) node;
- (id*) last;
- (void) setCurrent: (id*)cur;
- (void) setNode: (id**)newNode;

@end

@interface OLDeque (PrivateMethods)

- (OLDequeIterator*) beginImpl;
- (id) blankInitializer: (OLAllocator*)alloc;
- (void) copyFrom: (OLDequeIterator*)first to: (OLDequeIterator*)last dest: (OLDequeIterator*)dest;
- (void) copyBackwardsFrom: (OLDequeIterator*)first to: (OLDequeIterator*)last dest: (OLDequeIterator*)dest;
- (void) copyForeignFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last dest: (OLDequeIterator*)dest;
- (void) destroyNodesFrom: (id**)first to: (id**)last;
- (OLDequeIterator*) endImpl;
- (OLDequeIterator*) eraseImplFrom: (OLDequeIterator*)first to: (OLDequeIterator*)last needItor: (BOOL)needItor;
- (OLDequeIterator*) insertPrepare: (OLDequeIterator*)where;
- (void) initializeMap: (unsigned)size;
- (void) popBackImpl: (BOOL)release;
- (void) popFrontImpl: (BOOL)release;
- (id*) pushBackPrepare;
- (id*) pushFrontPrepare;
- (void) reallocateMap: (unsigned)newNodes atFront: (BOOL)atFront;
- (OLDequeIterator*) reserveElementsAtBack: (unsigned)count;
- (OLDequeIterator*) reserveElementsAtFront: (unsigned)count;
- (void) reserveMapAtBack: (unsigned)numberOfNodes;
- (void) reserveMapAtFront: (unsigned)numberOfNodes;

@end

@implementation OLDeque

+ (id) deque
{
    OL_BEGIN_AUTO_CTOR(OLDeque)
        init
    OL_END_AUTO_CTOR;
}

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

+ (id) dequeWithDeque: (OLDeque*)right
{
    OL_BEGIN_AUTO_CTOR(OLDeque)
        initWithDeque: right
    OL_END_AUTO_CTOR;
}

+ (id) dequeWithSize: (unsigned)size filledWith: (id)value
{
    OL_BEGIN_AUTO_CTOR(OLDeque)
        initWithSize: size filledWith: value
    OL_END_AUTO_CTOR;
}

- (id) init
{
    return [self initWithAllocator: [OLAllocator defaultAllocator]];
}

- (id) initFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last
{
    return [self initFrom: first to: last allocator: [OLAllocator defaultAllocator]];
}

- (id) initFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last allocator: (OLAllocator*)alloc
{
    OLForwardIterator* firstCopy;

    [self blankInitializer: alloc];
    [self initializeMap: 0];
    firstCopy = [first copyWithAllocator: allocator];
    for( ; ![firstCopy isEqual: last]; [firstCopy advance])
        [self pushBack: [firstCopy dereference]];
    [allocator deallocateObject: firstCopy];
    return self;
}

- (id) initWithAllocator: (OLAllocator*)alloc
{
    [self blankInitializer: alloc];
    [self initializeMap: 0];
    return self;
}

- (id) initWithCoder: (NSCoder*)decoder
{
    OLAllocator* alloc = [OLContainerCoder decodeAllocatorWithCoder: decoder];
    unsigned count;
    id* array = [OLContainerCoder decodeWithCoder: decoder allocator: alloc
        returnedCount: &count];
    OLArrayIterator* first = [[alloc allocateObject: [OLArrayIterator class]]
        initWithPointer: array];
    OLArrayIterator* last = [[alloc allocateObject: [OLArrayIterator class]]
        initWithPointer: array + count];

    [self initFrom: first to: last allocator: alloc];
    [alloc deallocateBytes: array];
    [alloc deallocateObject: first];
    [alloc deallocateObject: last];
    return self;
}

- (id) initWithDeque: (OLDeque*)deque
{
    [self blankInitializer: deque->allocator];
    [self initializeMap: [deque size]];
    [self copyForeignFrom: deque->start to: deque->finish dest: start];
    return self;
}

- (id) initWithSize: (unsigned)count filledWith: (id)value
{
    return [self initWithSize: count filledWith: value allocator: [OLAllocator defaultAllocator]];
}

- (id) initWithSize: (unsigned)count filledWith: (id)value allocator: (OLAllocator*)alloc
{
    id** cur;
    id* dcur;

    [self blankInitializer: alloc];
    [self initializeMap: count];
    for (cur = [start node]; cur < [finish node]; cur++)
    {
        for (dcur = *cur; dcur < *cur + OL_BUFFER_SIZE; dcur++)
            *dcur = OBJ_RETAIN(value);
    }
    for (dcur = [finish first]; dcur < [finish current]; dcur++)
        *dcur = OBJ_RETAIN(value);
    return self;
}

- (void) dealloc
{
    id** startNode = [start node];

    for ( ; ![start isEqual: finish]; [start advance])
        OBJ_RELEASE([start dereference]);
    if (map != NULL)
    {
        [self destroyNodesFrom: startNode to: [finish node] + 1];
        [allocator deallocateBytes: map];
    }
    [allocator deallocateObject: start];
    [allocator deallocateObject: finish];
    [super dealloc];
}

- (void) assign: (unsigned)count filledWith: (id)value
{
    OLDequeIterator* first = [self beginImpl];
    OLDequeIterator* eraseFrom;
    id* obj;
    unsigned mySize = [self size];

    if (count > mySize)
    {
        for ( ; ![first isEqual: finish]; [first advance])
        {
            obj = [first current];
            OBJ_RELEASE(*obj);
            *obj = OBJ_RETAIN(value);
        }
        [self insertAt: finish count: count - mySize filledWith: value];
    }
    else
    {
        eraseFrom = [first copyWithAllocator: allocator];
        [self eraseImplFrom: [eraseFrom advanceBy: count] to: finish needItor: NO];
        for ( ; ![first isEqual: finish]; [first advance])
        {
            obj = [first current];
            OBJ_RELEASE(*obj);
            *obj = OBJ_RETAIN(value);
        }
        [allocator deallocateObject: eraseFrom];
    }
    [allocator deallocateObject: first];
}

- (void) assignAt: (unsigned)index value: (id)object
{
    id* target;

    [start advanceBy: index];
    target = [start current];
    if (*target != object)
    {
        OBJ_RELEASE(*target);
        *target = OBJ_RETAIN(object);
    }
    [start advanceBy: -index];
}

- (void) assignFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last
{
    OLDequeIterator* firstCopy = [first copyWithAllocator: allocator];
    OLDequeIterator* myFirst = [self beginImpl];
    id* cur;

    for ( ; !([firstCopy isEqual: last] || [myFirst isEqual: finish]);
         [firstCopy advance], [myFirst advance])
    {
        cur = [myFirst current];
        OBJ_RELEASE(*cur);
        *cur = OBJ_RETAIN([firstCopy dereference]);
    }
    if ([firstCopy isEqual: last])
        [self eraseImplFrom: myFirst to: finish needItor: NO];
    else
        [self insertAt: finish from: firstCopy to: last];
    [allocator deallocateObject: firstCopy];
    [allocator deallocateObject: myFirst];
}

- (id) at: (unsigned)index
{
    id obj = [[start advanceBy: index] dereference];

    [start advanceBy: -index];
    return obj;
}

- (id) back
{
    id obj = [[finish reverse] dereference];

    [finish advance];
    return obj;
}

- (OLDequeIterator*) begin
{
    return OBJ_COND_AUTORELEASE([self beginImpl]);
}

- (void) clear
{
    id** node;
    id* cur;

    for (node = [start node] + 1; node < [finish node]; node++)
    {
        for (cur = *node; cur < *node + OL_BUFFER_SIZE; cur++)
            OBJ_RELEASE(*cur);
        [allocator deallocateBytes: *node];
    }
    if ([start node] != [finish node])
    {
        for (cur = [start current]; cur < [start last]; cur++)
            OBJ_RELEASE(*cur);
        for (cur = [finish first]; cur < [finish current]; cur++)
            OBJ_RELEASE(*cur);
        [allocator deallocateBytes: [finish first]];
    }
    else
    {
        for (cur = [start current]; cur < [finish current]; cur++)
            OBJ_RELEASE(*cur);
    }
    [allocator deallocateObject: finish];
    finish = [start copyWithAllocator: allocator];
}

- (id) copyWithAllocator: (OLAllocator*)alloc
{
    return [[allocator allocateObject: [OLDeque class]] initWithDeque: self];
}

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

- (BOOL) empty
{
    return [start current] == [finish current];
}

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

    [OLContainerCoder encodeAllocator: allocator withCoder: encoder];
    [OLContainerCoder encodeFrom: first count: [self size]
        withCoder: encoder allocator: allocator];
    [allocator deallocateObject: first];
}

- (OLDequeIterator*) end
{
    return OBJ_COND_AUTORELEASE([self endImpl]);
}

- (OLDequeIterator*) erase: (OLDequeIterator*)where
{
    OLDequeIterator* next = [where copyWithAllocator: allocator];
    int diff;

    [next advance];
    diff = [where difference: start];
    OBJ_RELEASE([where dereference]);
    if ([next difference: start] < [self size] >> 1)
    {
        [self copyBackwardsFrom: start to: where dest: next];
        [self popFrontImpl: NO];
    }
    else
    {
        [self copyFrom: next to: finish dest: where];
        [self popBackImpl: NO];
    }
    [allocator deallocateObject: next];
    return OBJ_COND_AUTORELEASE([[[allocator allocateObject: [OLDequeIterator class]]
        initWithDequeIterator: start] advanceBy: diff]);
}

- (OLDequeIterator*) eraseFrom: (OLDequeIterator*)first to: (OLDequeIterator*)last
{
    return OBJ_COND_AUTORELEASE([self eraseImplFrom: first to: last needItor: YES]);
}

- (id) front
{
    return [start dereference];
}

- (OLAllocator*) allocator
{
    return allocator;
}

- (OLDequeIterator*) insertAt: (OLDequeIterator*)where value: (id)object
{
    OLDequeIterator* result;

    if ([where current] == [start current])
    {
        [self pushFront: object];
        result = [start copyWithAllocator: allocator];
    }
    else if ([where current] == [finish current])
    {
        [self pushBack: object];
        result = [finish copyWithAllocator: allocator];
        [result reverse];
    }
    else
    {
        result = [self insertPrepare: where];
        *[result current] = OBJ_RETAIN(object);
    }
    return OBJ_COND_AUTORELEASE(result);
}

- (void) insertAt: (OLDequeIterator*)where count: (unsigned)num filledWith: (id)value
{
    OLDequeIterator* itor;
    OLDequeIterator* newPos;
    OLDequeIterator* oldStart;
    OLDequeIterator* pos;
    OLDequeIterator* startN;
    OLDequeIterator* oldFinish;
    OLDequeIterator* finishN;
    OLDequeIterator* fillLast;
    int elemsBefore;
    int elemsAfter;
    unsigned length;

    if ([where current] == [start current])
    {
        newPos = [self reserveElementsAtFront: num];
        itor = [newPos copyWithAllocator: allocator];
        for ( ; ![itor isEqual: start]; [itor advance])
            *[itor current] = OBJ_RETAIN(value);
        [allocator deallocateObject: itor];
        [allocator deallocateObject: start];
        start = newPos;
    }
    else if ([where current] == [finish current])
    {
        newPos = [self reserveElementsAtBack: num];
        itor = [finish copyWithAllocator: allocator];
        for ( ; ![itor isEqual: newPos]; [itor advance])
            *[itor current] = OBJ_RETAIN(value);
        [allocator deallocateObject: itor];
        [allocator deallocateObject: finish];
        finish = newPos;
    }
    else
    {
        elemsBefore = [where difference: start];
        length = [self size];
        if (elemsBefore < length / 2)
        {
            newPos = [self reserveElementsAtFront: num];
            oldStart = [start copyWithAllocator: allocator];
            pos = [start copyWithAllocator: allocator];
            [pos advanceBy: elemsBefore];
            if (elemsBefore >= num)
            {
                startN = [start copyWithAllocator: allocator];
                [startN advanceBy: num];
                [self copyFrom: start to: startN dest: newPos];
                [allocator deallocateObject: start];
                start = newPos;
                [self copyFrom: startN to: pos dest: oldStart];
                newPos = [pos copyWithAllocator: allocator];
                [newPos advanceBy: -num];
                for ( ; ![newPos isEqual: pos]; [newPos advance])
                    *[newPos current] = OBJ_RETAIN(value);
                [allocator deallocateObject: newPos];
            }
            else
            {
                [self copyFrom: start to: pos dest: newPos];
                startN = [newPos copyWithAllocator: allocator];
                for ( ; ![startN isEqual: start]; [startN advance])
                    *[startN current] = OBJ_RETAIN(value);
                [allocator deallocateObject: start];
                start = newPos;
                for ( ; ![oldStart isEqual: pos]; [oldStart advance])
                    *[oldStart current] = OBJ_RETAIN(value);
            }
            [allocator deallocateObject: oldStart];
            [allocator deallocateObject: startN];
        }
        else
        {
            newPos = [self reserveElementsAtBack: num];
            oldFinish = [finish copyWithAllocator: allocator];
            elemsAfter = length - elemsBefore;
            pos = [finish copyWithAllocator: allocator];
            [pos advanceBy: -elemsAfter];
            if (elemsAfter > num)
            {
                finishN = [finish copyWithAllocator: allocator];
                [finishN advanceBy: -num];
                [self copyFrom: finishN to: finish dest: finish];
                [allocator deallocateObject: finish];
                finish = newPos;
                [self copyBackwardsFrom: pos to: finishN dest: oldFinish];
                newPos = [pos copyWithAllocator: allocator];
                [newPos advanceBy: num];
                for ( ; ![pos isEqual: newPos]; [pos advance])
                    *[pos current] = OBJ_RETAIN(value);
            }
            else
            {
                finishN = [finish copyWithAllocator: allocator];
                fillLast = [pos copyWithAllocator: allocator];
                [fillLast advanceBy: num];
                for ( ; ![finishN isEqual: fillLast]; [finishN advance])
                    *[finishN current] = OBJ_RETAIN(value);
                [self copyFrom: pos to: finish dest: fillLast];
                [allocator deallocateObject: fillLast];
                [allocator deallocateObject: finish];
                finish = newPos;
                for ( ; ![pos isEqual: oldFinish]; [pos advance])
                    *[pos current] = OBJ_RETAIN(value);
            }
            [allocator deallocateObject: oldFinish];
            [allocator deallocateObject: finishN];
        }
        [allocator deallocateObject: pos];
    }
}

- (void) insertAt: (OLDequeIterator*)where from: (OLForwardIterator*)first to: (OLForwardIterator*)last
{
    OLDequeIterator* newPos;
    OLDequeIterator* oldStart;
    OLDequeIterator* pos;
    OLDequeIterator* startN;
    OLForwardIterator* mid;
    OLDequeIterator* oldFinish;
    OLDequeIterator* finishN;
    int n = [OLIterator distanceFrom: first to: last allocator: allocator];
    int elemsBefore;
    int elemsAfter;
    unsigned length;

    if ([where current] == [start current])
    {
        newPos = [self reserveElementsAtFront: n];
        [self copyForeignFrom: first to: last dest: newPos];
        [allocator deallocateObject: start];
        start = newPos;
    }
    else if ([where current] == [finish current])
    {
        newPos = [self reserveElementsAtBack: n];
        [self copyForeignFrom: first to: last dest: finish];
        [allocator deallocateObject: finish];
        finish = newPos;
    }
    else
    {
        elemsBefore = [where difference: start];
        length = [self size];
        if (elemsBefore < length / 2)
        {
            newPos = [self reserveElementsAtFront: n];
            oldStart = [start copyWithAllocator: allocator];
            pos = [start copyWithAllocator: allocator];
            [pos advanceBy: elemsBefore];
            if (elemsBefore >= n)
            {
                startN = [start copyWithAllocator: allocator];
                [startN advanceBy: n];
                [self copyFrom: start to: startN dest: newPos];
                [allocator deallocateObject: start];
                start = newPos;
                [self copyFrom: startN to: pos dest: oldStart];
                [pos advanceBy: -n];
                [self copyForeignFrom: first to: last dest: pos];
                [allocator deallocateObject: startN];
            }
            else
            {
                mid = [first copyWithAllocator: allocator];
                [OLIterator advanceIterator: mid distance: n - elemsBefore];
                [self copyFrom: start to: pos dest: newPos];
                [newPos advanceBy: elemsBefore];
                [self copyForeignFrom: first to: mid dest: newPos];
                [newPos advanceBy: -elemsBefore];
                [allocator deallocateObject: start];
                start = newPos;
                [allocator deallocateObject: mid];
            }
            [allocator deallocateObject: oldStart];
            [allocator deallocateObject: pos];
        }
        else
        {
            newPos = [self reserveElementsAtBack: n];
            oldFinish = [finish copyWithAllocator: allocator];
            elemsAfter = length - elemsBefore;
            pos = [finish copyWithAllocator: allocator];
            [pos advanceBy: -elemsAfter];
            if (elemsAfter > n)
            {
                finishN = [finish copyWithAllocator: allocator];
                [finishN advanceBy: -n];
                [self copyFrom: finishN to: finish dest: finish];
                [allocator deallocateObject: finish];
                finish = newPos;
                [self copyBackwardsFrom: pos to: finishN dest: oldFinish];
                [self copyForeignFrom: first to: last dest: pos];
                [allocator deallocateObject: finishN];
            }
            else
            {
                mid = [first copyWithAllocator: allocator];
                [OLIterator advanceIterator: mid distance: elemsAfter];
                [self copyForeignFrom: mid to: last dest: finish];
                finishN = [finish copyWithAllocator: allocator];
                [finishN advanceBy: [OLIterator distanceFrom: mid to: last allocator: allocator]];
                [self copyFrom: pos to: finish dest: finishN];
                [allocator deallocateObject: finish];
                finish = newPos;
                [self copyForeignFrom: first to: mid dest: pos];
                [allocator deallocateObject: finishN];
                [allocator deallocateObject: mid];
            }
            [allocator deallocateObject: oldFinish];
            [allocator deallocateObject: pos];
        }
    }
}

- (BOOL) isEqual: (id)object
{
    OLDeque* right;
    OLDequeIterator* myBegin;
    OLDequeIterator* itsBegin;
    BOOL equal = YES;

    if (![object isKindOfClass: [OLDeque class]])
        return NO;
    right = (OLDeque*)object;
    if ([right size] != [self size])
        return NO;
    myBegin = [self beginImpl];
    itsBegin = [right->start copyWithAllocator: allocator];
    for ( ; ![myBegin isEqual: finish]; [myBegin advance], [itsBegin advance])
    {
        if (![[myBegin dereference] isEqual: [itsBegin dereference]])
        {
            equal = NO;
            break;
        }
    }
    [allocator deallocateObject: myBegin];
    [allocator deallocateObject: itsBegin];
    return equal;
}

- (unsigned) maxSize
{
    return UINT_MAX;
}

- (void) popBack
{
    [self popBackImpl: YES];
}

- (void) popFront
{
    [self popFrontImpl: YES];
}

- (void) pushBack: (id)object
{
    *[self pushBackPrepare] = OBJ_RETAIN(object);
}

- (void) pushFront: (id)object
{
    *[self pushFrontPrepare] = OBJ_RETAIN(object);
}

- (OLReverseRandomIterator*) rbegin
{
    OLDequeIterator* itor = [self endImpl];
    OLReverseRandomIterator* rc = OBJ_COND_AUTORELEASE([[allocator allocateObject:
        [OLReverseRandomIterator class]] initWithIterator: itor]);

    [allocator deallocateObject: itor];
    return rc;
}

- (OLReverseRandomIterator*) rend
{
    OLDequeIterator* itor = [self beginImpl];
    OLReverseRandomIterator* rc = OBJ_COND_AUTORELEASE([[allocator allocateObject:
        [OLReverseRandomIterator class]] initWithIterator: itor]);

    [allocator deallocateObject: itor];
    return rc;
}

- (void) resize: (unsigned)newsize filledWith: (id)value
{
    OLDequeIterator* pos;
    unsigned length = [self size];

    if (newsize < length)
    {
        pos = [start copyWithAllocator: allocator];
        [pos advanceBy: newsize];
        [self eraseImplFrom: pos to: finish needItor: NO];
        [allocator deallocateObject: pos];
    }
    else
    {
        [self insertAt: finish count: newsize - length filledWith: value];
    }
}

- (unsigned) size
{
    return [finish difference: start];
}

- (void) swap: (OLDeque*)right
{
    OL_PREPARE_SLOW_SWAP;
    OLDeque* copy;

    if (self != right)
    {
        if ([allocator isEqual: right->allocator])
        {
            OL_SLOW_SWAP(start, right->start);
            OL_SLOW_SWAP(finish, right->finish);
            OL_SLOW_SWAP(map, right->map);
            OL_FAST_SWAP(mapSize, right->mapSize);
        }
        else
        {
            copy = [[allocator allocateObject: [OLDeque class]] initWithDeque: self];
            [self assignFrom: right->start to: right->finish];
            [right assignFrom: copy->start to: copy->finish];
            [allocator deallocateObject: copy];
        }
    }
}

@end

@implementation OLDequeIterator

- (id) advance
{
    current++;
    if (current == last)
    {
        [self setNode: node + 1];
        current = first;
    }
    return self;
}

- (id) advanceBy: (int)count
{
    int offset = count + (current - first);
    int nodeOffset;

    if (offset >= 0 && offset < OL_BUFFER_SIZE)
    {
        current += count;
    }
    else
    {
        nodeOffset = (offset > 0) ? offset / OL_BUFFER_SIZE :
            -((-offset - 1) / OL_BUFFER_SIZE) - 1;
        [self setNode: node + nodeOffset];
        current = first + (offset - nodeOffset * OL_BUFFER_SIZE);
    }
    return self;
}

- (id) assign: (id)object
{
    if (*current != object)
    {
        OBJ_RELEASE(*current);
        *current = OBJ_RETAIN(object);
    }
    return self;
}

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

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

- (id) dereference
{
    return *current;
}

- (int) difference: (OLRandomAccessIterator*)other
{
    int diff = 0;
    OLDequeIterator* right;

    if ([other isKindOfClass: [OLDequeIterator class]])
    {
        right = (OLDequeIterator*)other;
        diff = OL_BUFFER_SIZE * (node - right->node - 1) + (current - first) +
            (right->last - right->current);
    }
    return diff;
}

- (BOOL) isEqual: (id)object
{
    return [super isEqual: object] &&
           current == ((OLDequeIterator*)object)->current;
}

- (id) reverse
{
    if (current == first)
    {
        [self setNode: node - 1];
        current = last;
    }
    current--;
    return self;
}

@end

@implementation OLDequeIterator (PrivateMethods)

- (id) init
{
    [super init];
    current = NULL;
    first = NULL;
    last = NULL;
    node = NULL;
    return self;
}

- (id) initWithCurrent: (id*)cur node: (id**)nde
{
    [super init];
    current = cur;
    [self setNode: nde];
    return self;
}

- (id) initWithDequeIterator: (OLDequeIterator*)right
{
    [super init];
    current = right->current;
    first = right->first;
    last = right->last;
    node = right->node;
    return self;
}

- (id*) current
{
    return current;
}

- (id*) first
{
    return first;
}

- (id*) last
{
    return last;
}

- (id**) node
{
    return node;
}

- (void) setCurrent: (id*)cur
{
    current = cur;
}

- (void) setNode: (id**)newNode
{
    node = newNode;
    first = *node;
    last = first + OL_BUFFER_SIZE;
}

@end

@implementation OLDeque (PrivateMethods)

- (OLDequeIterator*) beginImpl
{
    return [start copyWithAllocator: allocator];
}

- (id) blankInitializer: (OLAllocator*)alloc
{
    [super init];
    allocator = OBJ_RETAIN(alloc);
    start = [[allocator allocateObject: [OLDequeIterator class]] init];
    finish = [[allocator allocateObject: [OLDequeIterator class]] init];
    map = NULL;
    mapSize = 0;
    return self;
}

- (void) copyFrom: (OLDequeIterator*)first to: (OLDequeIterator*)last dest: (OLDequeIterator*)dest
{
    OLDequeIterator* src;
    OLDequeIterator* dst;

    if (![first isEqual: last])
    {
        src = [first copyWithAllocator: allocator];
        dst = [dest copyWithAllocator: allocator];
        do
        {
            *[dst current] = [src dereference];
            [src advance];
            [dst advance];
        } while (![src isEqual: last]);
        [allocator deallocateObject: src];
        [allocator deallocateObject: dst];
    }
}

- (void) copyBackwardsFrom: (OLDequeIterator*)first to: (OLDequeIterator*)last dest: (OLDequeIterator*)dest
{
    OLDequeIterator* src;
    OLDequeIterator* dst;

    if (![first isEqual: last])
    {
        src = [last copyWithAllocator: allocator];
        dst = [dest copyWithAllocator: allocator];
        do
        {
            *[[dst reverse] current] = [[src reverse] dereference];
        } while (![src isEqual: first]);
        [allocator deallocateObject: src];
        [allocator deallocateObject: dst];
    }
}

- (void) copyForeignFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last dest: (OLDequeIterator*)dest
{
    OLForwardIterator* src;
    OLDequeIterator* dst;

    if (![first isEqual: last])
    {
        src = [first copyWithAllocator: allocator];
        dst = [dest copyWithAllocator: allocator];
        do
        {
            *[dst current] = OBJ_RETAIN([src dereference]);
            [src advance];
            [dst advance];
        } while (![src isEqual: last]);
        [allocator deallocateObject: src];
        [allocator deallocateObject: dst];
    }
}

- (void) destroyNodesFrom: (id**)first to: (id**)last
{
    id** cur;

    for (cur = first; cur < last; cur++)
        [allocator deallocateBytes: *cur];
}

- (OLDequeIterator*) endImpl
{
    return [finish copyWithAllocator: allocator];
}

- (OLDequeIterator*) eraseImplFrom: (OLDequeIterator*)first to: (OLDequeIterator*)last needItor: (BOOL)needItor
{
    OLDequeIterator* result;
    OLDequeIterator* scratch;
    int diff;
    int numBefore;

    if ([first isEqual: start] && [last isEqual: finish])
    {
        [self clear];
        result = [finish copyWithAllocator: allocator];
    }
    else
    {
        scratch = [first copyWithAllocator: allocator];
        for ( ; ![scratch isEqual: last]; [scratch advance])
            OBJ_RELEASE([scratch dereference]);
        [allocator deallocateObject: scratch];
        diff = [last difference: first];
        numBefore = [first difference: start];
        if (numBefore < ([self size] - diff) / 2)
        {
            [self copyBackwardsFrom: start to: first dest: last];
            scratch = [start copyWithAllocator: allocator];
            [scratch advanceBy: diff];
            [self destroyNodesFrom: [start node] to: [scratch node]];
            [allocator deallocateObject: start];
            start = scratch;
        }
        else
        {
            [self copyFrom: last to: finish dest: first];
            scratch = [finish copyWithAllocator: allocator];
            [scratch advanceBy: -diff];
            [self destroyNodesFrom: [scratch node] + 1 to: [finish node] + 1];
            [allocator deallocateObject: finish];
            finish = scratch;
        }
        result = [start copyWithAllocator: allocator];
        [result advanceBy: numBefore];
    }
    if (!needItor)
    {
        [allocator deallocateObject: result];
        result = nil;
    }
    return result;
}

- (OLDequeIterator*) insertPrepare: (OLDequeIterator*)where
{
    OLDequeIterator* result;
    OLDequeIterator* first1;
    int index = [where difference: start];

    if (index < [self size] / 2)
    {
        [self pushFrontPrepare];
        first1 = [start copyWithAllocator: allocator];
        [first1 advance];
        result = [start copyWithAllocator: allocator];
        [result advanceBy: index + 1];
        [self copyFrom: first1 to: result dest: start];
        [result reverse];
    }
    else
    {
        [self pushBackPrepare];
        first1 = [finish copyWithAllocator: allocator];
        [first1 reverse];
        result = [start copyWithAllocator: allocator];
        [result advanceBy: index];
        [self copyBackwardsFrom: result to: first1 dest: finish];
    }
    [allocator deallocateObject: first1];
    return result;
}

- (void) initializeMap: (unsigned)size
{
    unsigned numNodes = size / OL_BUFFER_SIZE + 1;
    id** nstart;
    id** nfinish;
    id** cur;

    mapSize = MAX(OL_INITIAL_MAP_SIZE, numNodes + 2);
    map = [allocator allocateBytes: mapSize * sizeof(id*)];
    nstart = map + (mapSize - numNodes) / 2;
    nfinish = nstart + numNodes;
    for (cur = nstart; cur < nfinish; cur++)
        *cur = [allocator allocateBytes: OL_BUFFER_SIZE * sizeof(id)];
    [start setNode: nstart];
    [finish setNode: nfinish - 1];
    [start setCurrent: [start first]];
    [finish setCurrent: [finish first] + size % OL_BUFFER_SIZE];
}

- (void) popBackImpl: (BOOL)release
{
    if ([finish current] != [finish first])
    {
        [finish setCurrent: [finish current] - 1];
    }
    else
    {
        [allocator deallocateBytes: [finish first]];
        [finish setNode: [finish node] - 1];
        [finish setCurrent: [finish last] - 1];
    }
    if (release)
        OBJ_RELEASE(*[finish current]);
}

- (void) popFrontImpl: (BOOL)release
{
    if (release)
        OBJ_RELEASE(*[start current]);
    if ([start current] != [start last] - 1)
    {
        [start setCurrent: [start current] + 1];
    }
    else
    {
        [allocator deallocateBytes: [start first]];
        [start setNode: [start node] + 1];
        [start setCurrent: [start first]];
    }
}

- (id*) pushBackPrepare
{
    id* pos = [finish current];

    if ([finish current] != [finish last] - 1)
    {
        [finish setCurrent: [finish current] + 1];
    }
    else
    {
        [self reserveMapAtBack: 1];
        *([finish node] + 1) = [allocator allocateBytes: OL_BUFFER_SIZE * sizeof(id)];
        [finish setNode: [finish node] + 1];
        [finish setCurrent: [finish first]];
    }
    return pos;
}

- (id*) pushFrontPrepare
{
    id* pos;

    if ([start current] != [start first])
    {
        [start setCurrent: [start current] - 1];
        pos = [start current];
    }
    else
    {
        [self reserveMapAtFront: 1];
        *([start node] - 1) = [allocator allocateBytes: OL_BUFFER_SIZE * sizeof(id)];
        [start setNode: [start node] - 1];
        [start setCurrent: [start last] - 1];
        pos = [start current];
    }
    return pos;
}

- (void) reallocateMap: (unsigned)newNodes atFront: (BOOL)atFront
{
    id** newNStart;
    id** newMap;
    unsigned oldNodeCount = [finish node] - [start node] + 1;
    unsigned newNodeCount = oldNodeCount + newNodes;
    unsigned newMapSize;

    if (mapSize > 2 * newNodeCount)
    {
        newNStart = map + (mapSize - newNodeCount) / 2 + (atFront ? newNodes : 0);
        if (newNStart < [start node])
            memmove(newNStart, [start node], oldNodeCount * sizeof(id*));
        else
            memmove(newNStart + oldNodeCount, [start node], oldNodeCount * sizeof(id*));
    }
    else
    {
        newMapSize = mapSize + MAX(mapSize, newNodes) + 2;
        newMap = [allocator allocateBytes: newMapSize * sizeof(id*)];
        newNStart = newMap + (newMapSize - newNodeCount) / 2 + (atFront ? newNodes : 0);
        memcpy(newNStart, [start node], oldNodeCount * sizeof(id*));
        [allocator deallocateBytes: map];
        map = newMap;
        mapSize = newMapSize;
    }
    [start setNode: newNStart];
    [finish setNode: newNStart + oldNodeCount - 1];
}

- (OLDequeIterator*) reserveElementsAtBack: (unsigned)count
{
    OLDequeIterator* result;
    unsigned vacancies = [finish last] - [finish current] - 1;
    unsigned newNodes;
    unsigned i;

    if (count > vacancies)
    {
        newNodes = ((count - vacancies) + OL_BUFFER_SIZE - 1) / OL_BUFFER_SIZE;
        [self reserveMapAtBack: newNodes];
        for (i = 1; i <= newNodes; i++)
            *([finish node] + i) = [allocator allocateBytes: OL_BUFFER_SIZE * sizeof(id)];
    }
    result = [finish copyWithAllocator: allocator];
    [result advanceBy: count];
    return result;
}

- (OLDequeIterator*) reserveElementsAtFront: (unsigned)count
{
    OLDequeIterator* result;
    unsigned vacancies = [start current] - [start first];
    unsigned newNodes;
    unsigned i;

    if (count > vacancies)
    {
        newNodes = ((count - vacancies) + OL_BUFFER_SIZE - 1) / OL_BUFFER_SIZE;
        [self reserveMapAtFront: newNodes];
        for (i = 1; i <= newNodes; i++)
            *([start node] - i) = [allocator allocateBytes: OL_BUFFER_SIZE * sizeof(id)];
    }
    result = [start copyWithAllocator: allocator];
    [result advanceBy: -count];
    return result;
}

- (void) reserveMapAtBack: (unsigned)numberOfNodes
{
    if (numberOfNodes + 1 > mapSize - ([finish node] - map))
        [self reallocateMap: numberOfNodes atFront: NO];
}

- (void) reserveMapAtFront: (unsigned)numberOfNodes
{
    if (numberOfNodes > [start node] - map)
        [self reallocateMap: numberOfNodes atFront: YES];
}

@end
