/* Assertion */

function AssertionError(msg) 
{
    this.msg = msg;
}

AssertionError.prototype.toString = function()
{
    return "AssertionError: " + this.msg;
};

function assert(expr, msg) 
{
    if (!expr) 
    {
        throw new AssertionError(msg);
    }
}



/* Queue */

function Queue(args) 
{
    this.queue = [];
    this.front = 0;

    /* `optimize` flag is a space-time tradeoff:
        If disabled, will conserve memory by cleaning up references. */
    args = args || {};
    this.optimize = args.optimize || false;
}

Queue.prototype.isEmpty = function() 
{
    return this.queue.length <= this.front;
};

Queue.prototype.push = function(obj) 
{
    this.queue.push(obj);
};

Queue.prototype.pop = function() 
{
    // Return 'undefined' for empty queues
    if (this.isEmpty())
    {
        return undefined;
    }

    // Store ref
    var obj = this.queue[this.front];

    // Delete reference if we're unoptimized
    if (!this.optimize)
    {
        this.queue[this.front] = null;
    }

    // Move "front" pointer of queue
    this.front++;

    // Prune array
    if (this.front > this.queue.length / 2) 
    {
        this.queue = this.queue.slice(this.front);
        this.front = 0;
    }

    return obj;
};

Queue.prototype.peek = function() 
{
    return this.queue[this.front];
};

