var INF = Infinity;
if (typeof Array.prototype.add == "undefined") {
	Array.prototype.add = function(a) {
		var b = [];
		for (var i = 0, l = Math.min(this.length, a.length); i < l; i++) {
			b[i] = this[i] + a[i];
		}
		return b;
	}
}
if (typeof Array.prototype.sub == "undefined") {
	Array.prototype.sub = function(a) {
		var b = [];
		for (var i = 0, l = Math.min(this.length, a.length); i < l; i++) {
			b[i] = this[i] - a[i];
		}
		return b;
	}
}
if (typeof Array.prototype.mul == "undefined") {
	Array.prototype.mul = function(m) {
		var b = [];
		for (var i = 0, l = this.length; i < l; i++) {
			b[i] = this[i] * m;
		}
		return b;
	}
}
if (typeof Array.prototype.affine == "undefined") {
	Array.prototype.affine = function(k, b) {
		var a = [];
		var _k = k, _b = b;
		for (var i = 0, l = this.length; i < l; i++) {
			if (typeof k == "object") _k = k[i];
			if (typeof b == "object") _b = b[i];
			a[i] = this[i] * _k + _b;
		}
		return a;
	}
}
if (typeof Array.prototype.scalar == "undefined") {
	Array.prototype.scalar = function(a) {
		var sum = 0;
		for (var i = 0, l = Math.min(this.length, a.length); i < l; i++) {
			sum += this[i] * a[i];
		}
		return sum;
	}
}
if (typeof Array.prototype.sum == "undefined") {
	Array.prototype.sum = function() {
		var sum = 0;
		for (var i = this.length-1; i >= 0; i--) {
			sum += this[i];
		}
		return sum;
	}
}

/**	
 *	from mozilla's dev center
 *	@url https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/Array/Reduce
 */
if (!Array.prototype.reduce) {
	Array.prototype.reduce = function(fun /*, initial*/) {
		var len = this.length >>> 0;
		if (typeof fun != "function")
			throw new TypeError();
		// no value to return if no initial value and an empty array
		if (len == 0 && arguments.length == 1)
			throw new TypeError();
		var i = 0;
		if (arguments.length >= 2) {
			var rv = arguments[1];
		} else {
			do {
				if (i in this) {
					var rv = this[i++];
					break;
				}
				// if array contains no values, no initial value to return
				if (++i >= len)
					throw new TypeError();
			} while (true);
		}
		for (; i < len; i++) {
			if (i in this)
				rv = fun.call(null, rv, this[i], i, this);
		}
		return rv;
	};
}

if (typeof Array.prototype.sortBy == "undefined") {
	Array.prototype.sortBy = function(fieldName, sign) {
		if (!sign) sign = 1;
		return this.sort(function(a, b){
			return sign * (a[fieldName] - b[fieldName]);
		});
	}
}
if (typeof Array.prototype.findBy == "undefined") {
	Array.prototype.findBy = function(obj) {
		for (var i = this.length-1; i >= 0; i--) {
			var equals = true;
			for (p in obj) {
				if ((this[i][p] != "undefined")
				&&(this[i][p] != obj[p])) {
					equals = false;
				}
			}
			if (equals) return this[i];
		}		
	}
}

var $clone = function(obj) {
/*	if (typeof obj != "object") return obj;
	if (typeof obj.length != "undefined") return obj.clone(); // array */
	var o = {};
	for (p in obj) {
		o[p] = obj[p];
	}
	return o;
}

var $$clone = function(obj, level, options) {
	var new_entities = [];
	var old_entities = [];
	var _clone = function(obj, lvl) {
		if (typeof lvl == "undefined") lvl = Infinity;
		if (lvl == 0) return obj;
		if (typeof obj.clone == "function") return obj.clone();
		if (typeof obj == "object") return _clone(obj);
		if (typeof obj.length != "undefined") return obj.extend([]);
//		if (typeof obj == "function") return obj;
		return obj;
		var o = {};
		for (p in obj) {
			if (!options.match || p.match(options.match)) {
				o[p] = _clone(obj[p], lvl-1);
			}
		}
		return o;
	}
	return _clone(obj, level);
}