// Return new array with duplicate values removed
Array.prototype.unique = function() {
	var arr = [ ];
	var l = this.length;
	for (var i = 0; i < l; i ++) {
		for (var j = i+1; j < l; j ++) {
			if (this[i] === this[j]) {
				j = ++i;
			}
		}
		arr.push(this[i]);
	}
	return arr;
};

Array.prototype.maxBy = function(f) {
    var max = this[0];
    for (var i = 1; i < this.length; i++) {
        if (f(this[i]) > f(max)) {
            max = this[i];
        }
    }

    return max;
};

Array.prototype.minBy = function(f) {
    var min = this[0];
    for (var i = 1; i < this.length; i++) {
        if (f(this[i]) < f(min)) {
            min = this[i];
        }
    }

    return min;
};


/**
 * Searchs for the given value in an ordered array and returns its index.
 *
 * @param x The value to lookup.
 * @param f A function to apply to the array elements before comparison.
 * @param fail_ Function called when the search fails. Receives the last search
 *              index. This argument is optional: by default returns -1.
 * @return If the value is found, returns its index.
 *         If the value is not found, returns the result of calling fail().
 * @example [1, 2, 3, 4].binSearchBy(3, function (x) { return x; }) === 2
 *          [[1, 'a'], [2, 'b'], [3, 'c'], [4, 'd']]
 *            .binSearch(3, function(x) { return x[0]; }) === 2
 *          [1, 2, 4].binSearchBy(3, function (x) { return x; }) === -1
 */
Array.prototype.binSearchBy = function (x, f, fail_) {
    var begin = 0,
        end = this.length - 1,
        middle,
        point,
        fail = fail_ || function(middle) { return -1; };

    while (begin <= end) {
        middle = Math.floor((end - begin) / 2 + begin);
        point = this[middle];

        if (x < f(point)) {
            end = middle - 1;
        } else if (x > f(point)) {
            begin = middle + 1;
        } else {
            return middle;
        }
    }

    return fail(middle);
};

