// Copyright 1997-1999 Omni Development, Inc.  All rights reserved.
//
// This software may only be used and reproduced according to the
// terms in the file OmniSourceLicense.html, which should be
// distributed with this project and can also be found at
// http://www.omnigroup.com/DeveloperResources/OmniSourceLicense.html.

#import "NSMutableArray-OFExtensions.h"

#import <Foundation/Foundation.h>
#import <OmniBase/OmniBase.h>

RCS_ID("$Header: /Network/Developer/Source/CVS/OmniGroup/OmniFoundation/OpenStepExtensions.subproj/NSMutableArray-OFExtensions.m,v 1.3 1999/03/30 21:25:44 kc Exp $")

@implementation NSMutableArray (OFExtensions)

/* Very slow, of course --- N^2 time. Doesn't do any sanity checks (eg,
   that anArray and self aren't the same array.) */
- (void)insertObjectsFromArray:(NSArray *)anArray atIndex:(unsigned int)anIndex
{
    unsigned int sourceIndex, sourceCount, destIndex;
    
    destIndex = anIndex;
    sourceIndex = 0;
    sourceCount = [anArray count];
    
    while (sourceIndex < sourceCount) {
        [self insertObject:[anArray objectAtIndex:sourceIndex]
		atIndex:destIndex];
	sourceIndex ++;
	destIndex ++;
    }
}

- (void)removeIdenticalObjectsFromArray:(NSArray *)removeArray;
{
    NSEnumerator               *removeEnumerator;
    id				removeObject;

    if (!removeArray)
	return;
    removeEnumerator = [removeArray objectEnumerator];
    while ((removeObject = [removeEnumerator nextObject]))
	[self removeObjectIdenticalTo:removeObject];
}

/* Assumes the array is already sorted to insert the object quickly in the right place */
/* (new objects are inserted just after old objects that are NSOrderedSame) */
- (void)insertObject:anObject inArraySortedUsingSelector:(SEL)selector;
{
    unsigned int low = 0;
    unsigned int range = 1;
    unsigned int test = 0;
    unsigned int count = [self count];
    NSComparisonResult result;
    IMP insertedImpOfSel = [anObject methodForSelector:selector];
    IMP objectAtIndexImp = [self methodForSelector:@selector(objectAtIndex:)];

    while (count >= range) /* range is the lowest power of 2 > count */
        range <<= 1;

    while (range) {
        test = low + (range >>= 1);
        if (test >= count)
            continue;
        result = (NSComparisonResult)insertedImpOfSel(anObject, selector, 
			objectAtIndexImp(self, @selector(objectAtIndex:), test));
        if (result == NSOrderedDescending)
            low = test+1;
    }
    [self insertObject:anObject atIndex:low];
}    

/* Assumes the array is already sorted to find the object quickly and remove it */
- (void)removeObject:anObject fromArraySortedUsingSelector:(SEL)selector
{
    unsigned int low = 0;
    unsigned int range = 1;
    unsigned int test = 0;
    unsigned int count = [self count];
    NSComparisonResult result;
    id compareWith;
    IMP removedImpOfSel = [anObject methodForSelector:selector];
    IMP objectAtIndexImp = [self methodForSelector:@selector(objectAtIndex:)];
    
    while (count >= range) /* range is the lowest power of 2 > count */
        range <<= 1;

    while (range) {
        test = low + (range >>= 1);
        if (test >= count)
            continue;
	compareWith = objectAtIndexImp(self, @selector(objectAtIndex:), test);
	if (compareWith == anObject) {
	    [self removeObjectAtIndex:test];
	    return;
	}
	result = (NSComparisonResult)removedImpOfSel(anObject, selector, compareWith);
	if (result == NSOrderedDescending)
            low = test+1;
	else if (result == NSOrderedSame) {
	    /* uh oh - now we can't be sure of finding it by binary splitting */
	    /* fall back on something disgustingly linear (but easy) */
	    int sameAt = test;

	    while (test--) {
		compareWith = objectAtIndexImp(self, @selector(objectAtIndex:), test);
		if (compareWith == anObject) {
		    [self removeObjectAtIndex:test];
		    return;
		}
		result = (NSComparisonResult)removedImpOfSel(anObject, selector,
								 compareWith);
		if (result != NSOrderedSame)
		    break;
	    }
	    test = sameAt;
	    while (++test < count) {
		compareWith = objectAtIndexImp(self, @selector(objectAtIndex:), test);
		if (compareWith == anObject) {
		    [self removeObjectAtIndex:test];
		    return;
		}
	    }
	}
    }
}

@end
