//
// $Id: List.m,v 1.28 2005/05/18 06:46:53 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 "List.h"
#import "Macros.h"
#import "ContainerCoder.h"
#import <limits.h>

@interface OLListNode : NSObject
{
@public
    id          object;
    OLListNode* previous;
    OLListNode* next;
}
@end

@interface OLListIterator (PrivateMethods)

- (id) initWithNode: (OLListNode*)nde;
- (OLListNode*) node;
- (void) setNode: (OLListNode*)n;

@end

@interface OLList (PrivateMethods)

- (OLListIterator*) beginImpl;
- (OLListIterator*) endImpl;
- (OLListNode*) eraseImpl: (OLListIterator*)where;
- (OLListIterator*) eraseImplFrom: (OLListIterator*)first to: (OLListIterator*)last needItor: (BOOL)needItor;
- (OLListNode*) insertImpl: (OLListIterator*)where value: (id)object;
- (void) moveNodesTo: (OLListNode*)pos from: (OLListNode*)first to: (OLListNode*)last;

@end

@implementation OLList

+ (id) list
{
    OL_BEGIN_AUTO_CTOR(OLList)
        init
    OL_END_AUTO_CTOR;
}

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

+ (id) listWithList: (OLList*)right
{
    OL_BEGIN_AUTO_CTOR(OLList)
        initWithList: right
    OL_END_AUTO_CTOR;
}

+ (id) listWithSize: (unsigned)size filledWith: (id)value
{
    OL_BEGIN_AUTO_CTOR(OLList)
        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
{
    OLListIterator* where;

    [self initWithAllocator: alloc];
    where = [self beginImpl];
    [self insertAt: where from: first to: last];
    [allocator deallocateObject: where];
    return self;
}

- (id) initWithAllocator: (OLAllocator*)alloc
{
    [super init];
    allocator = OBJ_RETAIN(alloc);
    node = [[allocator allocateObject: [OLListNode class]] init];
    node->object = nil;
    node->previous = node;
    node->next = node;
    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) initWithList: (OLList*)list
{
    OLListIterator* itorBegin = [list beginImpl];
    OLListIterator* itorEnd = [list endImpl];

    [self initFrom: itorBegin to: itorEnd];
    [allocator deallocateObject: itorBegin];
    [allocator deallocateObject: itorEnd];
    return self;
}

- (id) initWithSize: (unsigned)size filledWith: (id)value
{
    OLListIterator* itorBegin;

    [self init];
    itorBegin = [self beginImpl];
    [self insertAt: itorBegin count: size filledWith: value];
    [allocator deallocateObject: itorBegin];
    return self;
}

- (void) dealloc
{
    [self clear];
    [allocator deallocateObject: node];
    OBJ_RELEASE(allocator);
    [super dealloc];
}

- (void) assign: (unsigned)count filledWith: (id)value
{
    OLListIterator* itorBegin = [self beginImpl];
    OLListIterator* itorEnd = [self endImpl];
    OLListNode* nde;

    for ( ; ![itorBegin isEqual: itorEnd] && count > 0; [itorBegin advance], --count)
    {
        nde = [itorBegin node];
        OBJ_RELEASE(nde->object);
        nde->object = OBJ_RETAIN(value);
    }
    if (count > 0)
    {
        [self insertAt: itorEnd count: count filledWith: value];
    }
    else
    {
        [self eraseImplFrom: itorBegin to: itorEnd needItor: NO];
    }
    [allocator deallocateObject: itorBegin];
    [allocator deallocateObject: itorEnd];
}

- (void) assignFrom: (OLForwardIterator*)first to: (OLForwardIterator*)last
{
    OLListIterator* myFirst = [self beginImpl];
    OLListIterator* myLast = [self endImpl];
    OLListIterator* firstCopy = [first copyWithAllocator: allocator];
    OLListNode* nde;

    for ( ; !([firstCopy isEqual: last] || [myFirst isEqual: myLast]);
            [firstCopy advance], [myFirst advance])
    {
        nde = [myFirst node];
        OBJ_RELEASE(nde->object);
        nde->object = OBJ_RETAIN([firstCopy dereference]);
    }
    if ([firstCopy isEqual: last])
    {
        [self eraseImplFrom: myFirst to: myLast needItor: NO];
    }
    else
    {
        [self insertAt: myLast from: firstCopy to: last];
    }
    [allocator deallocateObject: myFirst];
    [allocator deallocateObject: myLast];
    [allocator deallocateObject: firstCopy];
}

- (id) back
{
    return node->previous->object;
}

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

- (void) clear
{
    OLListNode* cur = node->next;
    OLListNode* tmp;

    while (cur != node)
    {
        tmp = cur;
        cur = cur->next;
        OBJ_RELEASE(tmp->object);
        [allocator deallocateObject: tmp];
    }
    node->next = node;
    node->previous = node;
}

- (id) copyWithAllocator: (OLAllocator*)allocatr
{
    return [[allocatr allocateObject: [OLList class]]
        initWithList: self];
}

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

- (BOOL) empty
{
    return (node->next == node) ? YES : NO;
}

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

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

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

- (OLListIterator*) erase: (OLListIterator*)where
{
    return OBJ_COND_AUTORELEASE([[allocator allocateObject: [OLListIterator class]]
        initWithNode: [self eraseImpl: where]]);
}

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

- (id) front
{
    return node->next->object;
}

- (OLAllocator*) allocator
{
    return allocator;
}

- (OLListIterator*) insertAt: (OLListIterator*)where value: (id)object
{
    return OBJ_COND_AUTORELEASE([[allocator allocateObject: [OLListIterator class]]
        initWithNode: [self insertImpl: where value: object]]);
}

- (void) insertAt: (OLListIterator*)where count: (unsigned)num filledWith: (id)value
{
    for ( ; num > 0; --num)
        [self insertImpl: where value: value];
}

- (void) insertAt: (OLListIterator*)where from: (OLForwardIterator*)first to: (OLForwardIterator*)last
{
    OLForwardIterator* firstCopy = [first copyWithAllocator: allocator];

    for ( ; ![firstCopy isEqual: last]; [firstCopy advance])
        [self insertImpl: where value: [firstCopy dereference]];
    [allocator deallocateObject: firstCopy];
}

- (BOOL) isEqual: (id)object
{
    OLList* other;
    OLListIterator* myFirst;
    OLListIterator* myLast;
    OLListIterator* itsFirst;
    BOOL broken = NO;

    if (![object isKindOfClass: [OLList class]])
        return NO;
    other = (OLList*)object;
    if ([other size] != [self size])
        return NO;
    myFirst = [self beginImpl];
    myLast = [self endImpl];
    itsFirst = [other beginImpl];
    for ( ; ![myFirst isEqual: myLast]; [myFirst advance], [itsFirst advance])
    {
        if (![[myFirst dereference] isEqual: [itsFirst dereference]])
        {
            broken = YES;
            break;
        }
    }
    [allocator deallocateObject: myFirst];
    [allocator deallocateObject: myLast];
    [allocator deallocateObject: itsFirst];
    return broken ? NO : YES;
}

- (unsigned) maxSize
{
    return UINT_MAX;
}

- (void) merge: (OLList*)right
{
    OLLess* less = [[allocator allocateObject: [OLLess class]] init];

    [self merge: right withOrder: less];
    [allocator deallocateObject: less];
}

- (void) merge: (OLList*)right withOrder: (id<OLBoolBinaryFunction>)pred
{
    OLListIterator* thisFirst = [self beginImpl];
    OLListIterator* thisLast = [self endImpl];
    OLListIterator* thatFirst = [right beginImpl];
    OLListIterator* thatLast = [right endImpl];
    OLListIterator* tmp;
    BOOL sameAllocator = [allocator isEqual: right->allocator];
    OLListNode* next;

    while (!([thisFirst isEqual: thisLast] || [thatFirst isEqual: thatLast]))
    {
        if ([pred performBinaryFunctionWithArg:
                [thatFirst dereference] andArg: [thisFirst dereference]])
        {
            if (sameAllocator)
            {
                next = [thatFirst node]->next;
                [self moveNodesTo: [thisFirst node] from: [thatFirst node] to: next];
                [thatFirst setNode: next];
            }
            else
            {
                [self insertImpl: thisFirst value: [thatFirst node]->object];
                tmp = [right erase: thatFirst];
                tmp = [[allocator allocateObject: [OLListIterator class]] initWithNode:
                    [[right erase: thatFirst] node]];
                [allocator deallocateObject: thatFirst];
                thatFirst = tmp;
            }
        }
        else
        {
            [thisFirst advance];
        }
    }
    if (![thatFirst isEqual: thatLast])
    {
        [self insertAt: thisLast from: thatFirst to: thatLast];
        [right eraseImplFrom: thatFirst to: thatLast needItor: NO];
    }
    [allocator deallocateObject: thisFirst];
    [allocator deallocateObject: thisLast];
    [allocator deallocateObject: thatFirst];
    [allocator deallocateObject: thatLast];
}

- (void) popBack
{
    OLListIterator* last;

    if (![self empty])
    {
        last = [self endImpl];
        [last reverse];
        [self eraseImpl: last];
        [allocator deallocateObject: last];
    }
}

- (void) popFront
{
    OLListIterator* first;

    if (![self empty])
    {
        first = [self beginImpl];
        [self eraseImpl: first];
        [allocator deallocateObject: first];
    }
}

- (void) pushBack: (id)object
{
    OLListIterator* last = [self endImpl];

    [self insertImpl: last value: object];
    [allocator deallocateObject: last];
}

- (void) pushFront: (id)object
{
    OLListIterator* first = [self beginImpl];

    [self insertImpl: first value: object];
    [allocator deallocateObject: first];
}

- (OLReverseBidiIterator*) rbegin
{
    OLListIterator* last = [self endImpl];
    OLReverseBidiIterator* rb = OBJ_COND_AUTORELEASE([[allocator allocateObject:
        [OLReverseBidiIterator class]] initWithIterator: last]);

    [allocator deallocateObject: last];
    return rb;
}

- (void) remove: (id)object
{
    OLListIterator* first = [self beginImpl];
    OLListIterator* last = [self endImpl];
    OLListIterator* tmp;
    Class itorClass = [OLListIterator class];

    while (![first isEqual: last])
    {
        if ([object isEqual: [first dereference]])
        {
            tmp = [[allocator allocateObject: itorClass]
                initWithNode: [self eraseImpl: first]];
            [allocator deallocateObject: first];
            first = tmp;
        }
        else
        {
            [first advance];
        }
    }
    [allocator deallocateObject: first];
    [allocator deallocateObject: last];
}

- (void) removeIf: (id<OLBoolUnaryFunction>)pred
{
    OLListIterator* first = [self beginImpl];
    OLListIterator* last = [self endImpl];
    OLListIterator* tmp;
    Class itorClass = [OLListIterator class];

    while (![first isEqual: last])
    {
        if ([pred performUnaryFunctionWithArg: [first dereference]])
        {
            tmp = [[allocator allocateObject: itorClass]
                initWithNode: [self eraseImpl: first]];
            [allocator deallocateObject: first];
            first = tmp;
        }
        else
        {
            [first advance];
        }
    }
    [allocator deallocateObject: first];
    [allocator deallocateObject: last];
}

- (OLReverseBidiIterator*) rend
{
    OLListIterator* first = [self beginImpl];
    OLReverseBidiIterator* re = OBJ_COND_AUTORELEASE([[allocator allocateObject:
        [OLReverseBidiIterator class]] initWithIterator: first]);

    [allocator deallocateObject: first];
    return re;
}

- (void) resize: (unsigned)count filledWith: (id)value
{
    OLListIterator* first = [self beginImpl];
    OLListIterator* last = [self endImpl];
    unsigned len = 0;

    for ( ; ![first isEqual: last] && len < count; [first advance], len++);

    if (len == count)
    {
        [self eraseImplFrom: first to: last needItor: NO];
    }
    else
    {
        [self insertAt: last count: count - len filledWith: value];
    }
    [allocator deallocateObject: first];
    [allocator deallocateObject: last];
}

- (void) reverse
{
    OLListNode* last = node;
    OLListNode* cur = last;
    OLListNode* tmp;

    do
    {
        tmp = cur->next;
        cur->next = cur->previous;
        cur->previous = tmp;
        cur = cur->previous;
    } while (cur != last);
}

- (unsigned) size
{
    OLListIterator* first = [self beginImpl];
    OLListIterator* last = [self endImpl];
    unsigned sz = [OLIterator distanceFrom: first to: last];

    [allocator deallocateObject: first];
    [allocator deallocateObject: last];
    return sz;
}

- (void) sort
{
    OLLess* less = [[allocator allocateObject: [OLLess class]] init];

    [self sortWith: less];
    [allocator deallocateObject: less];
}

- (void) sortWith: (id<OLBoolBinaryFunction>)pred
{
    OLList*         counter[64];
    OLList*         carry;
    OLListIterator* carryBegin;
    OLListIterator* myBegin;
    int             fill = 0;
    int             i;

    if (node->next != node && node->next->next != node)
    {
        for (i = 0; i < 64; i++)
            counter[i] = [[allocator allocateObject: [OLList class]] initWithAllocator: allocator];
        carry = [[allocator allocateObject: [OLList class]] initWithAllocator: allocator];
        while (![self empty])
        {
            carryBegin = [carry beginImpl];
            myBegin = [self beginImpl];
            [carry spliceAt: carryBegin list: self from: myBegin];
            [allocator deallocateObject: carryBegin];
            [allocator deallocateObject: myBegin];
            i = 0;
            while (i < fill && ![counter[i] empty])
            {
                [counter[i] merge: carry withOrder: pred];
                [carry swap: counter[i++]];
            }
            [carry swap: counter[i]];
            if (i == fill)
                fill++;
        }
        for (i = 1; i < fill; i++)
            [counter[i] merge: counter[i - 1] withOrder: pred];
        [self swap: counter[fill - 1]];
        for (i = 0; i < 64; i++)
            [allocator deallocateObject: counter[i]];
        [allocator deallocateObject: carry];
    }
}

- (void) spliceAt: (OLListIterator*)where list: (OLList*)right
{
    OLListIterator* rightBegin = [right beginImpl];
    OLListIterator* rightEnd = [right endImpl];

    [self spliceAt: where list: right from: rightBegin to: rightEnd];
    [allocator deallocateObject: rightBegin];
    [allocator deallocateObject: rightEnd];
}

- (void) spliceAt: (OLListIterator*)where list: (OLList*)right from: (OLListIterator*)first
{
    OLListIterator* rightEnd = [[[allocator allocateObject: [OLListIterator class]]
        initWithNode: [first node]] advance];

    [self spliceAt: where list: right from: first to: rightEnd];
    [allocator deallocateObject: rightEnd];
}

- (void) spliceAt: (OLListIterator*)where list: (OLList*)right from: (OLListIterator*)first to: (OLListIterator*)last
{
    OLListIterator* firstCopy;

    if (![first isEqual: last])
    {
        if ([allocator isEqual: right->allocator])
        {
            [self moveNodesTo: [where node] from: [first node] to: [last node]];
        }
        else
        {
            [self insertAt: where from: first to: last];
            firstCopy = [[allocator allocateObject: [OLListIterator class]]
                initWithNode: [first node]];
            for ( ; ![firstCopy isEqual: last]; [firstCopy advance])
                [right eraseImpl: firstCopy];
            [allocator deallocateObject: firstCopy];
        }
    }
}

- (void) swap: (OLList*)right
{
    OL_PREPARE_SLOW_SWAP;
    OLList* tmpList;
    OLListIterator* itorBegin;
    OLListIterator* itorEnd;

    if (self != right)
    {
        if ([allocator isEqual: right->allocator])
        {
            OL_SLOW_SWAP(node, right->node);
        }
        else
        {
            itorBegin = [self beginImpl];
            itorEnd = [self endImpl];
            tmpList = [[allocator allocateObject: [OLList class]]
                initFrom: itorBegin to: itorEnd allocator: allocator];
            [allocator deallocateObject: itorBegin];
            [allocator deallocateObject: itorEnd];
            itorBegin = [right beginImpl];
            itorEnd = [right endImpl];
            [self assignFrom: itorBegin to: itorEnd];
            [allocator deallocateObject: itorBegin];
            [allocator deallocateObject: itorEnd];
            itorBegin = [tmpList beginImpl];
            itorEnd = [tmpList endImpl];
            [right assignFrom: itorBegin to: itorEnd];
            [allocator deallocateObject: itorBegin];
            [allocator deallocateObject: itorEnd];
            [allocator deallocateObject: tmpList];
        }
    }
}

- (void) unique
{
    OLEqualTo* equal = [[allocator allocateObject: [OLEqualTo class]] init];

    [self uniqueWith: equal];
    [allocator deallocateObject: equal];
}

- (void) uniqueWith: (id<OLBoolBinaryFunction>)pred
{
    OLListIterator* first;
    OLListIterator* last;
    OLListIterator* next;

    if (![self empty])
    {
        first = [self beginImpl];
        last = [self endImpl];
        next = [self beginImpl];
        while (![[next advance] isEqual: last])
        {
            if ([pred performBinaryFunctionWithArg:
                    [first dereference] andArg: [next dereference]])
            {
                [self eraseImpl: next];
            }
            else
            {
                [first setNode: [next node]];
            }
            [next setNode: [first node]];
        }
        [allocator deallocateObject: first];
        [allocator deallocateObject: last];
        [allocator deallocateObject: next];
    }
}

@end

@implementation OLListNode

@end

@implementation OLListIterator

- (id) advance
{
    node = node->next;
    return self;
}

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

- (id) copyWithAllocator: (OLAllocator*)allocator
{
    return [[allocator allocateObject: [OLListIterator class]]
        initWithNode: node];
}

- (id) copyWithZone: (NSZone*)zone
{
    return [[OLListIterator allocWithZone: zone] initWithNode: node];
}

- (id) dereference
{
    return node->object;
    return self;
}

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

- (id) reverse
{
    node = node->previous;
    return self;
}

@end

@implementation OLListIterator (PrivateMethods)

- (id) initWithNode: (OLListNode*)nde
{
    [super init];
    node = nde;
    return self;
}

- (OLListNode*) node
{
    return node;
}

- (void) setNode: (OLListNode*)n
{
    node = n;
}

@end

@implementation OLList (PrivateMethods)

- (OLListIterator*) beginImpl
{
    return [[allocator allocateObject: [OLListIterator class]] initWithNode: node->next];
}

- (OLListIterator*) endImpl
{
    return [[allocator allocateObject: [OLListIterator class]] initWithNode: node];
}

- (OLListNode*) eraseImpl: (OLListIterator*)where
{
    OLListNode* nde = [where node];
    OLListNode* next = nde->next;
    OLListNode* prev = nde->previous;

    prev->next = next;
    next->previous = prev;
    OBJ_RELEASE(nde->object);
    [allocator deallocateObject: nde];
    return next;
}

- (OLListIterator*) eraseImplFrom: (OLListIterator*)first to: (OLListIterator*)last needItor: (BOOL)needItor
{
    OLListIterator* firstCopy = [first copyWithAllocator: allocator];

    for ( ; ![firstCopy isEqual: last]; [firstCopy advance])
        [self eraseImpl: firstCopy];
    [allocator deallocateObject: firstCopy];
    return needItor ?
        [[allocator allocateObject: [OLListIterator class]] initWithNode: [last node]] :
        nil;
}

- (OLListNode*) insertImpl: (OLListIterator*)where value: (id)object
{
    OLListNode* tmp = [[allocator allocateObject: [OLListNode class]] init];
    OLListNode* next = [where node];
    OLListNode* prev = next->previous;

    tmp->object = OBJ_RETAIN(object);
    tmp->next = next;
    tmp->previous = prev;
    prev->next = tmp;
    next->previous = tmp;
    return tmp;
}

- (void) moveNodesTo: (OLListNode*)pos from: (OLListNode*)first to: (OLListNode*)last
{
    OLListNode* tmp;

    if (pos != last)
    {
        last->previous->next = pos;
        first->previous->next = last;
        pos->previous->next = first;
        tmp = pos->previous;
        pos->previous = last->previous;
        last->previous = first->previous;
        first->previous = tmp;
    }
}

@end
