      1 ﻿
      2 /**
      3  * @name	CeL Hamming function
      4  * @fileoverview
      5  * 本檔案包含了 Hamming Code, 漢明碼的 functions。
      6  * @since	2010/3/21 14:26:08
      7  * @example
      8  * CeL.use('math.Hamming');
      9  * var hc = CeL.Hamming.encode('1100');
     10  */
     11
     12
     13 /*
     14 TODO:
     15 最佳化
     16 calculate generator matrix
     17 SEC-DED ("single error correction, double error detection") 版本（加在最後）
     18 http://www.ee.unb.ca/cgi-bin/tervo/hamming.pl
     19 最小漢明距離3
     20 	http://phpbb.godfat.org/viewtopic.php?t=66&sid=6e34dd040aa98c64c75bfe099008c82a
     21 BCH碼
     22 */
     23
     24 if (typeof CeL === 'function')
     25 CeL.setup_module('data.math.Hamming',
     26 function(library_namespace, load_arguments) {
     27
     28 //	nothing required
     29
     30
     31 var module_name = this.module_name;
     32
     33
     34 //============================================================================
     35 //	definition of module Hamming
     36
     37 var
     38 /**
     39  * Hamming code
     40  * @class	Hamming Code 的 constructor
     41  * @constructor
     42  */
     43 CeL.data.math.Hamming
     44 = function() {
     45 	return this.encode.apply(this, arguments);
     46 };
     47
     48 //class public interface	---------------------------
     49
     50 CeL.data.math.Hamming
     51 .
     52 /**
     53  * 是否左右顛倒。
     54  * default: data[1,2,..] 左至右, reverse: data[..,2,1] 右至左
     55  * @memberOf	CeL.data.math.Hamming
     56  */
     57 reverse = false;
     58
     59
     60 CeL.data.math.Hamming
     61 .
     62 /**
     63  * encode data to Hamming Code.
     64  * @param data	data stream
     65  * @param no_reverse	forced NO reverse
     66  * @return	{String} encoded Hamming Code
     67  * @memberOf	CeL.data.math.Hamming
     68  */
     69 encode = function(data, no_reverse) {
     70 	data = (library_namespace.is_Array(data) ? data.join('') : '' + data)
     71 			.replace(/[ ,]/g, '');
     72 	if (!/^[01]{1,20}$/.test(data))
     73 		return '';
     74 	if (this.reverse && !no_reverse)
     75 		data = data.split('').reverse().join('');
     76 	// library_namespace.debug('encode [' + d + ']',1,module_name + '.encode');
     77 	var g = [ '' ], i_g = 1, bin = 1, i, i_d = 0, l_d = data.length, ch;
     78 	for (;; i_g++) {
     79 		if (i_g === bin)
     80 			// library_namespace.debug('initial [' + g.length + '] as 0', 1, module_name + '.encode'),
     81 			g.push(0), bin *= 2;
     82 		else {
     83 			// library_namespace.debug('initial [' + g.length + '] as ' + d.charAt(i_d) + ' (' + i_d + '/' + l_d + ')', 1, module_name + '.encode');
     84 			g.push(ch = data.charAt(i_d));
     85 			for (i = 1; i < bin; i *= 2)
     86 				if (i_g & i)
     87 					g[i] ^= ch;
     88 			if (++i_d === l_d)
     89 				break;
     90 		}
     91 	}
     92
     93
     94 	if (library_namespace.is_debug()) {
     95 		//	for debug print
     96 		for (bin = i = 1, i_d = []; i < g.length; i++) {
     97 			ch = g[i];
     98 			if (i === bin)
     99 				ch = '<span style="color:#292;">' + ch + '</span>', bin *= 2;
    100 			//	i_d 在這表示一個專門用於顯示的 array
    101 			i_d.push(ch);
    102 		}
    103 		library_namespace.log('Hamming code of [' + data + ']: ['
    104 				+ i_d.join('') + ']');
    105 	}
    106
    107 	if (this.reverse && !no_reverse)
    108 		g = g.reverse();
    109 	return g.join('');
    110 };
    111
    112
    113 CeL.data.math.Hamming
    114 .
    115 /**
    116  * 將 Hamming Code 分成 data & check bits
    117  * @param code	Hamming Code to split
    118  * @return	[資料位元 data bits, 檢查位元 check bits (parity bits)]
    119  * @memberOf	CeL.data.math.Hamming
    120  */
    121 split_code = function(code) {
    122 	if (library_namespace.is_Array(code))
    123 		code = code.join('');
    124 	code = (' ' + code).split('');
    125 	library_namespace.debug('split [' + code + ']', 1, module_name + '.split_code');
    126 	var i = 1, l = code.length, cb = [];
    127 	while (i < l)
    128 		cb.push(code[i]), code[i] = '', i *= 2;
    129 	library_namespace.debug('→ data [' + code.join('').replace(/ +/g, '') + '], check bits  [' + cb + ']', 1, module_name + '.split_code');
    130 	return [ code.join('').replace(/ +/g, ''), cb ];
    131 };
    132
    133 CeL.data.math.Hamming
    134 .
    135 /**
    136  * decode Hamming Code to data
    137  * @param code
    138  * @return
    139  * @memberOf	CeL.data.math.Hamming
    140  */
    141 decode = function(code) {
    142 	if (!code || !/^[01]{3,30}$/.test(code = ('' + code).replace(/[ ,]/g, '')))
    143 		return '';
    144 	code = code.split('');
    145 	if (this.reverse)
    146 		code = code.reverse();
    147
    148 	var i = 0, l = code.length, ch, bin = 1, split_c = this.split_code(code), test_c, cb;
    149
    150 	if (!split_c)
    151 		return;
    152 	//	check bits (parity bits)
    153 	cb = split_c[1];
    154 	test_c = this.encode(split_c[0], 1);
    155 	if (!test_c || !(test_c = this.split_code(test_c)))
    156 		return;
    157
    158 	library_namespace.debug('received check bits: [' + cb.join('') + '], calculated: [' + test_c[1].join('') + ']', 1, module_name + '.decode');
    159 	test_c = parseInt(test_c[1].reverse().join(''), 2)
    160 			^ parseInt(cb.reverse().join(''), 2);
    161 	library_namespace.debug('error bit' + (this.reverse ? ' (do reversed XOR)' : '') + ': ' + test_c + '(' + test_c.toString(2) + '), ' + code.join(''), 1, module_name + '.decode');
    162 	if (test_c)
    163 		if (test_c < code.length)
    164 			code[test_c - 1] ^= 1, split_c = this.split_code(code);
    165 		else
    166 			//	這算是能檢測出 2 bits 以上錯誤的特殊例子，機率通常也不大：已經超過 index 了。
    167 			//	e.g., reversed 011010001100
    168 			library_namespace.debug('<em>Out of index!</em> More than 2 errors occurred.', 1, module_name + '.decode');
    169
    170 	if (library_namespace.is_debug())
    171 		if (test_c) {
    172 			cb = code.join('\0').split('\0');
    173 			cb[test_c - 1] = '<span style="color:#f33;">' + cb[test_c - 1] + '</span>';
    174 			library_namespace.debug('→ ' + cb.join(''), 1, module_name + '.decode');
    175 		} else
    176 			library_namespace.debug('The Hamming code is correct.', 1, module_name + '.decode');
    177
    178 	split_c = split_c[0];
    179 	if (this.reverse)
    180 		split_c = split_c.split('').reverse().join('');
    181 	return split_c;
    182 };
    183
    184
    185 CeL.data.math.Hamming
    186 .
    187 /**
    188  * 顯示 Hamming Code 的計算方法
    189  * @param {Number} bit_length	bit length. e.g., 8, 16.
    190  * @memberOf	CeL.data.math.Hamming
    191  */
    192 show = function(bit_length) {
    193 	var code = [], bit = [], parity = [], i = 1, j, k, d = 1, a = 1, cc = 1, dc = 1;
    194 	for (;; i++) {
    195 		bit[i] = i.toString(2);
    196 		if (i === a)
    197 			code[i] = 'C' + Math.pow(2, cc++ - 1), a *= 2;
    198 		else {
    199 			code[i] = 'D' + dc++;
    200 			for (j = 1, k = 1; j < cc; j++, k *= 2)
    201 				if (i & k)
    202 					if (parity[j])
    203 						parity[j] += '<span style="color:#aaa;">⊕</span>' + code[i];
    204 					else
    205 						parity[j] = code[i];
    206 			if (dc > bit_length)
    207 				break;
    208 		}
    209 	}
    210 	for (i = 1; i < code.length; i++) {
    211 		a = code[i];
    212 		if (j = a.match(/^C(\d+)$/))
    213 			a += ' = ' + parity[Math.round(Math.log(j[1]) * Math.LOG2E) + 1];
    214 		library_namespace.debug(bit[i] + ': ' + a);
    215 	}
    216
    217 };
    218
    219
    220 /**
    221  * 不 extend 的 member
    222  * @ignore
    223  */
    224 CeL.data.math.Hamming
    225 .no_extend = '*';
    226
    227
    228
    229
    230 return (
    231 	CeL.data.math.Hamming
    232 );
    233 }
    234
    235
    236 );
    237
    238