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