cejs
Version:
A JavaScript module framework that is simple to use.
1,588 lines (1,369 loc) • 117 kB
JavaScript
/**
* @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&id=30169&tpg=1&ppg=1&sty=1&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