# This script implements a circular queue and
# its relative tests!

fun newCircularQueue(T: Type = int, max: int = 3) {
    
    if T == None:
        error("Invalid type");

    if max <= 0:
        error("Maximum queue size must be positive");
    
    list = [];
    i = 0;
    while i < max: {
        list[i] = none;
        i = i+1;
    }

    return {
        max: max,
        head: 0,
        tail: 0,
        list: list,
        type: T
    };
}

fun getSize(queue: Map) {
    
    size = none;
    if queue.tail == queue.head: {
        if queue.list[queue.tail] == none: {
            size = 0;
        } else {
            size = queue.max;
        }
    } else if queue.tail < queue.head: {
        size = queue.head 
             - queue.tail;
    } else {
        size = queue.head 
             + queue.max 
             - queue.tail;
    }
    return size;
}

fun append(queue: Map, value) {
    
    if type(value) != queue.type:
        error("Invalid type");

    size = getSize(queue);

    queue.list[queue.head] = value;

    if size == queue.max: {
        queue.tail = queue.tail+1;
        if queue.tail == queue.max:
            queue.tail = 0;
    }

    queue.head = queue.head+1;
    if queue.head == queue.max:
        queue.head = 0;
}

fun pop(queue: Map) {
    
    if getSize(queue) == 0:
        return none;

    value = queue.list[queue.tail];
    queue.list[queue.tail] = none;

    queue.tail = queue.tail+1;
    if queue.tail == queue.max:
        queue.tail = 0;

    return value;
}

{
    utils = import("../utils.noja");

    queue = newCircularQueue(int, 3);
    assert(getSize(queue) == 0);
    assert(queue.head == 0);
    assert(queue.tail == 0);

    append(queue, 1);
    assert(queue.head == 1);
    assert(queue.tail == 0);
    assert(getSize(queue) == 1);
    assert(utils.compareAny(queue.list, [1, none, none]));

    append(queue, 2);
    assert(queue.head == 2);
    assert(queue.tail == 0);
    assert(getSize(queue) == 2);
    assert(utils.compareAny(queue.list, [1, 2, none]));

    append(queue, 3);
    assert(queue.head == 0);
    assert(queue.tail == 0);
    assert(getSize(queue) == 3);
    assert(utils.compareAny(queue.list, [1, 2, 3]));

    append(queue, 4);
    assert(queue.head == 1);
    assert(queue.tail == 1);
    assert(getSize(queue) == 3);
    assert(utils.compareAny(queue.list, [4, 2, 3]));

    v = pop(queue);
    assert(v == 2);
    assert(queue.head == 1);
    assert(queue.tail == 2);
    assert(getSize(queue) == 2);
    assert(utils.compareAny(queue.list, [4, none, 3]));

    v = pop(queue);
    assert(v == 3);
    assert(queue.head == 1);
    assert(queue.tail == 0);
    assert(getSize(queue) == 1);
    assert(utils.compareAny(queue.list, [4, none, none]));

    v = pop(queue);
    assert(v == 4);
    assert(queue.head == 1);
    assert(queue.tail == 1);
    assert(getSize(queue) == 0);
    assert(utils.compareAny(queue.list, [none, none, none]));

    v = pop(queue);
    assert(v == none);
    assert(queue.head == 1);
    assert(queue.tail == 1);
    assert(getSize(queue) == 0);
    assert(utils.compareAny(queue.list, [none, none, none]));
}