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