UNPKG

cejs

Version:

A JavaScript module framework that is simple to use.

1,588 lines (1,369 loc) 117 kB
/** * @name CeL integer function * @fileoverview * 本檔案包含了整數 (integer) 暨小數的 functions,相當/類似於 BigInteger, bigint, Large number。<br /> * 在純 javascript 的環境下,藉由原生計算功能,盡可能提供高效的大數計算。<br /> * integer 大數基本上即為 Integer.BASE 進位制之數字系統(Positional notation or place-value notation)。 * * @example * <code> * CeL.run('data.math.integer'); * var integer = new CeL.integer('654561264556287547824234523'); * CeL.log(integer.add('096527893048039647894')); * </code> * * @since 2013/9/8 13:42:58 * @see * https://zh.wikipedia.org/wiki/%E9%80%B2%E4%BD%8D%E5%88%B6 */ /* TODO: 在 ECMAScript 6 之後,或可以繼承重寫。 為了減輕負擔,有些屬性可以不放在 Integer.prototype。 http://reference.wolfram.com/mathematica/tutorial/SomeNotesOnInternalImplementation.html http://gmplib.org/ http://www.craig-wood.com/nick/articles/pi-chudnovsky/ Arbitrary-precision arithmetic / 高精度計算 https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic http://msdn.microsoft.com/zh-tw/library/system.numerics.biginteger.aspx http://docs.oracle.com/javase/7/docs/api/java/math/BigInteger.html https://github.com/silentmatt/javascript-biginteger https://github.com/peterolson/BigInteger.js https://github.com/peterolson/BigRational.js https://github.com/cwacek/bigint-node/blob/master/lib/bigint.js http://www.leemon.com/crypto/BigInt.html http://www-cs-students.stanford.edu/~tjw/jsbn/ http://java.sun.com/javase/6/docs/api/java/math/BigInteger.html 規格書: integer = new Integer(number, do not set fraction = false, base = default base); integer = new Integer(number String, base of String, base = default base); integer = new Integer(Integer, (ignored), base = default base); // digit Array integer[{integer}digit index] = the digit of base ^ (index + exponent) integer[KEY_NEGATIVE] = {Undefined|Boolean}this integer is negative integer[KEY_BASE] = {natural number}base of this integer integer[KEY_EXPONENT] = {integer}exponent of this integer integer[KEY_CACHE] = {Undefined|Array}cache String of value integer[KEY_CACHE][base] = {String}value in base integer[KEY_TYPE] = {Undefined|Number}NaN / Infinity integer[KEY_FACTORS] = {Undefined|Array}factors / 因數 integer[KEY_FACTORS].sort(function(a,b){var na=Array.isArray(a),nb=Array.isArray(b);return na^nb?na^0:na&&nb?a.length-b.length||a[a.length-1]-b[b.length-1]:a-b;}); */ if (typeof CeL === 'function') CeL.run( { name: 'data.math.integer', require: 'data.code.compatibility.|data.native.|data.math.GCD', no_extend: 'random,compare', code: function (library_namespace) { 'use strict'; // requiring var GCD = this.r('GCD'); // ---------------------------------------------------------------------// // basic constants. 定義基本常數。 var // assert: isNaN(KEY_*) // {safe integer} MIN_BASE <= instance[KEY_BASE] <= MAX_BASE // instance[KEY_BASE] 基數/底數初始設定完後,除非歸零,否則不可再改變! KEY_BASE = 'base', // sign. true: *this* is negative, false/undefined: positive. KEY_NEGATIVE = 'negative', //{integer|Undefined}[exponent] 輸入數值標記之科學記數法指數 in instance[KEY_BASE]。default 0. KEY_EXPONENT = 'exponent', //僅為大數整數分解(因數分解, integer factorization)存在。 // this[KEY_FACTORS] = [ {safe integer}scalar純量, Integer, ..] KEY_FACTORS = 'factors', // instance[KEY_CACHE][base] = string in base; KEY_CACHE = 'cache', //instance[KEY_TYPE] = NaN / Infinity; unset: instance is normal number. // 指示/存儲特殊值。 ** instance[\d] 本身僅存儲純數字。 KEY_TYPE = 'type', // 本 library 所允許之最大可安全計算整數。MAX_SAFE_INTEGER <= Number.MAX_SAFE_INTEGER。 MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER, // see for_each_digit() //之後再作初始化。 // assert: 1 < MIN_BASE <= MAX_BASE MIN_BASE = 0, // assert: MAX_BASE * MAX_BASE < MAX_SAFE_INTEGER + 2 // see change_base_to(), for_each_digit() // 為方便乘法處理,因此取自乘不致 overflow ( > MAX_SAFE_INTEGER) 之值,預防 overflow 用。 MAX_BASE = Math.floor(Math.sqrt(MAX_SAFE_INTEGER)), // 可辨認之數字字串。 // [ full , sign, integer part 整數部分, fractional part 小數部分, exponent 指數, percent ] PATTERN_NUMBER = /([+\-]?)([\d]*)(?:\.([\d]+))?(?:[eE]([+\-]?\d+))?\s*([%‰‱])?/, // hex 用。 // [ full , sign, integer part 整數部分, fractional part 小數部分, exponent 指數, percent ] PATTERN_NUMBER_HEX = /([+\-]?)([\da-z]*)(?:\.([\da-z]+))?(?:[eE]([+\-]?\d+))?\s*([%‰‱])?/, // https://en.wikipedia.org/wiki/Parts-per_notation exponent_parts_per = { // percentage (%), 百分比, %(全形百分號) '%' : -2, // permille (‰), 千分率 '‰' : -3, // permyriad (‱) (Basis point), 萬分率 '‱' : -4 // ppm (parts-per-million, 10–6), ppb (parts-per-billion, 10–9), ppt (parts-per-trillion, 10–12) and ppq (parts-per-quadrillion, 10-15). }, //當展現十進位數字時,若非無窮小數,則最多可展現之位數。 MAX_TRUNCATED = get_precision(1 / 3) - 1, DECIMAL_BASE_LENGTH = Math.log10(MAX_SAFE_INTEGER) >> 1, DECIMAL_BASE = (1 + '0'.repeat(DECIMAL_BASE_LENGTH)) | 0, // default base. DEFAULT_BASE = DECIMAL_BASE, MULTIPLICATION_BOUNDARY = multiplication_boundary(DEFAULT_BASE), /** * parseInt( , radix) 可處理之最大 radix,<br /> * 與 Number.prototype.toString ( [ radix ] )<br /> * 可用之最大基數 (radix, base)。<br /> * 10 Arabic numerals + 26 Latin alphabet.<br /> * 之後再作初始化。 * * @inner * @see * <a href="https://en.wikipedia.org/wiki/Hexadecimal" accessdate="2013/9/8 17:26">Hexadecimal</a> */ MAX_RADIX = 0, // 之後再作初始化。 MIN_RADIX = 0, // 應與 parseInt() 一致。 DEFAULT_RADIX = parseInt('10'), HEX_RADIX = parseInt('0x10'), PATTERN_HEX = new RegExp('^0x([0-9a-f]{' + Number.MAX_SAFE_INTEGER.toString(HEX_RADIX).length + ',})$', 'i'), // 數字過大,parseInt() 無法獲得精密數值時使用 DEFAULT_DIGITS。不分大小寫。應與 parseInt() 一致。 // assert: DEFAULT_DIGITS.length === MAX_RADIX // assert: DEFAULT_DIGITS.toLowerCase() === DEFAULT_DIGITS DEFAULT_DIGITS = '', DEFAULT_DIGITS_CACHE, // copy from data.math MULTIPLICATIVE_IDENTITY = library_namespace.MULTIPLICATIVE_IDENTITY, // copy from data.math ZERO_EXPONENT = library_namespace.ZERO_EXPONENT, // copy from data.math ABSORBING_ELEMENT = library_namespace.ABSORBING_ELEMENT, trim_0, // radix point / radix character / decimal mark 小數點 radix_point = '.', // Array 或 Uint32Array。 array_type = Array, // array_clone(from, to[, assignment]): 在不改變 to 之 reference 下,將 to 之陣列內容改為與 from 相同。 array_clone, //reset digits of (this) array_reset, // shift_digits; // ---------------------------------------------------------------------// /** * front end of operation(運算) * @param {String}operator operator * @param number the second integer * @return 計算後的結果 * @see * https://en.wikipedia.org/wiki/Operation_(mathematics) * <a href="http://www.javaworld.com.tw/jute/post/view?bid=35&amp;id=30169&amp;tpg=1&amp;ppg=1&amp;sty=1&amp;age=0#30169" accessdate="2010/4/16 20:47">JavaWorld@TW Java論壇 - post.view</a> * @_name _module_.prototype.op */ function operate(OP_SET, target, operator, operand, flag) { if (operator.slice(-1) === '=') { if (operator === '===') return target === operand; if (operator !== '=' && operator !== '==') operator = operator.slice(0, -1); // 避免 target +-×÷ target if (target === operand) target = target.clone(); } else target = target.clone(); if (operator in OP_SET) OP_SET[operator].call(target, operand, flag); else library_namespace.error('operate: Invalid operator [' + operator + ']!'); return target; } function set_operate(OP_SET) { return function (operator, operand, flag) { return operate(OP_SET, this, operator, operand, flag); }; } // ---------------------------------------------------------------------// // 初始調整並規範基本常數。 /** * 工具函數:轉換 ['a','b','c'] → {a:0,b:1,c:2}。 * * @param {Array}[base] 輸入數值採用之進位制基底/數字 digit 字集。 * * @return 回傳 cache 物件。 * * @inner */ function digit_cache(base) { var digits = Object.create(null); base.forEach(function (digit, index) { if (digit.length !== 1) library_namespace.error('digit_cache: Invalid digit: [' + digit + '].'); else if (digit in digits) library_namespace.error('Digit already exists: [' + digit + '] = ' + digits[digit]); else digits[digit] = index; }); return digits; } // 工具函數 //truncation: truncate array to length function Array_reset(array, to_length) { // 或可參考: // http://stackoverflow.com/questions/1232040/how-to-empty-an-array-in-javascript to_length = array.length - (to_length | 0); while (0 < to_length--) array.pop(); return array; } function General_reset(array, to_length) { var i = array.length; to_length |= 0; while (to_length < i--) array[i] = 0; return []; } function Array_clone(from, to) { if (from !== to) { Array_reset(to); array_type.prototype.push.apply(to, from); } } function General_clone(from, to) { if (from !== to) { var index = to.length, l = from.length; if (index < l) { library_namespace.warn('General_clone: Target array has a shorter length!'); //index = l; } else while (l < index) //高位補 0。 to[--index] = 0; // assert: index <= from.length, should be (from.length). while (0 < index--) to[index] = from[index]; } } // 清理高數位的 0。 function Array_trim_0(integer, preserve) { var index = integer.length; // 1 < index: 直接保留最後一個,省得麻煩。 if (preserve === undefined) preserve = 1; // assert: integer[index] is integer while (preserve < index-- && integer[index] === 0); integer.length = index + 1; return integer; } //exponent > 0 時,會去掉尾數 exponent 個 digits。 //exponent < 0 時,會補上尾數 exponent 個 digits。 function Array_shift_digits(integer, exponent, no_set_exponent) { if (exponent |= 0) { if (!no_set_exponent) integer[KEY_EXPONENT] += exponent; if (0 < exponent) integer.splice(0, exponent); else { //當 exponent 量過大(e.g., 在 .precise_divide() 中,循環節 period = 71687),Chrome 32 用 //<code>Array.prototype.unshift.apply(integer, new Array(-exponent));</code> //會出現 RangeError: Maximum call stack size exceeded var a = integer.slice(); integer.length = -exponent; integer.fill(0); Array.prototype.push.apply(integer, a); } } } function General_shift_digits(integer, exponent) { if (exponent |= 0) { if (!no_set_exponent) integer[KEY_EXPONENT] += exponent; if (0 < exponent) for (var i = 0, l = integer.length; i < l; i++) integer[i] = i + exponent < l ? integer[i + exponent] : 0; else for (var i = integer.length - 1; 0 <= i; i--) integer[i] = i + exponent < 0 ? 0 : integer[i + exponent]; } } //找出最小可用之 radix。 while (Number.isNaN(parseInt('1', ++MIN_RADIX))); try { for (; ; MAX_RADIX++) // console.log(MAX_RADIX + ' ' + DEFAULT_DIGITS); // will be '0123456789abcdefghijklmnopqrstuvwxyz' DEFAULT_DIGITS += MAX_RADIX < DEFAULT_RADIX ? MAX_RADIX.toString() : MAX_RADIX.toString(MAX_RADIX + 1); } catch (e) { } // 將 DEFAULT_DIGITS 轉成小寫。 DEFAULT_DIGITS = DEFAULT_DIGITS.toLowerCase(); DEFAULT_DIGITS_CACHE = digit_cache(DEFAULT_DIGITS.split('')); //規範 MAX_SAFE_INTEGER if (MAX_SAFE_INTEGER > Number.MAX_SAFE_INTEGER) MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER; //決定 determines thie MIN_BASE while ((MAX_SAFE_INTEGER / ++MIN_BASE | 0) < 0); (function () { // 測試 array_type 可存取 attributes。 var a = array_type && new array_type(2), b; if (a) a[KEY_BASE] = 9; if (!a || a[KEY_BASE] !== 9) // assert: Array 可存取 attributes。 a = new (array_type = Array); else if (0 < a.BYTES_PER_ELEMENT) { // for TypedArray, 決定 MAX_BASE。 // 1 byte = 8 bits b = Math.floor(Math.sqrt(1 << 8 * a.BYTES_PER_ELEMENT)); if (b < MAX_BASE) { if (a.BYTES_PER_ELEMENT < 4) library_namespace.warn('所使用之 array type 能存放之值過小,將影響效能!'); MAX_BASE = b; } else if (MAX_BASE < b) // 一般說來,TypedArray 不可能存放超過 Number.MAX_SAFE_INTEGER 之整數值,因此不應該執行到這! library_namespace.error('所使用之 array type 能存放超過最大可安全計算整數 Number.MAX_SAFE_INTEGER 之值,恐造成錯誤計算結果!'); } // 決定可用的 .push() 等 array 工具函數。 if (array_type.prototype.push) { array_type.prototype.push.apply(a = new array_type, [4, 3]); if (a[1] === 3 && a.length === 2) { a.length = 0; if (a.length === 0) { //可設定 .length array_clone = Array_clone; array_reset = Array_reset; trim_0 = Array_trim_0; shift_digits = Array_shift_digits; } } } if (!array_clone) { array_clone = General_clone; //無法設定 .length array_reset = General_reset; trim_0 = function (integer) { return integer; }; shift_digits = General_shift_digits; } })(); // ---------------------------------------------------------------------// // 工具函數 // 為正規 base。 function valid_base(base, force_change_base) { // assert: MAX_BASE === MAX_BASE | 0 if (base === (base | 0) // && (force_change_base ? MIN_RADIX <= base : MIN_BASE <= base) && base <= MAX_BASE //&& base !== Integer.prototype[KEY_BASE] ) return base; } // 為正規 radix。 function valid_radix(radix) { // assert: MIN_RADIX === MIN_RADIX | 0 if (radix === (radix | 0) && MIN_RADIX <= radix && radix <= MAX_BASE ) return radix; } // 超過此界限,與元素(Integer digit)相乘時即有可能超過 Number.MAX_SAFE_INTEGER。 // boundary(base+2)<Number.MAX_SAFE_INTEGER function multiplication_boundary(base) { // assert: return > 1 return valid_base(base) ? Math.floor(MAX_SAFE_INTEGER / base) : MULTIPLICATION_BOUNDARY; } // 若為準確次方,則回傳次方數。 // number = base ^ count_exponent(number, base) function count_exponent(number, base) { if (number < base) return -count_exponent(base, number); var exponent = 0; while (number !== 0 && 0 === number % base) number /= base, exponent++; if (number === ZERO_EXPONENT) return exponent; } function do_modified(integer) { delete integer[KEY_CACHE]; } // ---------------------------------------------------------------------// // definition of module integer /** * 任意大小、帶正負號的整數。integer instance. * * @example * <code> * CeL.log((new CeL.integer('876567896')).op('*','6456789976545678')); * </code> * * @class Integer 的 constructor * @constructor */ function Integer(number, base, to_base, force_change_base) { if (1 === arguments.length && is_Integer(number)) // number = Integer(number) // 當純粹只是為了轉換型別時使用。 return this instanceof Integer ? number.clone() : number; var integer = new_instance(); if (number !== undefined || 1 < arguments.length) if (Number.isSafeInteger(number)) { if (MIN_RADIX <= base && !to_base || typeof to_base === 'boolean') // shift arguments force_change_base = to_base, to_base = base; // 快速處理常用功能。 if (to_base = valid_base(to_base, force_change_base)) integer[KEY_BASE] = to_base; else // assert: integer[KEY_BASE] === DEFAULT_BASE to_base = DEFAULT_BASE; if (number === 0) integer[0] = 0; else { if (number < 0) integer[KEY_NEGATIVE] = true, number = -number; for (var index = 0; 0 < number; number = number / to_base | 0) integer[index++] = number % to_base; } } else assignment.apply(integer, arguments); return integer; } // instance public interface ------------------- // 每個位數存放 {safe integer} 0 – 此數-1,大於等於 此數 即須進位。 //read-only Integer.prototype[KEY_BASE] = DEFAULT_BASE; // 預設為 0 次方。 Integer.prototype[KEY_EXPONENT] = 0; // https://en.wikipedia.org/wiki/Operation_(mathematics) var OP_REFERENCE = { '+': add, '-': subtract, '*': multiply, '/': divide, '%': modulo, '^': power, '=': assignment, '==': compare }; Object.assign(Integer.prototype, OP_REFERENCE, { forEach: Array.prototype.forEach, // 下面全部皆為 assignment,例如 '+' 實為 '+='。 assignment: assignment, assign: assignment, // add_assignment add: add, // subtract_assignment subtract: subtract, // multiply_assignment multiply: multiply, // divide_assignment division: division, divide: divide, div: divide, modulo: modulo, mod: modulo, power: power, pow: power, square: square, square_root: square_root, sqrt: square_root, // 至此為 assignment。 precise_divide: precise_divide, clone: clone, // 偶數的 is_even: function (test_odd) { return !(KEY_TYPE in this) && this[KEY_EXPONENT] === 0 // && this.modulo(2) === (test_odd ? 1 : 0); }, // 奇數的,單數的 is_odd: function () { return this.is_odd(true); }, // absolute value/絕對值/模 // https://en.wikipedia.org/wiki/Absolute_value abs: function (negative) { if ((negative = !!negative) !== !!this[KEY_NEGATIVE]) do_modified(this), this[KEY_NEGATIVE] = negative; return this; }, // 變換正負號。 negate: function () { do_modified(this); if (this[KEY_NEGATIVE]) delete this[KEY_NEGATIVE]; else if (!this.is_0()) this[KEY_NEGATIVE] = true; return this; }, is_positive: function () { return this.compare(0) > 0; }, is_negative: function () { return !!this[KEY_NEGATIVE]; }, // 正負符號。 // https://en.wikipedia.org/wiki/Sign_(mathematics) // https://en.wikipedia.org/wiki/Sign_function sign: function (negative) { // NaN: 0 return this[KEY_NEGATIVE] ? -1 : this.is_0() ? 0 : Number.isNaN(this[KEY_TYPE]) ? undefined : 1; }, get_base: function () { return this[KEY_BASE]; }, get_exponent: function (exp) { if (Number.isSafeInteger(exp) && exp !== this[KEY_EXPONENT]) { do_modified(this); // WARNING 注意: 除非有正當理由,否則不應直接修改 exponent! if (!(this[KEY_EXPONENT] = exp)) // Can't use delete @ IE8. //delete this[KEY_EXPONENT]; this[KEY_EXPONENT] = 0; } return this[KEY_EXPONENT]; }, expand_exponent: function () { //直接展開指數。直接作位元操作,以求效率。 shift_digits(this, -this[KEY_EXPONENT], true); // assert: this[KEY_EXPONENT] === 0 //去除 exponent 設定。 // Can't use delete @ IE8. //delete this[KEY_EXPONENT]; this[KEY_EXPONENT] = 0; return this; }, Euclidean_algorithm: Euclidean_algorithm, to_precision: to_precision, round: round, floor: function (digits) { return this.round(digits, -Infinity); }, ceil: function (digits) { return this.round(digits, Infinity); }, log: log, is_0: is_0, compare_amount: compare_amount, compare: compare, equals: function (number) { return this.compare(number) === 0; }, isFinite: function () { return !(KEY_TYPE in this); }, isNaN: function () { return (KEY_TYPE in this) && isNaN(this[KEY_TYPE]); }, power_modulo: power_modulo, Miller_Rabin: Miller_Rabin, not_prime: not_prime, Pollards_rho: Pollards_rho_1980, factorize: factorize, op: set_operate(OP_REFERENCE), for_each_digit: for_each_digit, ratio_to: ratio_to, valueOf: valueOf, digits: digits, digit_sum: digit_sum, toString: toString }); // setup Integer constructor after Integer.prototype configured. var new_instance = Array.derive(Integer, array_type); // class public interface --------------------------- var is_Integer = (new Integer) instanceof Integer ? function (number) { return number instanceof Integer; } : array_type === Array ? Array.isArray // : library_namespace.type_tester(library_namespace.is_type(new array_type)); function Integer_compare(number1, number2) { if (typeof number1 === 'number' && typeof number2 === 'number') return number1 - number2; if (!is_Integer(number1)) number1 = new Integer(number1, null, is_Integer(number2) && number2[KEY_BASE]); return number1.compare(number2); } // get the extreme value (極端值: maximum/min) of input values function extreme(values, get_minima) { var index = values.length, extreme_value, value, compare; if (!index) // ES6: Math.max: If no arguments are given, the result is −∞. return get_minima ? Infinity : -Infinity; extreme_value = values[--index]; while (0 < index--) { // WARNING 注意: 當碰上許多大數時,會出現需要多次轉換 extreme_value 成 Integer 的效能低下情形! // 但若許多數字不同底,而最大的是 String,則可能獲得部分效能。 if (Number.isNaN(compare = Integer_compare(extreme_value, value = values[index]))) // ES6: Math.max: If any value is NaN, the result is NaN. return NaN; if (get_minima ? compare > 0 : compare < 0) extreme_value = value; // 依規範,必須掃描一次,確定沒 NaN。不可中途跳出。 if (false && (get_minima ? compare > 0 : compare < 0) //當有改變時才偵測。 && typeof (extreme_value = value) === 'number' && !Number.isFinite(extreme_value = value)) break; } return extreme_value; } // range: // 1 – Number.MAX_SAFE_INTEGER 當作 digits // Number.MAX_SAFE_INTEGER + 1 – Number.MAX_VALUE || (is_Integer(range) && !(KEY_TYPE in range)) 當作 maximum value // 其他採預設值 digits = 2 function random(range, base) { var r, i; if (0 < range && isFinite(range)) if (!Number.isSafeInteger(range = +range)) range = new Integer(range, null, base); if (is_Integer(range) && !(KEY_TYPE in range)) { //求極值之最大位元 for (i = range.length; 0 < i && range[--i] < 2 ;); if (range[i] < 2) range = 0; else { r = new Integer(0, base); r[i] = Math.floor(range[i] * Math.random()); range = i; } } // 其他情況採預設值 digits = 2 if (!(0 < range) || !Number.isSafeInteger(range)) range = 2; // assert: range = {natural number}digits if (!r) r = new Integer(0, base); for (base = r[KEY_BASE]; 0 < range;) r[--range] = Math.floor(base * Math.random()); return r; } // WARNING 注意: this operation will modify arguments! // https://en.wikipedia.org/wiki/Lehmer%27s_GCD_algorithm function Integer_GCD(number_array) { if (arguments.length > 1) // Array.from() number_array = Array.prototype.slice.call(arguments); // 盡可能先挑小的,增加效率。 number_array.sort(); var index = 0, length = number_array.length, number, n, // use_Number, Integer_Array = [], gcd = undefined; // 先嘗試是否可能找出較小的 gcd 以提升效能。 for (; index < length;) { number = number_array[index++]; if (typeof number === 'string') number = Number.isSafeInteger(n = parseInt(number)) ? n : new Integer(number); if (is_Integer(number)) Integer_Array.push(number); else if (number && !(1 < (gcd = gcd === undefined ? number : GCD([number, gcd])))) return gcd; } for (use_Number = gcd !== undefined, index = 0, length = Integer_Array.length; index < length;) { number = Integer_Array[index++]; if ((KEY_TYPE in number) || number.is_0()) continue; if (gcd === undefined) { gcd = number; continue; } if (use_Number) { // number = number % gcd // assert: number.valueOf() is not Infinity if (number = number.modulo(gcd).valueOf()) { gcd = GCD([gcd, number]); if (gcd === 1) break; } //else: 整除,GCD 不變,跳過此數。 } else { //if (typeof (gcd = number.Euclidean_algorithm(gcd)[1]) === 'number') use_Number = true; // 使用 .modulo() 以增進效率,不使用 .Euclidean_algorithm()。 while (!number.modulo(gcd).is_0()) //swap gcd, number. n = gcd, gcd = number, number = n; if (gcd.is_0(1)) { gcd = 1; break; } if (Number.isSafeInteger(n = gcd.valueOf())) gcd = n, use_Number = true; } }; return gcd; } // https://en.wikipedia.org/wiki/Least_common_multiple function Integer_LCM(number_array) { if (arguments.length > 1) // Array.from() number_array = Array.prototype.slice.call(arguments); var lcm = new Integer(number_array[0]), //index = 1: [0] → lcm index = 1, length = number_array.length, number; // TODO: 增進效率。 for (; index < length && lcm.compare(0) > 0 ;) lcm = lcm.division(lcm.clone().Euclidean_algorithm( // lcm = lcm / GCD * number is_Integer(number = number_array[index++]) ? number.clone() : number)[1]) // .multiply(number); return lcm; } // factorial_cache[ n ] = n! // factorial_cache = [ 0! = 1, 1!, 2!, .. ] var factorial_cache = [1], // maximum cache length. // assert: (factorial_cache_limit!) 已逾越 SafeInteger 範圍, 約為 18。 factorial_cache_limit = 80, factorial_SafeInteger_limit; /** * Get the factorial (階乘) of (integer).<br /> * * @param {integer}integer * safe integer. * @returns n的階乘. * @see https://en.wikipedia.org/wiki/Factorial */ function factorial(integer) { if ((integer !== integer | 0) || !Number.isSafeInteger(integer) || !(integer >= 0)) { library_namespace.error('factorial: invalid number: ' + integer); return NaN; } var length = factorial_cache.length; if (integer < length) { if (is_Integer(integer = factorial_cache[integer])) integer = integer.clone(); return integer; } var f = factorial_cache[--length]; if (!factorial_SafeInteger_limit) { while (length < integer) if (Number.isSafeInteger(f *= ++length)) factorial_cache.push(f); else { f = factorial_cache[--length]; factorial_SafeInteger_limit = length; break; } if (integer === length) return f; } f = new Integer(f); for (var l = Math.min(factorial_cache_limit, integer) ; length < l;) factorial_cache.push(f.multiply(++length).clone()); while (length < integer) f.multiply(++length); return f; } // 組合數學 /** * Get the permutation (排列的可能方式數量) of (n).<br /> * = n! / (n - k)!<br /> * TODO: precision * * @param {safe * integer}n: 排列的元素數量 * @param {safe * integer}k 排列的長度 * @param {safe * integer}precision * * @returns {safe integer} permutation (排列的可能方式數量) of * (n). * @see https://en.wikipedia.org/wiki/Permutation */ function permutation(n, k, precision) { if ((n !== n | 0) || !Number.isSafeInteger(n) || !(n >= 0)) { library_namespace.error('permutation: invalid number: ' + n); return NaN; } if (isNaN(k)) return factorial(n); if (!k) return factorial_cache[0]; k = n - (k | 0); if (!(0 <= k && k < n)) { library_namespace.error('permutation: invalid number: ' + k); return NaN; } if (k < 2) return factorial(n); if (n <= factorial_cache_limit) { n = factorial(n); k = factorial_cache[k]; if (is_Integer(n)) n = n.division(k); else n /= k; return n; } var m, p; if (factorial_cache_limit < k) p = new Integer(++k); else m = factorial(factorial_cache_limit).division(factorial_cache[k]), p = new Integer(k = factorial_cache_limit + 1); while (++k <= n) p.multiply(k); if (m) p.multiply(m); return p; } // 多項式定理係數/multinomial coefficients // https://en.wikipedia.org/wiki/Multinomial_theorem#Multinomial_coefficients function multinomial_coefficient(count_array) { if (!Array.isArray(count_array)) count_array = Array.prototype.slice.apply(arguments); // small → big count_array.sort(); var c = new Integer(MULTIPLICATIVE_IDENTITY), // n = count_array.pop(), i = 0, l = count_array.length, k, p, f; while (i < l) { k = count_array[i++]; p = permutation(n += k, k); f = factorial(k); c.multiply(typeof p !== 'number' || factorial_SafeInteger_limit < k ? Integer(p).division(f) : p / f); } return c; } // 二項式係數/組合 // (1 + x)^n 的多項式展式中,x^k 項的係數。 // combination(n, k) = n!/k!/(n-k)! // https://en.wikipedia.org/wiki/Combination function combination(n, k) { return multinomial_coefficient([k, n - k]); } // get the ratio of (length) th convergent of continued fraction. // 取得連分數序列(sequence)至第(length)個逼近的比例值 // modified from data.math.continued_fraction // TODO: {Integer|Array}partial_numerator, {Boolean}get_coefficients, to quadratic function convergent_of(sequence, length, base, partial_numerator, get_coefficients) { if (!Array.isArray(sequence) || !sequence.length || sequence.length < 1) return sequence; if (length < 0) length += sequence.length; if (!(0 < length) || sequence.length < length) length = sequence.length; var numerator = new Integer(1), denominator = new Integer(0), i; if (length % 2) i = numerator, numerator = denominator, denominator = i; while (length--) { i = new Integer(sequence[length], base); if (length % 2) denominator.add(i.multiply(numerator)); else numerator.add(i.multiply(denominator)); } return [numerator, denominator]; } Object.assign(Integer, { radix_point: function (p) { if (p) radix_point = p; return radix_point; }, DEFAULT_BASE: DEFAULT_BASE, valid_radix: valid_radix, set_operate: set_operate, random: random, // maximum max: function Integer_max() { // get max() return extreme(arguments); }, min: function Integer_min() { // get min() return extreme(arguments, true); }, compare: Integer_compare, // WARNING 注意: this operation will modify both dividend and divisor! Euclidean_algorithm: function (dividend, divisor, get_coefficients) { if (!is_Integer(dividend)) dividend = new Integer(dividend, is_Integer(divisor) && divisor[KEY_BASE]); return dividend.Euclidean_algorithm(divisor, get_coefficients); }, GCD: Integer_GCD, LCM: Integer_LCM, // TODO: precision square_root: function (number, negative_exponent) { return (new Integer(number)).square_root(negative_exponent); }, E: Integer_E, exp: Integer_exp, LN2: Integer_LN2, LN10: Integer_LN10, PI: Integer_PI, factorial: factorial, permutation: permutation, combination: combination, multinomial_coefficient: multinomial_coefficient, convergent_of: convergent_of, is_Integer: is_Integer }); // ---------------------------------------------------------------------// // 因 clone 頗為常用,作特殊處置以增進效率。 function clone(convert_to_Number_if_possible, target_Integer, include_cache) { if (convert_to_Number_if_possible === true) { var value = this.valueOf(); if (value <= Number.MAX_SAFE_INTEGER) { return value; } } else if (typeof convert_to_Number_if_possible !== 'boolean' && include_cache === undefined) { // shift arguments include_cache = target_Integer; target_Integer = convert_to_Number_if_possible; } if (!is_Integer(target_Integer)) { target_Integer = new_instance(); } [KEY_BASE, KEY_NEGATIVE, KEY_TYPE in this ? KEY_TYPE : KEY_EXPONENT].forEach(function (key) { if (key in this) target_Integer[key] = this[key]; }, this); if (!(KEY_TYPE in this)) { array_clone(this, target_Integer); if (include_cache) { if (KEY_CACHE in this) // clone Array target_Integer[KEY_CACHE] = this[KEY_CACHE].slice(); if (KEY_FACTORS in this) target_Integer[KEY_FACTORS] = this[KEY_FACTORS].slice(); } } return target_Integer; } function get_precision(number) { var matched = String(number).match(/^-?(\d*)\.(\d+)$/); if (matched) return (matched[1] === '0' ? 0 : matched[1].length) + matched[2].length; } /** * assignment value of integer instance.<br /> * 僅設定單一值。 * * @param {Number|String|Integer}number 輸入數值(value/number)大小。 * @param {natural number|String|Array}[base] 輸入字串採用之進位制基底/數字 digit 字集。區分大小寫。僅於輸入字串時有用。 * @param {natural number}[to_base] 內採基底/進位制。 * * @example * <code> * CeL.log((new CeL.integer('876567896')).op('*','6456789976545678')); * </code> * * @return 回傳 integer 物件。 */ function assignment(number, base, to_base, force_change_base) { if (typeof number === 'number' && get_precision(number) < MAX_TRUNCATED) { //當可能以十進位為底做截斷時,採用十進位之值。 //e.g., 輸入 3423.3451242354,則取 '3423.3451242354',而非 3423.34512423539990778081119060516357421875。 number = number.toString(); // shift arguments force_change_base = to_base; to_base = base; base = DEFAULT_RADIX; } /** * 前期處理: String → Number / Integer<br /> * 轉換指定進位的數字文字,成為{Number}純量或 {Integer} 物件。<br /> * treat arguments as: (number_String, base, to_base) * * @see * <a href="https://en.wikipedia.org/wiki/Numerical_digit" accessdate="2010/4/16 20:47">Numerical digit</a> */ if (typeof number === 'string' && (number = number.trim())) { // 正規化(normalize) base // {Array}base → {String}base if (Array.isArray(base)) { base.forEach(function (digit) { if (digit.length !== 1) library_namespace.error('assignment: Invalid digit of base: [' + digit + '].'); }); base = base.join(''); } if (typeof base === 'string' && DEFAULT_DIGITS.startsWith(base.toLowerCase())) // 使用 DEFAULT_DIGITS。 base = base.length; if (typeof base === 'string' ? base.length < 2 //base is number : !valid_radix(base)) { if (base) library_namespace.error('assignment: Invalid base: [' + base + ']'); base = undefined; } var digits, value; if (value = number.match(PATTERN_HEX)) number = value[1], base = HEX_RADIX; if (typeof base === 'string') { digits = digit_cache(base.split('')); value = number.split(''); number = new Integer(0, base = base.length); // 使用 DEFAULT_DIGITS。不分大小寫(將轉成小寫)。基本上應與 parseInt() 一致。 // [ full , sign, integer part 整數部分, fractional part 小數部分, decimal exponent 指數, percent ] } else if (value = (value = number.toLowerCase()).match(PATTERN_NUMBER) // || value.match(PATTERN_NUMBER_HEX)) { if (!base) base = DEFAULT_RADIX; number = new Integer(0, base); //處理 minus sign if (value[1] === '-') number[KEY_NEGATIVE] = true; //處理指數 value[4] |= 0; if (value[3]) { //處理掉 fractional part 小數部分 value[4] -= value[3].length; value[2] += value[3]; } if ((digits = value[2].match(/^(.*)(0+)$/)) //1e4: 若是 exponent 不大,則基本上無須處理,直接展開即可。 && (value[4] < 0 || 1e4 < value[4] + digits[2].length)) { //去掉最後的 0 value[4] += digits[2].length; value[2] = digits[1]; } if (value[5]) value[4] += exponent_parts_per[value[5]]; if (value[4]) //1e4: 若是 exponent 不大,則基本上無須處理,直接展開即可。 if (value[4] < 0 || 1e4 < value[4]) number[KEY_EXPONENT] = value[4]; else value[2] += '0'.repeat(value[4]); //去掉起頭的 '0'。 value = value[2].replace(/^0+/, '').split(''); digits = DEFAULT_DIGITS_CACHE; // 死馬當活馬醫,嘗試以 native method 取得。 // 若非十進位又包含 radix_point,則跳過。 } else if (//(!base || base !== DEFAULT_RADIX || number.indexOf(radix_point) === -1) && Number.isSafeInteger(value = base ? parseInt(number, base) : parseFloat(number))) number = value; else { library_namespace.error('assignment: Invalid number string: [' + number + '].'); number = NaN; } if (Array.isArray(value)) { //base: {natural number}length of base. //digits: {Object}base cache. //value: {Array}digits of specified base // number: 欲轉換 base 之 {Integer}。 value.reverse(); // Array.map() value.forEach(function (digit, index) { if (digit in digits) number[index] = digits[digit]; else library_namespace.error('assignment: Invalid number digit: [' + digit + '].'); }); if (!to_base && count_exponent(DEFAULT_BASE, base)) to_base = DEFAULT_BASE; } } else if (MIN_RADIX <= base && !to_base || typeof to_base === 'boolean') // shift arguments force_change_base = to_base, to_base = base, base = undefined; // --------------------------------------- if (is_Integer(number)) { // 已經是 Integer 了。 // clone, deep_copy。 //let to_base === this[KEY_BASE], base === number[KEY_BASE] // 無設定 to_base 時,將 base 視作 to_base。 // assert: number[KEY_BASE] 為正規 base。 to_base = valid_base(to_base, force_change_base) || number[KEY_BASE]; base = number[KEY_BASE]; if (this !== number || base !== to_base) { if (this === number) number = number.clone(); else { // copy attributes. this[KEY_NEGATIVE] = number[KEY_NEGATIVE]; if (KEY_CACHE in number) { var array = this[KEY_CACHE] = []; number[KEY_CACHE].forEach(function (string, radix) { array[radix] = string; }); } else delete this[KEY_CACHE]; if (KEY_FACTORS in number) { var array = this[KEY_FACTORS] = []; number[KEY_FACTORS].forEach(function (factor) { if (factor) array.push(is_Integer(factor) ? factor.clone() : factor); }); } else delete this[KEY_FACTORS]; } do_modified(this); this[KEY_BASE] = to_base; if (KEY_TYPE in number) { //處理特殊值。 this[KEY_TYPE] = number[KEY_TYPE]; // Can't use delete @ IE8. //delete this[KEY_EXPONENT]; this[KEY_EXPONENT] = 0; array_reset(this); } else if (to_base === base || number.length < 2 && !(to_base <= number[0])) { //處理簡易數值。 if (number[KEY_EXPONENT]) this[KEY_EXPONENT] = number[KEY_EXPONENT]; else // Can't use delete @ IE8. //delete this[KEY_EXPONENT]; this[KEY_EXPONENT] = 0; array_clone(number, this); } else { // change base to / set base / 數字基底的轉換。 // https://en.wikipedia.org/wiki/Change_of_base // https://en.wikipedia.org/wiki/Base_conversion var exponent = count_exponent(to_base, base), to_digit_Array = array_reset(this), scalar = 0, base_now = ZERO_EXPONENT; // 對 exponent 做特殊處置,增進效率。 if (0 < exponent) { // e.g., base 10 → to_base 100 if (number[KEY_EXPONENT]) { //因為會改變 number,因此新造一個。 number = number.clone(); if (0 < number[KEY_EXPONENT]) { // e.g., base=1e1, to_base=1e7, 23e(+17*1) = 23000e(+2*7) this[KEY_EXPONENT] = number[KEY_EXPONENT] / exponent | 0; shift_digits(number, -number[KEY_EXPONENT] % exponent); } else { // e.g., base=1e1, to_base=1e7, 23e(-17*1) = 230000e(-3*7) this[KEY_EXPONENT] = (number[KEY_EXPONENT] / exponent | 0) - 1; shift_digits(number, -(number[KEY_EXPONENT] % exponent) - exponent); } } number.forEach(function (digit, index) { scalar += digit * base_now; if ((index + 1) % exponent === 0) to_digit_Array.push(scalar), scalar = 0, base_now = ZERO_EXPONENT; else base_now *= base; }); if (scalar) to_digit_Array.push(scalar); array_clone(to_digit_Array, this); } else if (exponent < 0) { // e.g., base 100 → to_base 10 exponent = -exponent; if (number[KEY_EXPONENT]) // e.g., base=1e7, to_base=1e1, 2300e(+2*7) = 2300e(+14*1) // e.g., base=1e7, to_base=1e1, 2300e(-2*7) = 2300e(-14*1) this[KEY_EXPONENT] = exponent * number[KEY_EXPONENT]; number.forEach(function (digit, index) { for (var i = 0; i < exponent; i++) to_digit_Array.push(digit % to_base), digit = digit / to_base | 0; }); trim_0(to_digit_Array); array_clone(to_digit_Array, this); } else if (1 === (exponent = Math.log(MAX_BASE) / Math.log(to_base) | 0)) { //無法做特殊處置時之一般性處理。 var fraction = 0, index, boundary = multiplication_boundary(to_base); if (number[KEY_EXPONENT]) { //因為會改變 number,因此新造一個。 number = number.clone(); if (0 < number[KEY_EXPONENT]) //直接展開指數。 number.expand_exponent(); else { library_namespace.error('assignment: Unable to convert from base ' + base + ' to base ' + to_base + ' with exponent ' + number[KEY_EXPONENT] + ' without loss of significance.'); //計算 fraction。 index = -number[KEY_EXPONENT]; for (var fraction_base = ZERO_EXPONENT; fraction_base && index;) fraction += number[--index] * (fraction_base /= base); //直接展開指數。去掉 fraction。 number.expand_exponent(); } } //reset (this) array_clone([0], this); index = number.length; while (0 < index--) { base_now *= base; scalar = scalar * base + number[index]; if (boundary < base_now * base || index === 0) { this.for_each_digit(function (digit, carry, index) { // 除了積本身,這邊可能出現 scalar<=(boundary-1), carry<=(base-1)。 // (base-1)*boundary+(boundary-1)+(base-1) <= Number.MAX_SAFE_INTEGER // This is also the limit of (base), therefore: // MAX_BASE<=Math.sqrt(Number.MAX_SAFE_INTEGER+2), // boundary<=(Number.MAX_SAFE_INTEGER+2)/base-1, return digit * base_now + carry + (index ? 0 : scalar); }); //reset scalar = 0, base_now = ZERO_EXPONENT; } } if (fraction) this.add(fraction, this[KEY_NEGATIVE]); if (0 === number.length) // assert: Array.(number) number.push(0); } else //盡可能把 to_base 加大,減少 call .for_each_digit() 的次數,以增進效率。 this.assignment(new Integer(number, Math.pow(to_base, exponent)), to_base, force_change_base); } } // --------------------------------------- } else { if (typeof number !== 'number') { library_namespace.error('assignment: Invalid value to assignment: [' + number + '].'); number = NaN; } if (base !== to_base // || this.compare(number) !== 0) { do_modified(this); // value/scalar純量 to digit Array. // treat arguments as: (number, do not set fraction = false, to_base) // 對於非數字,無法斷定。 if (number < 0) number = -number, this[KEY_NEGATIVE] = true; else delete this[KEY_NEGATIVE]; delete this[KEY_FACTORS]; // Can't use delete @ IE8. //delete this[KEY_EXPONENT]; this[KEY_EXPONENT] = 0; if (!isFinite(number)) { //NaN, Infinity, -Infinity this[KEY_TYPE] = number; array_reset(this); } else { delete this[KEY_TYPE]; //to_base 實為欲轉換之標的 base。 if (to_base = valid_base(to_base, force_change_base)) this[KEY_BASE] = to_base; else to_base = this[KEY_BASE]; //base 實為是否不轉換小數部分。 if (base && number !== Math.floor(number)) { //number 有小數部分。 library_namespace.warn('assignment: Number has a fractional part: [' + number + '].'); number = Math.floor(number); } if (number < to_base && number === (number | 0)) // 僅設定scalar純量部份。 array_clone([number], this); else { var digit_Array = array_reset(this); // assert: 1 < to_base if (number !== Math.floor(number)) { // 當 base_now === 0,表示系統已無法處理較這更小的數字,再保留這之下的數值已無意義。 for (var base_now = ZERO_EXPONENT, remainder = number % 1; remainder && (base_now /= to_base) ;) digit_Array.unshift((remainder *= to_base) | 0), remainder %= 1; this[KEY_EXPONENT] = -digit_Array.length; number = Math.floor(number); } else if (!Number.isSafeInteger(number)) //test only library_namespace.warn('assignment: Number is too large: [' + number + '].'); while (0 < number) { digit_Array.push(number % to_base); number = Math.floor(number / to_base); } array_clone(digit_Array, this); } } } } return this; } function get_test_value(number) { return is_Integer(number) ? number.valueOf(TYPE_TEST) : +number; } // little_natural: little natural number < MIN_RADIX, e.g., 1 function is_0(little_natural) { return !(KEY_TYPE in this) && this.length < 2 && (little_natural ? !this[KEY_EXPONENT] && this[0] === little_natural : !this[0]); } /** * 測試大小/比大小。僅比較量之大小,忽略符號。 * @param number the number to compare * @return {Number} 0:==, <0:<, >0:> * @_name _module_.prototype.compare_to */ // return < 0 : this < number // return === 0 : this === number // return > 0 : this > number // return others : invalid number function compare_amount(number) { if (this === number) return 0; var i = typeof number === 'string' ? 0 : get_test_value(number), l, d; if ((KEY_TYPE in this) || !isFinite(i)) // NaN 等極端數字的情形。 return Math.floor(this[KEY_TYPE]) - Math.floor(i); // 強制轉成同底的 Integer 再處理。 if (!is_Integer(number) || this[KEY_BASE] !== number[KEY_BASE]) number = new Integer(number, null, this[KEY_BASE]); i = this.length; // 處理 [KEY_EXPONENT] d = this[KEY_EXPONENT] - number[KEY_EXPONENT]; l = i + d - number.length; if (!l) //找到第一個兩者不同的位數。 while (0 < i-- && !(l = (this[i] || 0) - (number[i + d] || 0))); return l; } /** * 測試大小/比大小 * @param number the number to compare * @return {Number} 0:==, <0:<, >0:> * @_name _module_.prototype.compare_to */ function compare(number) { var c = typeof number === 'string' ? 0 : get_test_value(number); if ((KEY_TYPE in this) || !isFinite(c)) // NaN 等極端數字的情形。 return this[KEY_TYPE] - c; if (!is_Integer(number)) { if (typeof number === 'number' && this.length < 3 //2: Math.min(-Math.floor(Math.log(Number.EPSILON) / Math.log(MAX_BASE), Math.floor(Math.log(Number.MAX_VALUE) / Math.log(MAX_BASE)) + 1)) //預防 overflow or underflow. && Math.abs(this[KEY_EXPONENT]) < 2) return this.valueOf() - number; number = new Integer(number, null, this[KEY_BASE]); } if (this[KEY_NEGATIVE] ^ number[KEY_NEGATIVE]) return this[KEY_NEGATIVE] ? -1 : 1; c = this.compare_amount(number); return this[KEY_NEGATIVE] ? -c : c; } // 工具函數 // 將有問題的數字全部作正確進位。 // e.g., [10, 11].base = 10 → [0, 2, 1] function normalize_handle(digit, carry) { return digit + carry; } function normalize_digits(integer) { integer.for_each_digit(normalize_handle); } // 工具函數 // 將 this integer instance 自低位依 callcack() 處理至高位, // 結果存至 target_Integer[跳過 target_shift 個] || this。 // 可自動處理進退位。無法處理 overflow 問題。 // assert: callcack() 任一回傳,皆 isSafeInteger() === true。 function for_each_digit(callcack, target_Integer, target_shift) { if (!target_Integer) target_Integer = this; target_shift |= 0; var base = target_Integer[KEY_BASE], carry = 0, length = this.length, index = 0, digit; if (!Number.isSafeInteger(base)) library_namespace.error('for_each_digit: Invalid base: [' + base + '].'); for (; index < length || carry !== 0 ; index++, target_shift++) // 當 index >= length,僅作進位處理。 if (typeof (digit = index < length ? callcack(this[index] || 0, carry, index) // 當 this 皆 callcack() 過後,僅處理進退位。 : carry + (target_Integer[target_shift] || 0)) === 'number') { if (base <= digit) { // 處理進位。 // assert: 0 < (digit / base | 0) // MIN_BASE: 因為用 `|0`,故 base < 5 會出現問題: // (Number.MAX_SAFE_INTEGER / 4 | 0) < 0, 0 < (Number.MAX_SAFE_INTEGER / 5 | 0) carry = digit / base | 0; digi