      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
     26 /**
     27  * 本 module 之 name(id)，<span style="text-decoration:line-through;">不設定時會從呼叫時之 path 取得</span>。
     28  * @type	String
     29  * @constant
     30  * @inner
     31  * @ignore
     32  */
     33 var module_name = 'math.Hamming';
     34
     35 //===================================================
     36 /**
     37  * 若欲 include 整個 module 時，需囊括之 code。
     38  * @type	Function
     39  * @param	{Function} library_namespace	namespace of library
     40  * @param	load_arguments	呼叫時之 argument(s)
     41  * @return
     42  * @name	CeL.math.Hamming
     43  * @constant
     44  * @inner
     45  * @ignore
     46  */
     47 var code_for_including = function(library_namespace, load_arguments) {
     48
     49
     50
     51 //============================================================================
     52 //	definition of module Hamming
     53
     54 var
     55 /**
     56  * Hamming code
     57  * @class	Hamming Code 的 constructor
     58  * @constructor
     59  */
     60 CeL.math.Hamming
     61 = function() {
     62 	return this.encode.apply(this, arguments);
     63 };
     64
     65 //class public interface	---------------------------
     66
     67 CeL.math.Hamming
     68 .
     69 /**
     70  * 是否左右顛倒。
     71  * default: data[1,2,..] 左至右, reverse: data[..,2,1] 右至左
     72  * @memberOf	CeL.math.Hamming
     73  */
     74 reverse = false;
     75
     76
     77 CeL.math.Hamming
     78 .
     79 /**
     80  * encode data to Hamming Code.
     81  * @param data	data stream
     82  * @param no_reverse	forced NO reverse
     83  * @return	{String} encoded Hamming Code
     84  * @memberOf	CeL.math.Hamming
     85  */
     86 encode = function(data, no_reverse) {
     87 	data = (library_namespace.is_Array(data) ? data.join('') : '' + data)
     88 			.replace(/[ ,]/g, '');
     89 	if (!/^[01]{1,20}$/.test(data))
     90 		return '';
     91 	if (this.reverse && !no_reverse)
     92 		data = data.split('').reverse().join('');
     93 	// library_namespace.debug('encode [' + d + ']',1,module_name + '.encode');
     94 	var g = [ '' ], i_g = 1, bin = 1, i, i_d = 0, l_d = data.length, ch;
     95 	for (;; i_g++) {
     96 		if (i_g === bin)
     97 			// library_namespace.debug('initial [' + g.length + '] as 0',1,module_name + '.encode'),
     98 			g.push(0), bin *= 2;
     99 		else {
    100 			// library_namespace.debug('initial [' + g.length + '] as ' + d.charAt(i_d) + ' (' + i_d + '/' + l_d + ')',1,module_name + '.encode');
    101 			g.push(ch = data.charAt(i_d));
    102 			for (i = 1; i < bin; i *= 2)
    103 				if (i_g & i)
    104 					g[i] ^= ch;
    105 			if (++i_d === l_d)
    106 				break;
    107 		}
    108 	}
    109
    110
    111 	if (library_namespace.is_debug()) {
    112 		//	for debug print
    113 		for (bin = i = 1, i_d = []; i < g.length; i++) {
    114 			ch = g[i];
    115 			if (i === bin)
    116 				ch = '<span style="color:#292;">' + ch + '</span>', bin *= 2;
    117 			//	i_d 在這表示一個專門用於顯示的 array
    118 			i_d.push(ch);
    119 		}
    120 		library_namespace.log('Hamming code of [' + data + ']: ['
    121 				+ i_d.join('') + ']');
    122 	}
    123
    124 	if (this.reverse && !no_reverse)
    125 		g = g.reverse();
    126 	return g.join('');
    127 };
    128
    129
    130 CeL.math.Hamming
    131 .
    132 /**
    133  * 將 Hamming Code 分成 data & check bits
    134  * @param code	Hamming Code to split
    135  * @return	[資料位元 data bits, 檢查位元 check bits (parity bits)]
    136  * @memberOf	CeL.math.Hamming
    137  */
    138 split_code = function(code) {
    139 	if (library_namespace.is_Array(code))
    140 		code = code.join('');
    141 	code = (' ' + code).split('');
    142 	library_namespace.debug('split [' + code + ']', 1, module_name + '.split_code');
    143 	var i = 1, l = code.length, cb = [];
    144 	while (i < l)
    145 		cb.push(code[i]), code[i] = '', i *= 2;
    146 	library_namespace.debug('→ data [' + code.join('').replace(/ +/g, '') + '], check bits  [' + cb + ']', 1, module_name + '.split_code');
    147 	return [ code.join('').replace(/ +/g, ''), cb ];
    148 };
    149
    150 CeL.math.Hamming
    151 .
    152 /**
    153  * decode Hamming Code to data
    154  * @param code
    155  * @return
    156  * @memberOf	CeL.math.Hamming
    157  */
    158 decode = function(code) {
    159 	if (!code || !/^[01]{3,30}$/.test(code = ('' + code).replace(/[ ,]/g, '')))
    160 		return '';
    161 	code = code.split('');
    162 	if (this.reverse)
    163 		code = code.reverse();
    164
    165 	var i = 0, l = code.length, ch, bin = 1, split_c = this.split_code(code), test_c, cb;
    166
    167 	if (!split_c)
    168 		return;
    169 	//	check bits (parity bits)
    170 	cb = split_c[1];
    171 	test_c = this.encode(split_c[0], 1);
    172 	if (!test_c || !(test_c = this.split_code(test_c)))
    173 		return;
    174
    175 	library_namespace.debug('received check bits: [' + cb.join('') + '], calculated: [' + test_c[1].join('') + ']', 1, module_name + '.decode');
    176 	test_c = parseInt(test_c[1].reverse().join(''), 2)
    177 			^ parseInt(cb.reverse().join(''), 2);
    178 	library_namespace.debug('error bit' + (this.reverse ? ' (do reversed XOR)' : '') + ': ' + test_c + '(' + test_c.toString(2) + '), ' + code.join(''), 1, module_name + '.decode');
    179 	if (test_c)
    180 		if (test_c < code.length)
    181 			code[test_c - 1] ^= 1, split_c = this.split_code(code);
    182 		else
    183 			//	這算是能檢測出 2 bits 以上錯誤的特殊例子，機率通常也不大：已經超過 index 了。
    184 			//	e.g., reversed 011010001100
    185 			library_namespace.debug('<em>Out of index!</em> More than 2 errors occurred.', 1, module_name + '.decode');
    186
    187 	if (library_namespace.is_debug())
    188 		if (test_c) {
    189 			cb = code.join('\0').split('\0');
    190 			cb[test_c - 1] = '<span style="color:#f33;">' + cb[test_c - 1] + '</span>';
    191 			library_namespace.debug('→ ' + cb.join(''), 1, module_name + '.decode');
    192 		} else
    193 			library_namespace.debug('The Hamming code is correct.', 1, module_name + '.decode');
    194
    195 	split_c = split_c[0];
    196 	if (this.reverse)
    197 		split_c = split_c.split('').reverse().join('');
    198 	return split_c;
    199 };
    200
    201
    202 CeL.math.Hamming
    203 .
    204 /**
    205  * 顯示 Hamming Code 的計算方法
    206  * @param {Number} bit_length	bit length. e.g., 8, 16.
    207  * @memberOf	CeL.math.Hamming
    208  */
    209 show = function(bit_length) {
    210 	var code = [], bit = [], parity = [], i = 1, j, k, d = 1, a = 1, cc = 1, dc = 1;
    211 	for (;; i++) {
    212 		bit[i] = i.toString(2);
    213 		if (i === a)
    214 			code[i] = 'C' + Math.pow(2, cc++ - 1), a *= 2;
    215 		else {
    216 			code[i] = 'D' + dc++;
    217 			for (j = 1, k = 1; j < cc; j++, k *= 2)
    218 				if (i & k)
    219 					if (parity[j])
    220 						parity[j] += '<span style="color:#aaa;">⊕</span>' + code[i];
    221 					else
    222 						parity[j] = code[i];
    223 			if (dc > bit_length)
    224 				break;
    225 		}
    226 	}
    227 	for (i = 1; i < code.length; i++) {
    228 		a = code[i];
    229 		if (j = a.match(/^C(\d+)$/))
    230 			a += ' = ' + parity[Math.round(Math.log(j[1]) * Math.LOG2E) + 1];
    231 		library_namespace.debug(bit[i] + ': ' + a);
    232 	}
    233
    234 };
    235
    236
    237 /**
    238  * 不 extend 的 member
    239  * @ignore
    240  */
    241 CeL.math.Hamming
    242 .no_extend = '*';
    243
    244
    245 return (
    246 	CeL.math.Hamming
    247 );
    248 };
    249
    250 //============================================================================
    251
    252 CeL.setup_module(module_name, code_for_including);
    253
    254 };
    255