/* prelude.js library. http://code.google.com/p/prelude-js/ */

function stub(arg) {
    return arg
}

function map(func, seq) {
    var result = []
    for(var i = 0; i < seq.length; i++)
        result.push(func(seq[i]))
    return result
}

function each(func, seq) {
    for(var i = 0; i < seq.length; i++)
        func(seq[i])
}

function filter(func, seq) {
    var result = []
    if (!func) func = stub
    for(var i = 0; i < seq.length; i++)
        if (func(seq[i])) result.push(seq[i])
    return result
}

function all(func, seq) {
    for(var i = 0; i < seq.length; i++)
        if (!func(seq[i])) return false
    return true
}

function any(func, seq) {
    for(var i = 0; i < seq.length; i++)
        if (func(seq[i])) return true
    return false
}

function sum(seq) {
    var result = (typeof(seq[0]) == 'string') ? '' : 0
    for(var i = 0; i < seq.length; i++)
        result += seq[i]
    return result
}

function reduce(func, initval, seq) {
    var result = initval
    for(var i = 0; i < seq.length; i++)
        result = func(result, seq[i])
    return result
}

function min(seq) {
    return reduce(Math.min, Number.POSITIVE_INFINITY, seq)
}

function max(seq) {
    return reduce(Math.max, Number.NEGATIVE_INFINITY, seq)
}

function zip(/* seq, seq, ...*/) {
    var result = []
    var lists = arguments
    var m =  min(map(operator.itemgetter('length'), lists))
    var n = lists.length
    for(var i = 0; i < m; i++) {
        var elem = []
        for(var j = 0; j < n; j++)
            elem.push(lists[j][i])
        result.push(elem)
    }
    return result
}

function unzip(/* seq, seq, ...*/) {
    return zip(arguments)
}

// TODO uncomment?
//
//function compose() {
//    var functions = arguments
//    return function() {
//        var result = functions.pop()(arguments)
//        while (functions.length > 0)
//            result = functions.pop()(result)
//        return result
//    }
//}
//
//function slice(from, to/* , step */) {
//    var result = []
//    for(var i = 1; i < seq.length; i++)
//        result.push(seq[i])
//    return result
//}

var operator = {}

operator.getattr = function(obj, attr) {
    return obj[attr]
}

operator.itemgetter = function (attr) {
    var bits = attr.split('.')
    if (bits.length == 1)
        return function(obj) { return obj[attr] }
    return function(obj) { return reduce(operator.getattr, obj, bits) }
}

operator.itemchecker = function (attr, value) {
    var getter = operator.itemgetter(attr)
    return function(obj) { return getter(obj) == value }
}

operator.itemsetter = function (attr, value) {
    var bits = attr.split('.')
    if (bits.length == 1)
        return function(obj) { obj[attr] = value }
    else if (bits.length == 2)
        return function(obj) { obj[ bits[0] ][ bits[1] ] = value }
    else if (bits.length == 3)
        return function(obj) { obj[ bits[0] ][ bits[1] ][ bits[2] ] = value }
    else if (bits.length == 4)
        return function(obj) { obj[ bits[0] ][ bits[1] ][ bits[2] ][ bits[3] ] = value }
    else
        throw 'operator.itemsetter doesnt support high deep yet'
}

