      1 ﻿
      2 /**
      3  * @name	CeL quotient function
      4  * @fileoverview
      5  * 本檔案包含了 quotient 的 functions。
      6  * @since	2010/3/11 16:59:59
      7  * @example
      8  * CeL.use('math');	//	TODO: bug
      9  * CeL.use('math.quotient');
     10  * var q1 = new CeL.quotient(2,3);
     11  * //	數字基底的轉換:
     12  * CeL.log(CeL.quotient.parse_base('4877.6'.toLowerCase(),10).to_base(16).replace(/_([^\(]+)/,'_<i style="text-decoration:overline">$1</i>'));
     13  */
     14
     15
     16 /*
     17 TODO:
     18 use {String} value + {Number} exponent
     19
     20 http://www.leemon.com/crypto/BigInt.html
     21 http://www-cs-students.stanford.edu/~tjw/jsbn/
     22 http://java.sun.com/javase/6/docs/api/java/math/BigInteger.html
     23
     24 */
     25
     26 if (typeof CeL === 'function'){
     27
     28 /**
     29  * 本 module 之 name(id)，<span style="text-decoration:line-through;">不設定時會從呼叫時之 path 取得</span>。
     30  * @type	String
     31  * @constant
     32  * @inner
     33  * @ignore
     34  */
     35 var module_name = 'math.quotient';
     36
     37 //===================================================
     38 /**
     39  * 若欲 include 整個 module 時，需囊括之 code。
     40  * @type	Function
     41  * @param	{Function} library_namespace	namespace of library
     42  * @param	load_arguments	呼叫時之 argument(s)
     43  * @return
     44  * @name	CeL.math.quotient
     45  * @constant
     46  * @inner
     47  * @ignore
     48  */
     49 var code_for_including = function(library_namespace, load_arguments) {
     50
     51 //	requires
     52 //library_namespace.use('math');
     53 if (eval(library_namespace.use_function(
     54 		'math.to_rational_number,math.gcd,math.factorization')))
     55 	return;
     56
     57 //library_namespace.debug(to_rational_number);
     58 //library_namespace.debug(gcd);
     59
     60
     61 //============================================================================
     62 //	definition of module quotient
     63
     64 var
     65 /*
     66 整數部分
     67 分數	fraction
     68 真分數	proper fraction
     69 vinculum  = "divide by"
     70 */
     71 /**
     72  * 有理數 rational number，有理数全体のつくる集合はしばしば、商を意味する quotient の頭文字をとり、太字の Q で表す。<br/>
     73  * 若要輸入不同基底的數值，請用 parse_base()
     74  * @param	numerator	分子
     75  * @param	denominator	分母
     76  * @param {Boolean} approximate	取近似值
     77  * @example
     78  * CeL.log((new CeL.quotient(3,4)).count('*',new CeL.quotient(2,7)).reduce().to_print_mode());
     79  * @class	quotient 的 functions
     80  * @constructor
     81  */
     82 CeL.math.quotient
     83 = function(numerator, denominator, approximate) {
     84 	if (typeof numerator === 'object' && numerator instanceof _
     85 			//&& numerator.Class === 'quotient'
     86 			)
     87 		return numerator;
     88 	if (isNaN(numerator))
     89 		numerator = 0;
     90 	if (!denominator || isNaN(denominator))
     91 		denominator = 1;
     92 	else if (denominator < 0)
     93 		denominator = -denominator, numerator = -numerator;
     94
     95 	// to_rational_number 需 test，並回傳(分子,分母,誤差)！
     96 	var q = to_rational_number(numerator);
     97 	//library_namespace.debug(numerator + ' → ' + q);
     98 	if (!q)
     99 		numerator = 0;
    100 	else if (approximate || !q[2])
    101 		// 無誤差時使用之
    102 		numerator = q[0], denominator *= q[1] || 1;
    103 	else
    104 		while (numerator % 1 || denominator % 1)
    105 			// 化為整數
    106 			numerator *= 10, denominator *= 10;
    107
    108 	// value
    109 	this.n = numerator, this.d = denominator;
    110 	// this.type='quotient';
    111 	//library_namespace.debug(this.n + ' / ' + this.d);
    112 	return this;
    113 };
    114
    115 //class public interface	---------------------------
    116
    117 CeL.math.quotient
    118 .
    119 /**
    120  * 循環節分隔符號：整數.小數__repetend_separator__循環節
    121  * @memberOf	CeL.math.quotient
    122  */
    123 repetend_separator = '_';//' '
    124
    125 CeL.math.quotient
    126 .
    127 /**
    128  * 數字集
    129  * @memberOf	CeL.math.quotient
    130  * @see
    131  * <a href="http://en.wikipedia.org/wiki/Numerical_digit" accessdate="2010/4/16 20:47">Numerical digit</a>
    132  */
    133 digit_char = '0123456789abcdefghijklmnopqrstuvwxyz';//.split('')
    134
    135
    136 CeL.math.quotient
    137 .
    138 /**
    139  * 轉換指定進位的數字成為 quotient 物件
    140  * @since	2004/7/9 16:13
    141  * @param number	數字
    142  * @param base	基底
    143  * @param digit_char	循環小數 digit 字集
    144  * @return	回傳 quotient 物件，請用 quotient.to_base() 傳回所欲之 base
    145  * @memberOf	CeL.math.quotient
    146  * @example
    147  * var q=parse_base('10000.'+_.repetend_separator+'3',11);
    148  * if(!q)
    149  * 	alert('bad input!');
    150  * else
    151  * 	library_namespace.debug('<br/>'+q.base(8)+','+q.base()+' , '+q.to_print_mode()+','+q.print(1)+','+q.to_print_mode(2)+','+q.to_print_mode(3,0,'',5));
    152  */
    153 parse_base = function(number, base, digit_char) {
    154 	// if(!num) num = 0;
    155 	if ((!(base = Math.floor(base)) || base < 2) && digit_char)
    156 		base = digit_char.length;
    157
    158 	if (!digit_char)
    159 		digit_char = _.digit_char;
    160 	if (isNaN(base) || base < 2 || base > digit_char.length)
    161 		base = 10;
    162 	if (!number || base === 10
    163 			&& ('' + number).indexOf(_.repetend_separator) === -1)
    164 		// 可能有循環小數，所以不能放過僅僅 base === 10
    165 		return new _(number);
    166
    167 	var i = 0, n = new _(0, 1), m = 0, t = 0, p, c = {}, r = new _(0, 1);
    168 	for (; i < digit_char.length; i++)
    169 		c[digit_char.charAt(i)] = i; // 字集
    170
    171 	number += '', i = -1, n.d = r.d = 1;
    172 	//library_namespace.debug('<br/>'+i+','+num.length+','+t+','+num+','+n.to_print_mode());
    173 	if (number.charAt(0) === '-')
    174 		i = 0, m = 1;
    175 	while (++i < number.length && (p = number.charAt(i)) != '.')
    176 		// 整數
    177 		if (isNaN(p = c[p]) || p >= base)
    178 			// error!
    179 			return;
    180 		else
    181 			t = t * base + p;
    182 	//library_namespace.debug('<br/>'+i+','+num.length+','+t+','+num+','+n.to_print_mode());
    183 	while (++i < number.length
    184 			&& (p = number.charAt(i)) != _.repetend_separator)
    185 		// 小數
    186 		if (isNaN(p = c[p]) || p >= base)
    187 			// error!
    188 			return;
    189 		else
    190 			n.n = n.n * base + p, n.d *= base;
    191 	while (++i < number.length)
    192 		// 循環節
    193 		if (isNaN(p = c[number.charAt(i)]) || p >= base)
    194 			return; // error!
    195 		else
    196 			r.n = r.n * base + p, r.d *= base;
    197 	//library_namespace.debug('<br/>**'+n.to_print_mode());
    198 	//	善後
    199 	n = n.count('+=', t);
    200 	if (r.n)
    201 		r.d = (r.d - 1) * n.d, n.count('+=', r);
    202 	n.reduce();
    203 	//library_namespace.debug('<br/>*'+n.to_print_mode());
    204 	if (m)
    205 		n.n = -n.n;
    206 	return n;
    207 };
    208
    209
    210
    211 CeL.math.quotient
    212 .prototype = {
    213
    214 //	instance public interface	-------------------
    215
    216
    217 /**
    218  * 最簡分數/化簡, 約分 reduction
    219  * @return	化簡後的
    220  * @name	CeL.math.quotient.prototype.reduce
    221  */
    222 reduce : function() {
    223 	var g = gcd(this.n, this.d);
    224 	if (g && g > 1)
    225 		this.n /= g, this.d /= g;
    226 	return this;
    227 },
    228
    229 /**
    230  * 四則運算 + - * / (+-×÷)**[=]
    231  * @param op	operator
    232  * @param q2	the second quotient
    233  * @return	計算後的結果
    234  * @see
    235  * <a href="http://www.javaworld.com.tw/jute/post/view?bid=35&id=30169&tpg=1&ppg=1&sty=1&age=0#30169" accessdate="2010/4/16 20:47">JavaWorld@TW Java論壇 - post.view</a>
    236  * @name	CeL.math.quotient.prototype.count
    237  */
    238 count : function(op, q2) {
    239 	var q;
    240 	if (op.slice(-1) === '=')
    241 		q = this, op = op.slice(0, -1);
    242 	else
    243 		q = new _(this);
    244
    245 	q2 = new _(q2);
    246 	//library_namespace.debug('<br/>'+this.type+','+q.to_print_mode()+' , '+q2.to_print_mode());
    247 	if (op === '-')
    248 		q2.n = -q2.n, op = '+';
    249 	else if (op === '/') {
    250 		var t = q2.n;
    251 		q2.n = q2.d, q2.d = t, op = '*';
    252 	}
    253 	//library_namespace.debug('<br/>'+q.to_print_mode(1)+','+q2.to_print_mode(1));
    254 	if (op === '+')
    255 		q.n = q.n * q2.d + q.d * q2.n, q.d *= q2.d;
    256 	else if (op === '*')
    257 		q.n *= q2.n, q.d *= q2.d;
    258 	// N! 是指 N的階乘 (Factorial,power)
    259 	else if ((op === '**' || op === '^') && q2.reduce().d === 1) {
    260 		q.reduce(), q.n = Math.pow(q.n, q2.n), q.d = Math.pow(q.d, q2.n);
    261 		return q;
    262 	} else {
    263 		library_namespace.err('illegal operator [' + op + ']!');
    264 		return this;
    265 	}
    266 	//library_namespace.debug('<br/>'+q.to_print_mode(1)+','+q2.to_print_mode(1));
    267 	//	other: error
    268 	//library_namespace.debug('<br/>_'+q.reduce().to_print_mode());
    269 	try {
    270 		return q.reduce();
    271 	} catch (e) {
    272 	}
    273 	return q;
    274 },
    275
    276 /**
    277  * 依指定基底轉成循環小數 circulating decimal / repeating decimal。
    278  * 特殊情況可以考慮使用 .toString()，會快很多。
    279  * TODO: 小數
    280  * @since	2004/7/9 13:28
    281  * @param base	基底
    282  * @param digit_char	循環小數 digit 字集
    283  * @return	(decimal/數字部分string,repunitIndex/循環節Repetend出現在)
    284  * @see
    285  * <a href="http://mathworld.wolfram.com/RepeatingDecimal.html" accessdate="2010/4/16 20:47">Repeating Decimal -- from Wolfram MathWorld</a>
    286  * <a href="http://hk.geocities.com/goodprimes/OFrp.htm">循環小數與素數。素之異類。</a>
    287  * @name	CeL.math.quotient.prototype.to_base
    288  */
    289 to_base : function(base, digit_char) {
    290 	//if (!isNaN(digit_char)) digit_char += '';
    291 	if (typeof digit_char !== 'string' || digit_char.length < 2)
    292 		// 字集
    293 		digit_char = _.digit_char;
    294
    295 	// 基底預設為 10 進位
    296 	if (!(base = Math.floor(base)) || base == 10 ||
    297 			// illegal base
    298 			base < 2 || base > digit_char.length)
    299 		return this.to_decimal();
    300
    301 	this.reduce();
    302 	var d = this.d, o = this.n, i, t, m = 0,
    303 	// find base 的因數(factor)
    304 	f = factorization(base);
    305 	if (o < 0)
    306 		// 負數
    307 		o = -o, m = 1;
    308
    309 	// find 分母的因數(factor)與基底 base 之交集(不能用gcd)
    310 	for (i = 0, g = 1, t = d; i < f.length; i += 2)
    311 		while (t % f[i] === 0)
    312 			g *= f[i], t /= f[i];
    313
    314 	// get 整數 integer 部分 → out
    315 	f = o % d;
    316 	i = (o - f) / d;
    317 	o = '';
    318 	while (i)
    319 		t = i % base, i = (i - t) / base, o = digit_char.charAt(t) + o;
    320 	if (!o)
    321 		o = '0', m = 0;
    322 	//library_namespace.debug('<br/>_' + typeof o + ',' + (o || '(null)') + ',' + o);
    323 	if (!f)
    324 		return m ? '-' + o : o;
    325
    326 	// 進入小數
    327 	o += '.';
    328
    329 	// set 餘數f/分母d, 餘數residue mark r=f, 循環節 Repetend location of out:l, 已解決的因數 s
    330 	var r = f, l = o.length, s = 1;
    331
    332 	// do main loop
    333 	// while(o.length-l<d){ // 限制?位: debug用
    334 	for (;;) {
    335 		//library_namespace.debug('<br/>.'+r+','+f+'/'+d+'('+base+'),'+s+':'+g+','+o);
    336 		if (!f) {
    337 			// 可以整除，無循環。
    338 			l = 0;
    339 			break;
    340 		}
    341 		f *= base;
    342 		if (s === g) {
    343 			// 分母與 base 已互質
    344 			t = f, f %= d, o += digit_char.charAt((t - f) / d);
    345 			if (f === r)
    346 				// bingo! 餘數重複出現，循環節結束。
    347 				break;
    348 		} else {
    349 			// f 與 d 已互質
    350 			t = gcd(base, d), s *= t, f /= t, d /= t,
    351 			//	do the same thing
    352 			t = f, f %= d, o += digit_char.charAt((t - f) / d),
    353 			//	r 需重設..此處有否可能出問題? maybe not?
    354 			r = f, l = o.length;
    355 		}
    356 	}
    357
    358 	//	善後
    359 	if (l)
    360 		o += '(' + (o.length - l) + ')', o = o.slice(0, l)
    361 		+ _.repetend_separator + o.substr(l);
    362 	if (m)
    363 		o = '-' + o;
    364 	return o;
    365 },
    366
    367 /**
    368  * 為十進位最佳化的 to_base()<br/>
    369  * 以結論來說，好像快不了多少?
    370  * @since 2004/7/9 13:47
    371  * @return
    372  * @name	CeL.math.quotient.prototype.to_decimal
    373  */
    374 to_decimal : function() {
    375 	this.reduce();
    376 	var d = this.d, t = d, g = 1, m = 0, f, o = this.n;
    377 	if (o < 0)
    378 		o = -o, m = 1; // 負數
    379
    380 	// find 分母的 2,5 因數
    381 	while (t % 2 === 0)
    382 		//	使用下面這行會造成 bug: 輸出 .110011001100110011001100110011001100(2) 會導致 g===t===0, 掛掉
    383 		//	以結論來說，好像快不了多少?
    384 		//g <<= 1, t >>= 1;
    385 		g *= 2, t /= 2;
    386 		while (t % 5 === 0)
    387 			g *= 5, t /= 5;
    388
    389 		// get 整數 integer 部分 → out
    390 		f = o % d, o = (o - f) / d;
    391 		//library_namespace.debug('<br/>_'+typeof o+','+(o||'(null)')+','+o);
    392 		if (!f)
    393 			// 留下+-
    394 			return (m ? '-' : '') + o;
    395
    396 	// 進入小數
    397 	o += '.';
    398
    399 	// set 餘數f/分母d, 餘數 residue mark r=f, 循環節 Repetend location of out:l, 已解決的因數 s
    400 	var r = f, l = o.length, s = 1;
    401
    402 	// do main loop
    403 	// while(o.length-l<d){// 限制?位:debug用
    404 	for (;;) {
    405 		//library_namespace.debug('<br/>.'+r+','+f+'/'+d+','+s+':'+g+','+o);
    406 		if (!f) {
    407 			// 可以整除，無循環。
    408 			l = 0;
    409 			break;
    410 		}
    411 		f *= 10;
    412 		if (s === g) {
    413 			// 分母與 base 已互質
    414 			t = f, f %= d, o += (t - f) / d;
    415 			if (f === r)
    416 				// bingo! 循環節結束
    417 				break;
    418 		} else {
    419 			t = d % 5 === 0 ? d % 2 === 0 ? 10 : 5 : 2, s *= t, f /= t, d /= t,
    420 			// do the same thing
    421 			t = f, f %= d, o += (t - f) / d,
    422 			// r 需重設..此處有否可能出問題? maybe not?
    423 			r = f, l = o.length;
    424 		}
    425 	}
    426
    427 	//	善後
    428 	if (l)
    429 		o += '(' + (o.length - l) + ')',
    430 		o = o.slice(0, l) + _.repetend_separator + o.substr(l);
    431 	if (m)
    432 		o = '-' + o;
    433 	return o;
    434 },
    435
    436
    437 /*
    438 有效位數、小數位數	http://technet.microsoft.com/zh-tw/library/ms190476.aspx
    439 Precision, Scale
    440 有效位數是數字的位數。小數位數是數字中在小數點右側的位數。例如，123.45 這個數字的有效位數是 5，小數位數是 2。
    441 Precision is the number of digits in a number. Scale is the number of digits to the right of the decimal point in a number. For example, the number 123.45 has a precision of 5 and a scale of 2.
    442 */
    443
    444 /**
    445  * 顯示成各種不同模式的數字
    446  * @since	2004/7/9 14:23
    447  * @param mode	顯示模式
    448  * 0	假分數 improper fraction,
    449  * 1	帶分數 mixed number,
    450  * 2	直接除(10進位),
    451  * 3	轉成循環小數,除至小數點下digit位數（非四捨五入！）.
    452  * @param base	基底
    453  * @param sum_char	顯示帶分數時代表整數與真分數之間的分隔。多為' '或'+'或''。
    454  * @param digit	轉成循環小數時，代表循環小數 digit 字集
    455  * @return	{String}	顯示模式數字
    456  * @name	CeL.math.quotient.prototype.to_print_mode
    457  */
    458 to_print_mode : function(mode, base, sum_char, digit) {
    459 	if (mode < 3 || !mode) {
    460 		if (mode === 2)
    461 			return this.n / this.d;
    462 		var p, f;
    463 		if (!mode || this.n < this.d)
    464 			p = this.n + '/' + this.d;
    465 		else
    466 			f = this.n % this.d,
    467 			p = (this.n - f) / this.d
    468 					+ (f ? (sum_char || '+') + f + '/' + this.d : '');
    469 		return p;
    470 	}
    471
    472 	if (mode === 3) {
    473 		var n = this.to_base(base, sum_char);
    474 		if (isNaN(digit))
    475 			return n;
    476 		var f = n.indexOf(_.repetend_separator), b = n.indexOf('.');
    477 		if (f === -1 || !digit)
    478 			return b === -1 ? n :
    479 				digit ? n.slice(0, b + digit + 1) : n.slice(0, b);
    480 		digit += b + 1,
    481 		//	循環節
    482 		b = n.substr(f + 1),
    483 		n = n.slice(0, f);
    484 		while (n.length < digit)
    485 			n += b;
    486 		return n.slice(0, digit);
    487 	}
    488 },
    489
    490 /**
    491  * 測試大小
    492  * @param q2	the second quotient
    493  * @return	{Number}	0:==, <0:<, >0:>
    494  * @name	CeL.math.quotient.prototype.compare_to
    495  */
    496 compare_to : function(q2) {
    497 	q2 = new _(q2);
    498 	return this.n * q2.d - this.d * q2.n;
    499 }
    500
    501 };
    502
    503 //	setup toString function
    504 _.prototype.toString = _.prototype.to_print_mode;
    505
    506 return (
    507 	CeL.math.quotient
    508 );
    509 };
    510
    511 //============================================================================
    512
    513 CeL.setup_module(module_name, code_for_including);
    514
    515 };
    516