var checksum = new Class.create();

checksum.prototype = {
	
	initialize: function(type) {
		this.type = type;
		/**
		 * Verhoeff checksum
		 */
		// j down, k across
		this._vD = [
				[0,1,2,3,4,5,6,7,8,9],
				[1,2,3,4,0,6,7,8,9,5],
				[2,3,4,0,1,7,8,9,5,6],
				[3,4,0,1,2,8,9,5,6,7],
				[4,0,1,2,3,9,5,6,7,8],
				[5,9,8,7,6,0,4,3,2,1],
				[6,5,9,8,7,1,0,4,3,2],
				[7,6,5,9,8,2,1,0,4,3],
				[8,7,6,5,9,3,2,1,0,4],
				[9,8,7,6,5,4,3,2,1,0]
			];
		// pos%8 down, num across
		this._vP = [
				[0,1,2,3,4,5,6,7,8,9],
				[1,5,7,6,2,8,3,0,9,4],
				[5,8,0,3,7,9,6,1,4,2],
				[8,9,1,6,0,4,3,5,2,7],
				[9,4,5,3,1,2,6,8,7,0],
				[4,2,8,6,5,7,3,9,0,1],
				[2,7,9,3,8,0,6,4,1,5],
				[7,0,4,6,9,1,3,2,5,8]
			];
		// j down
		this._vInv = [0,4,3,2,1,5,6,7,8,9];
		
		/**
		 * cpr number
		 */
		this._C =		[4,3,2,7,6,5,4,3,2,1];
		this._Cvr =	[2,7,6,5,4,3,2,1];
	},
	
	checksum: function(number) {
		switch(this.type) {
			case 'verhoeff':
			case 'dihedral':
				return this._verhoeffChecksum(number);
			break;
			case 'luhn':
			case 'dankort':
			case 'mod10':
				return this._luhnChecksum(number);
			break;
			case 'mod11_10':
				return this._mod11_10Checksum(number);
			break;
			case 'mod11_2':
				return this._mod11_2Checksum(number);
			break;
			case 'cpr':
				return this._cprChecksum(number);
			break;
			case 'cvr':
				return this._cvrChecksum(number);
			break;
		}
	},
	
	calcsum: function(number) {
		switch(this.type) {
			case 'verhoeff':
			case 'dihedral':
				return this._verhoeffCalcChecksum(number);
			break;
			case 'luhn':
			case 'dankort':
			case 'mod10':
				return this._luhnCalcChecksum(number);
			break;
			case 'mod11_10': // broken
				return this._mod11_10CalcChecksum(number);
			break;
			case 'mod11_2':
				return this._mod11_2CalcChecksum(number);
			break;
			case 'cpr':
				return this._cprCalcChecksum(number);
			break;
			case 'cvr':
				return this._cvrCalcChecksum(number);
			break;
		}
	},
	
	_cprChecksum: function(number) {
		var n =String(number).toArray(), sum = 0;
		for (var i = 0, l = n.length; i < l; i++) {
			sum += Number(n[i])*this._C[i];
		}
		return sum%11 == 0;
	},
	
	_cprCalcsum: function(number) {
		var n =String(number).toArray(), sum = 0;
		for (var i = 0, l = n.length; i < l; i++) {
			sum += Number(n[i])*this._C[i];
		}
		var mod = sum%11;
		mod = mod == 0 ? 0 : 11-mod;
		return String(number)+String(mod);
	},
	
	_cvrChecksum: function(number) {
		var n =String(number).toArray(), sum = 0;
		for (var i = 0, l = n.length; i < l; i++) {
			sum += Number(n[i])*this._Cvr[i];
		}
		return sum%11 == 0;
	},
	
	_cvrCalcsum: function(number) {
		var n =String(number).toArray(), sum = 0;
		for (var i = 0, l = n.length; i < l; i++) {
			sum += Number(n[i])*this._Cvr[i];
		}
		var mod = sum%11;
		mod = mod == 0 ? 0 : 11-mod;
		return String(number)+String(mod);
	},
	
	_mod11_2CalcChecksum: function(number) {
		var c = this._mod11_2Getsum(number);
		return String(number) + String(c == 10 ? 'X' : c);
	},
	
	_mod11_2Checksum: function(number) {
		return this._mod11_2Getsum(number.substring(0,number.length-1 )) == this._mod11_2GetCheck(number);
	},
	
	_mod11_2GetCheck: function(number) {
		var n = String(number).toArray().last();
		return n == 'X' ? 10 : n;
	},
	
	_mod11_2Getsum: function(number) {
		var n = String(number).toArray(), c = 0;
		for (var i = 0, l = n.length; i < l; i++) {
			c = 2*(c+Number(n[i]));
		}
		c %= 11;
		return (12-c)%11;
	},
	
	_mod11_10CalcChecksum: function(number) {
		var n = String(number).toArray(), c = 10;
		for (var i = 0, l = n.length; i < l; i++) {
			n[i] = Number(n[i]);
			c = (2* this._mod11_10val(n[i]+c))%11;
		}
		var check = (11-c)%10;
		return String(number)+String(check);
	},
	
	_mod11_10Checksum: function(number) {
		var n = String(number).toArray(), c = 10;
		for (var i = 0, l = n.length-1 ; i < l; i++) {
			n[i] = Number(n[i]);
			c = (2* this._mod11_10val(n[i]+c))%11;
		}
		return (c+n.last()%10)==1;
	},
	
	_mod11_10val: function(n) {
		var val = n%10;
		return val == 0 ? 10 : val;
	},
	
	_verhoeffCalcChecksum: function(number) {
		var n = String(number).toArray().reverse(), c = 0;
		for (var i = 0, l = n.length; i < l; i++)
			c = this._vD[c][this._vP[(i+1 )%8][n[i]]];
		return String(number)+String(this._vInv[c]);
	},
	
	_verhoeffChecksum: function(number) {
		var n = String(number).toArray().reverse(), c = 0;
		for (var i = 0, l = n.length; i < l; i++)
			c = this._vD[c][this._vP[i%8][Number(n[i])]];
		return c==0;
	},
	
	_luhnCalcChecksum: function(number) {
		var n = String(number).toArray();
		var mod = this._luhnGetsum(n);
		var c = mod == 0 ? 0 : 10-mod;
		return String(number)+String(c);
	},
	
	_luhnChecksum: function(number) {
		var n = String(number).toArray().reverse();
		return this._luhnGetsum(n) == 0;
	},
	
	_luhnGetsum: function(n) {
		var sum = 0;
		for (var i = 0, l = n.length; i < l; i++) {
			n[i] = Number(n[i]);
			if (i%2) {
				n[i] *= 2;
				if (n[i] > 9) n[i] -= 9;
			}
			sum += n[i];
		}
		return sum%10;
	}
	
}