#import "PriorityQueueTest.h"
#import "PriorityQueue.h"
#import "Util.h"
#import "TestUtil.h"

NSArray* RandomPermutation(NSUInteger count);
NSArray* Permutations(NSUInteger count);

NSArray* RandomPermutation(NSUInteger count) {
    NSUInteger d[count];
    for (NSUInteger i = 0; i < count; i++)
        d[i] = i;
    for (NSUInteger i = 0; i < count; i++) {
        NSUInteger j = arc4random_uniform(count - i) + i;
        NSUInteger t = d[i];
        d[i] = d[j];
        d[j] = t;
    }
    NSMutableArray* r = [NSMutableArray array];
    for (NSUInteger i = 0; i < count; i++)
        [r addObject:[NSNumber numberWithUnsignedInteger:d[i]]];
    return r;
}
NSArray* Permutations(NSUInteger count) {
    if (count == 0) return [NSArray array];
    NSMutableArray* r = [NSMutableArray array];
    for (NSArray* s in Permutations(count - 1)) {
        for (NSUInteger e = 0; e < count; e++) {
            NSMutableArray* a = [NSMutableArray array];
            [a addObject:[NSNumber numberWithUnsignedInteger:e]];
            for (NSNumber* x in s) {
                [a addObject:[x unsignedIntegerValue] < e ? x : [NSNumber numberWithUnsignedInteger:[x unsignedIntegerValue] + 1]];
            }
            [r addObject:a];
        }
    }
    return r;
}

@implementation PriorityQueueTest

-(void) testTrivialPrioritizing {
    PriorityQueue* q = [PriorityQueue priorityQueueAscendingWithComparator:^(id obj1, id obj2){ return
        [obj1 intValue] - [obj2 intValue]; }];
    test([q count] == 0);
    testThrows([q peek]);
    testThrows([q dequeue]);
    
    [q enqueue:@1];
    [q enqueue:@2];
    [q enqueue:@3];
    test([q count] == 3);
    test([[q peek] intValue] == 1);
    test([[q dequeue] intValue] == 1);
    test([[q peek] intValue] == 2);
    test([[q dequeue] intValue] == 2);
    test([[q peek] intValue] == 3);
    test([[q dequeue] intValue] == 3);
    testThrows([q peek]);
    testThrows([q dequeue]);
}
-(void) testOrdersByComparator {
    PriorityQueue* q = [PriorityQueue priorityQueueAscendingWithComparator:^(id obj1, id obj2){ return
        [obj2 intValue] - [obj1 intValue]; }];
    [q enqueue:@1];
    [q enqueue:@2];
    [q enqueue:@3];
    test([[q dequeue] intValue] == 3);
    test([[q dequeue] intValue] == 2);
    test([[q dequeue] intValue] == 1);
}
-(void) testSortsAllSmallPermutations {
    const NSUInteger N = 7;
    for (NSArray* permutation in Permutations(N)) {
        PriorityQueue* q = [PriorityQueue priorityQueueAscendingWithComparator:^(id obj1, id obj2){ return
            [obj1 intValue] - [obj2 intValue]; }];
        for (NSNumber* e in permutation) {
            [q enqueue:e];
        }
        
        // dequeues in order
        for (NSUInteger i = 0; i < N; i++) {
            test([q count] == N - i);
            test([[q dequeue] unsignedIntegerValue] == i);
        }
        test([q count] == 0);
    }
}
-(void) testSortsRandomLargePermutations {
    const NSUInteger Size = 500;
    const NSUInteger Repetitions = 50;
    for (NSUInteger repeat = 0; repeat < Repetitions; repeat++) {
        PriorityQueue* q = [PriorityQueue priorityQueueAscendingWithComparator:^(id obj1, id obj2){ return
            [obj1 intValue] - [obj2 intValue]; }];
        NSArray* permutation = RandomPermutation(Size);
        for (NSNumber* e in permutation) {
            [q enqueue:e];
        }
        
        // dequeues in order
        for (NSUInteger i = 0; i < Size; i++) {
            test([q count] == Size - i);
            test([[q dequeue] unsignedIntegerValue] == i);
        }
        test([q count] == 0);
    }
}


@end
