cejs
Version:
A JavaScript module framework that is simple to use.
1,889 lines (1,688 loc) • 123 kB
JavaScript
/**
* @name CeL function for mathematics.
* @fileoverview 本檔案包含了數學演算相關的 functions。
*
* TODO: 方程式圖形顯示 by SVG
*
* @see http://www.wolframalpha.com/<br />
* http://www.numberempire.com/
*/
// More examples: see /_test suite/test.js
'use strict';
// 'use asm';
// --------------------------------------------------------------------------------------------
// 不採用 if 陳述式,可以避免 Eclipse JSDoc 與 format 多縮排一層。
typeof CeL === 'function' && CeL.run({
// module name
name : 'data.math',
require : 'data.code.compatibility.|data.native.set_bind',
// 設定不匯出的子函式。
// no_extend : '*',
// 為了方便格式化程式碼,因此將 module 函式主體另外抽出。
code : module_code
});
function module_code(library_namespace) {
// requiring
var set_bind = this.r('set_bind');
var has_bigint = library_namespace.env.has_bigint;
/**
* null module constructor
*
* @class 數學相關的 functions
*/
var _// JSDT:_module_
= function() {
// null module constructor
};
/**
* for JSDT: 有 prototype 才會將之當作 Class
*/
_// JSDT:_module_
.prototype = {};
/**
* <code>
數位
十分位 tenths digit
整數 whole number
</code>
*/
// ---------------------------------------------------------------------//
// basic constants. 定義基本常數。
var
/**
* empty product, or nullary product, 乘法單位元素.<br />
* number * MULTIPLICATIVE_IDENTITY === number.<br />
* 2/2, 3/3, ..
*
* MULTIPLICATIVE_IDENTITY = 1
*
* @type {Number}
* @constant
*
* @see https://en.wikipedia.org/wiki/Identity_element
* https://en.wikipedia.org/wiki/Empty_product
*/
MULTIPLICATIVE_IDENTITY = 1 / 1,
/**
* Any nonzero number raised by the exponent 0 is 1.<br />
* (any number) ^ 0 === Math.pow(number, 0) === ZERO_EXPONENT<br />
* Math.pow(2, 0), Math.pow(3, 0), ..
*
* ZERO_EXPONENT = 1
*
* @type {Number}
* @constant
*
* @see https://en.wikipedia.org/wiki/Exponentiation
*/
ZERO_EXPONENT = Math.pow(1, 0),
/**
* absorbing element, zero element.<br />
* number * ABSORBING_ELEMENT === ABSORBING_ELEMENT<br />
* Math.pow(2, 0), Math.pow(3, 0), ..
*
* @type {Number}
* @constant
*
* @see https://en.wikipedia.org/wiki/Absorbing_element
*/
ABSORBING_ELEMENT = 0,
/**
* multiplication sign. e.g., '⋅', '*', '×'.
*
* @type {String}
* @constant
*
* @see https://en.wikipedia.org/wiki/Multiplication_sign
* https://en.wikipedia.org/wiki/Interpunct
*/
MULTIPLICATION_SIGN = '⋅',
/**
* default base = 10.<br />
* 內定:10位數。應與 parseInt() 一致。
*
* @type {Natural}
* @constant
*/
DEFAULT_BASE = parseInt('10'),
/**
* The biggest integer we can square. 超過此數則無法安全操作平方。
*
* @type {Natural}
* @constant
*/
sqrt_max_integer = Math.sqrt(Number.MAX_SAFE_INTEGER) | 0;
// ---------------------------------------------------------------------//
/**
* use Horner's method to calculate the value of polynomial.
*
* @param {Array}coefficients
* coefficients of polynomial.<br />
* coefficients: [ degree 0, degree 1, degree 2, ... ]
* @param {Number}variable
* value of (x)
*
* @returns {Number} the value of polynomial
*
* @see https://en.wikipedia.org/wiki/Horner%27s_method
*/
function polynomial_value(coefficients, variable) {
return coefficients.reduceRight(function(value, coefficient) {
return value * variable + coefficient;
});
}
_.polynomial_value = polynomial_value;
_// JSDT:_module_
.
/**
* 輾轉相除 n1/n2 或 小數 n1/1 轉成 整數/整數。
*
* @param {Natural}n1
* number 1
* @param {Natural}[n2]
* number 2
* @param {Natural}times
* maximum times 次數, 1,2,..
*
* @return {Array} 連分數序列 (continued fraction) ** 負數視 _.mutual_division.done
* 而定!
*/
mutual_division = function mutual_division(n1, n2, times) {
var q = [], c;
if (isNaN(times) || times <= 0)
times = 80;
if (!n2 || isNaN(n2))
n2 = 1;
if (!Number.isInteger(n1)) {
c = n1;
var i = 9, f = n2;
while (i--) {
// 以整數運算比較快!這樣會造成整數多4%,浮點數多1/3倍的時間,但仍值得。
f *= DEFAULT_BASE;
c *= DEFAULT_BASE;
if (Number.isInteger(c)) {
n1 = c;
n2 = f;
break;
}
}
}
// 連分數負數之處理。更沒問題的: (n1 < 0?1:0) ^ (n2 < 0?1:0)
if (_.mutual_division.mode && ((n1 < 0) ^ (n2 < 0))) {
// 使兩數皆為正
if (n2 < 0)
n2 = -n2;
else
n1 = -n1;
q.push(-(1 + (n1 - (c = n1 % n2)) / n2));
n1 = n2;
n2 -= c;
}
// old:
if (false) {
while (b && n--) {
// 2.08s@10000
// 可能因為少設定(=)一次c所以較快。但(若輸入不為整數)不確保d為整數?用Math.floor((a-(c=a%b))/b)可確保,速度與下式一樣快。
c = a % b;
d.push((a - c) / b);
a = b;
b = c;
// 2.14s@10000:mutual_division(.142857)
// d.push(c=Math.floor(a/b)),c=a-b*c,a=b,b=c;
// 2.2s@10000
// d.push(Math.floor(a/b)),b=a%(c=b),a=c;
}
if (n)
d.push(0);
}
// 2.4s@10000
// 可能因為少設定(=)一次c所以較快。但(若輸入不為整數)不確保d為整數?用Math.floor((a-(c=a%b))/b)可確保,速度與下式一樣快。
while (times--)
if (n2) {
c = n1 % n2;
q.push((n1 - c) / n2);
n1 = n2;
n2 = c;
} else {
// [ ... , done mark, (最後非零的餘數。若原 n1, n2 皆為整數,則此值為
// GCD。但請注意:這邊是已經經過前面為了以整數運算,增加倍率過的數值!!) ]
q.push(_.mutual_division.done, n1);
// library_namespace.debug('done: ' + q);
break;
}
/**
* <code>
// 2.26s@10000
while(b&&n--)if(d.push((a-(c=a%b))/b),a=b,!(b=c)){d.push(0);break;}
var m=1;c=1;while(m&&n--)d.push(m=++c%2?b?(a-(a%=b))/b:0:a?(b-(b%=a))/a:0);//buggy
</code>
*/
return q;
};
_// JSDT:_module_
.mutual_division.done = -7;// ''
_// JSDT:_module_
.
/**
* !!mode:連分數處理,對負數僅有最初一數為負。
*/
mutual_division.mode = 0;
_// JSDT:_module_
.
/**
* 取得連分數序列的數值。
*
* @param {Array}sequence
* 序列
* @param {Natural}[max_no]
* maximum no. 取至第 max_no 個
*
* @return {Array}連分數序列的數值
*
* @requires mutual_division.done
*/
continued_fraction = function(sequence, max_no) {
if (!Array.isArray(sequence) || !sequence.length)
return sequence;
if (sequence.at(-2) === _.mutual_division.done)
sequence.length -= 2;
if (sequence.length < 1)
return sequence;
if (!max_no/* || max_no < 2 */|| max_no > sequence.length)
max_no = sequence.length;
var a, b;
if (max_no % 2) {
b = 1;
a = 0;
} else {
a = 1;
b = 0;
}
if (false) {
sequence[max_no++] = 1;
if (--max_no % 2) {
b = sequence[max_no];
a = s[--max_no];
} else {
a = sequence[max_no];
b = sequence[--max_no];
}
}
if (false)
library_namespace.debug('a=' + a + ', b=' + b + ', max_no='
+ max_no);
while (max_no--)
if (max_no % 2)
b += a * sequence[max_no];
else
a += b * sequence[max_no];
if (false)
library_namespace.debug('a=' + a + ', b=' + b);
return [ a, b ];
};
// quadratic (m√r + i) / D → continued fraction [... , [period ...]]
// Rosen, Kenneth H. (2011). Elementary Number Theory and its Applications
// (6th edition). Boston: Pearson Addison-Wesley. pp. 508–511.
// https://en.wikipedia.org/wiki/Periodic_continued_fraction
// https://en.wikipedia.org/wiki/Square_root_of_2
// https://en.wikipedia.org/wiki/Square_root#As_periodic_continued_fractions
// https://en.wikipedia.org/wiki/Generalized_continued_fraction#Roots_of_positive_numbers
function quadratic_to_continued_fraction(r, m, i, D) {
if (r < 0) {
throw 'The root is negative!';
}
if (!i)
i = 0;
if (!D)
D = 1;
if (!m)
m = 1;
else if (m < 0) {
m = -m;
i = -i;
D = -D;
}
// (m√r + i) / D
// = (√(r m^2) + i) / D
// = (√(d in book) + P0) / Q0
var d = m * m * r,
//
P = i, Q = D,
// A: α in book.
A, a, sequence = [], ptr = sequence, start_PQ;
// Be sure Q0 | (d - P0^2)
if ((d - P * P) % Q !== 0)
P *= Q, d *= Q * Q, Q *= Q;
// assert: now: Q0 | (d - P0^2)
for (var sqrt = Math.sqrt(d), t;;) {
if (start_PQ) {
if (P === start_PQ[0] && Q === start_PQ[1])
return sequence;
} else if (0 < (t = sqrt - P) && t < Q) {
// test if α is purely periodic.
start_PQ = [ P, Q ];
sequence.push(ptr = []);
}
ptr.push(a = Math.floor(A = (sqrt + P) / Q));
library_namespace.debug(((sequence === ptr ? 0
: sequence.length - 1)
+ ptr.length - 1)
+ ': P='
+ P
+ ', Q='
+ Q
+ ', α≈'
+ (DEFAULT_BASE * A | 0)
/ DEFAULT_BASE + ', a=' + a, 3);
// set next Pn = a(n-1)Q(n-1) - P(n-1), Qn = (d - Pn^2) / Q(n-1).
P = a * Q - P;
Q = (d - P * P) / Q;
if (Q === 0)
// is not a quadratic irrationality?
return sequence;
// assert: Pn, Qn are both integers.
}
}
_.quadratic_to_continued_fraction = quadratic_to_continued_fraction;
// get the first solution of Pell's equation: x^2 - d y^2 = 1 or -1.
// https://en.wikipedia.org/wiki/Pell%27s_equation
// Rosen, Kenneth H. (2005). Elementary Number Theory and its Applications
// (5th edition). Boston: Pearson Addison-Wesley. pp. 542-545.
function solve_Pell(d, n, NO) {
// TODO
// use CeL.data.math.quadratic.solve_Pell instead
;
}
// _.solve_Pell = solve_Pell;
_// JSDT:_module_
.
/**
* The best rational approximation. 取得值最接近之有理數 (use 連分數 continued fraction),
* 取近似值. c.f., 調日法 在分子或分母小於下一個漸進分數的分數中,其值是最接近精準值的近似值。
*
* @param {Number}number
* number
* @param {Number}[rate]
* 比例在 rate 以上
* @param {Natural}[max_no]
* maximum no. 最多取至序列第 max_no 個 TODO : 並小於 l: limit
*
* @return {Array}[分子, 分母, 誤差]
*
* @requires mutual_division,continued_fraction
* @see https://en.wikipedia.org/wiki/Continued_fraction#Best_to_rational_numbers
*/
to_rational_number = function(number, rate, max_no) {
if (!rate)
// This is a magic number: 我們無法準確得知其界限為何。
rate = 65536;
var d = _
.mutual_division(number, 1, max_no && max_no > 0 ? max_no : 20), i = 0, a, b = d[0], done = _.mutual_division.done;
if (!b)
b = d[++i];
while (++i < d.length && (a = d[i]) !== done)
if (a / b < rate)
b = a;
else
break;
if (false)
library_namespace.debug(number
+ ' '
+
// 連分數表示 (continued fraction)
(d.length > 1 && d.at(-2) === _.mutual_division.done ? '='
+ ' [<em>'
+ d[0]
+ ';'
+ d.slice(1, i).join(', ')
+ '</em>'
+ (i < d.length - 2 ? ', '
+ d.slice(i, -2).join(', ') : '')
+ '] ... ' + d.slice(-1)
:
// 約等於的符號是≈或≒,不等於的符號是≠。
// https://zh.wikipedia.org/wiki/%E7%AD%89%E4%BA%8E
'≈'
+ ' [<em>'
+ d[0]
+ ';'
+ d.slice(1, i).join(', ')
+ '</em>'
+ (i < d.length ? ', '
+ d.slice(i).join(', ') : '')
+ ']: ' + d.length + ',' + i + ',' + d[i]));
d = _.continued_fraction(d, i);
if (d[1] < 0) {
d[0] = -d[0];
d[1] = -d[1];
}
if (false)
library_namespace.debug('→ ' + d[0] + '/' + d[1]);
// [ {Integer}±numerator, {Natural}denominator ]
return [ d[0], d[1], d[0] / d[1] - number ];
};
// 正規化帶分數。 to_mixed_fraction
// 2019/7/10 14:22:6
// mixed_fraction =
// [ {Integer}±whole, {Integer}±numerator, {Natural|Undefined}denominator ]
function normalize_mixed_fraction(mixed_fraction) {
if (typeof mixed_fraction === 'number') {
// treat as float
mixed_fraction = [ mixed_fraction ];
}
var whole = mixed_fraction[0] || ABSORBING_ELEMENT;
var numerator = mixed_fraction[1] || ABSORBING_ELEMENT;
var denominator = mixed_fraction[2] || MULTIPLICATIVE_IDENTITY;
// {Natural}denominator >= 1
if (denominator < 0) {
denominator = -denominator;
numerator = -numerator;
}
// {Natural}denominator ∈ ℤ
if (!Number.isInteger(denominator)) {
// assert: is float
denominator = _.to_rational_number(denominator);
numerator *= typeof numerator === 'bigint' ? BigInt(denominator[1])
: denominator[1];
denominator = denominator[0];
}
// {Integer}±numerator ∈ ℤ
if (!Number.isInteger(numerator)) {
// assert: is float
numerator = _.to_rational_number(numerator);
denominator *= typeof denominator === 'bigint' ? BigInt(numerator[1])
: numerator[1];
numerator = numerator[0];
}
// assert: {Natural}denominator, {Integer}±numerator
// TODO: 約分here。
var using_bigint;
// {Integer}±whole ∈ ℤ
if (typeof whole === 'number' && !Number.isInteger(whole)) {
// assert: whole is float
whole = _.to_rational_number(whole);
var LCM = _.LCM(denominator, whole[1]);
if (typeof LCM === 'bigint') {
using_bigint = true;
// convert all numbers to the same type.
numerator = BigInt(numerator);
denominator = BigInt(denominator);
whole = whole.map(BigInt);
}
numerator = numerator
// (LCM / denominator) === GCD * whole[1]
* (LCM / denominator)
+ (whole[0] < 0 ? -(-whole[0] % whole[1]) : whole[0]
% whole[1]) * (LCM / whole[1]);
denominator = LCM;
if (using_bigint) {
whole = whole[0] / whole[1];
} else {
whole = whole[0] < 0 ? -Math.floor(-whole[0] / whole[1]) : Math
.floor(whole[0] / whole[1]);
}
}
// TODO: convert all numbers to the same type.
// whole, numerator 必須同符號。
if (whole * numerator < 0) {
if (whole < 0) {
whole++;
// numerator > 0
numerator -= denominator;
} else {
whole--;
// numerator < 0
numerator += denominator;
}
}
// 處理假分數。同時會處理絕對值為整數之問題。
if (Math.absolute(numerator) >= denominator) {
whole += using_bigint ? numerator / denominator : Math
.floor(numerator / denominator);
numerator %= denominator;
}
// 約分。
if (numerator == ABSORBING_ELEMENT) {
// normalize
denominator = MULTIPLICATIVE_IDENTITY;
} else {
var GCD = _.GCD(numerator, denominator);
if (GCD >= 2) {
if (using_bigint)
GCD = BigInt(GCD);
numerator /= GCD;
denominator /= GCD;
}
}
// export
mixed_fraction = Object.assign([ whole, numerator, denominator ], {
valueOf : mixed_fraction_valueOf,
toString : mixed_fraction_toString
});
return mixed_fraction;
}
function mixed_fraction_valueOf() {
if (!this[1])
return this[0];
return this[0] + this[1] / this[2];
}
function mixed_fraction_toString() {
if (!this[1])
return String(this[0]);
if (!this[0])
return this[1] + '/' + this[2];
return this[0] + (this[1] < 0 ? '' : '+') + this[1] + '/' + this[2];
}
_.normalize_mixed_fraction = normalize_mixed_fraction;
// ------------------------------------------------------------------------
// 正規化數字成 integer 或 bigint
// 在大量計算前,盡可能先轉換成普通 {Number} 以加快速度。
// cohandler(may convert to number)
function to_int_or_bigint(value, cohandler) {
var number;
if (typeof value === 'bigint') {
number = Number(value);
if (Number.isSafeInteger(number)) {
cohandler && cohandler(true);
return number;
} else {
cohandler && cohandler(false);
return value;
}
}
// 這方法無法準確處理像 `1e38/7`, `10/7` 這樣的情況。
if (typeof value === 'number') {
number = Math.round(value);
if (!Number.isSafeInteger(number)) {
throw new RangeError('Cannot convert number ' + value
+ ' to safe integer!');
}
cohandler && cohandler(true);
return Math.round(number);
}
number = parseInt(value);
if (Number.isSafeInteger(number)) {
cohandler && cohandler(true);
return number;
}
if (!has_bigint)
throw new RangeError('Cannot convert ' + number
+ ' to safe integer!');
cohandler && cohandler(false);
return BigInt(value);
}
// Let all elements of {Array}this the same type: int, else bigint.
// 可能的話應該將絕對值最大的數字放在前面,早點判別出是否需要用 {BigInt}。
function array_to_int_or_bigint() {
// assert: {Array}this
// cache int values
if (this.some(function(value, index) {
value = to_int_or_bigint(value);
this[index] = value;
return typeof value === 'bigint';
}, this)) {
// must using bigint
this.forEach(function(value, index) {
this[index] = BigInt(value);
}, this);
}
// assert: all elements of `this` is in the same type.
// typeof this[0] === typeof this[1]
return this;
}
// 可用於 {BigInt} 之 Math.abs
// https://en.wikipedia.org/wiki/Absolute_value
function absolute(value) {
return value < 0 ? -value : value;
}
Math.absolute = absolute;
/**
* 求多個數之 GCD(Greatest Common Divisor, 最大公因數/公約數).<br />
* Using Euclidean algorithm(輾轉相除法).<br />
*
* TODO: 判斷互質.
*
* @param {Integers}number_array
* number array
*
* @returns {Natural} GCD of the numbers specified
*/
function GCD(number_array) {
if (arguments.length > 1) {
// Array.from()
number_array = Array.prototype.slice.call(arguments);
}
// 正規化數字。
number_array = number_array.map(function(value) {
return Math.absolute(to_int_or_bigint(value));
})
// 由小至大排序可以減少計算次數?? 最起碼能夠延後使用 {BigInt} 的時機。
.sort(library_namespace.general_ascending)
// .unique_sorted()
;
// console.log(number_array);
// 不在此先設定 gcd = number_array[0],是為了讓每個數字通過資格檢驗。
var index = 0, length = number_array.length, gcd = 0, remainder, number;
// assert: 所有數字皆已先轉換成數字,並已轉為絕對值。
while (index < length) {
number = number_array[index++];
if (number >= 1) {
gcd = number;
break;
}
}
// console.log(gcd);
while (index < length && 2 <= gcd) {
number = number_array[index++];
if (!(number >= 1))
continue;
if (typeof number === 'bigint') {
number %= BigInt(gcd);
// [ gcd, number ] = [ gcd, number ].to_int_or_bigint();
remainder = [ gcd, number ].to_int_or_bigint();
gcd = remainder[0];
number = remainder[1];
}
// assert: typeof gcd === typeof number
// console.log([ gcd, number ]);
// Euclidean algorithm 輾轉相除法。
while ((remainder = number % gcd) >= 1) {
number = gcd;
// 使用絕對值最小的餘數。為了要處理 {BigInt},因此不採用 Math.min()。
// gcd = Math.min(remainder, gcd - remainder);
gcd = gcd - remainder < remainder ? gcd - remainder : remainder;
}
}
if (typeof gcd === 'bigint'
&& Number.isSafeInteger(number = Number(gcd))) {
gcd = number;
}
return gcd;
}
_// JSDT:_module_
.GCD = GCD;
_// JSDT:_module_
.
/**
* 求多個數之 LCM(Least Common Multiple, 最小公倍數): method 1.<br />
* Using 類輾轉相除法.<br />
*
* @param {Integers}number_array
* number array
*
* @returns {Natural} LCM of the numbers specified
*/
LCM = function LCM(number_array) {
if (arguments.length > 1) {
// Array.from()
number_array = Array.prototype.slice.call(arguments);
}
// 正規化數字。
number_array = number_array.map(function(value) {
return Math.absolute(to_int_or_bigint(value));
})
// .sort().reverse()
;
if (number_array.some(function(number) {
return number == 0;
})) {
// 允許 0:
return 0;
}
var lcm = number_array[0];
for (var index = 1, length = number_array.length; index < length; index++) {
var number = number_array[index];
// assert: {Integer}number
var gcd = _.GCD(number, lcm);
if (typeof number === typeof gcd) {
number /= gcd;
if (typeof number === typeof lcm) {
gcd = lcm * number;
if (Number.isSafeInteger(gcd)) {
lcm = gcd;
} else if (has_bigint) {
lcm = BigInt(lcm) * BigInt(number);
} else {
throw new RangeError('LCM is not safe integer!');
}
} else {
// assert: {BigInt}number or {BigInt}lcm
lcm = BigInt(lcm) * BigInt(number);
}
} else {
// assert: {BigInt}number, {Number}gcd
lcm = BigInt(lcm) * (number / BigInt(gcd));
}
}
return lcm;
};
_// JSDT:_module_
.
/**
* 求多個數之 LCM(Least Common Multiple, 最小公倍數): method 1.<br />
* Using 類輾轉相除法.<br />
*
* TODO: 更快的方法: 短除法? 一次算出 GCD, LCM?
*
* @param {Integers}number_array
* number array
*
* @returns {Natural} LCM of the numbers specified
*/
LCM3 = function LCM3(number_array) {
if (arguments.length > 1) {
// Array.from()
number_array = Array.prototype.slice.call(arguments);
}
// 正規化數字。
number_array = number_array.map(function(value) {
return Math.absolute(to_int_or_bigint(value));
})
// .sort().reverse()
;
if (number_array.some(function(number) {
return number == 0;
})) {
// 允許 0:
return 0;
}
var lcm = number_array[0], using_bigint;
for (var index = 1, length = number_array.length; index < length; index++) {
var number = number_array[index];
// assert: {Integer}number
if (typeof number !== typeof lcm) {
// assert: number, lcm 有一個是 bigint。
using_bigint = true;
lcm = BigInt(lcm);
number = BigInt(number);
}
// console.log([ lcm, number ]);
var number0 = number;
var lcm0 = lcm;
// 倒反版的 Euclidean algorithm 輾轉相除法.
// 反覆讓兩方各自加到比對方大的倍數,當兩者相同時,即為 lcm。
while (lcm !== number) {
// console.log([ lcm0, number0, lcm, number ]);
if (lcm > number) {
var remainder = -lcm % number0;
if (remainder) {
number = lcm + remainder + number0;
if (!using_bigint && has_bigint
&& !Number.isSafeInteger(number)) {
using_bigint = true;
number0 = BigInt(number0);
lcm0 = BigInt(lcm0);
number = BigInt(lcm + remainder) + number0;
lcm = BigInt(lcm);
}
} else {
// number0 整除 lcm: 取 lcm 即可.
break;
}
} else {
var remainder = -number % lcm0;
if (remainder) {
lcm = number + remainder + lcm0;
if (!using_bigint && has_bigint
&& !Number.isSafeInteger(lcm)) {
using_bigint = true;
number0 = BigInt(number0);
lcm0 = BigInt(lcm0);
lcm = BigInt(number + remainder) + BigInt(lcm0);
number = BigInt(number);
}
} else {
// lcm0 整除 number: 取 number 即可.
lcm = number;
break;
}
}
}
}
return lcm;
};
_// JSDT:_module_
.
/**
* 求多個數之 LCM(Least Common Multiple, 最小公倍數): method 2.<br />
* Using 類輾轉相除法.<br />
*
* @param {Integers}number_array
* number array
*
* @returns {Integer} LCM of the numbers specified
*/
LCM2 = function LCM2(number_array) {
if (arguments.length > 1) {
// Array.from()
number_array = Array.prototype.slice.call(arguments);
}
var i = 0, l = number_array.length, lcm = 1, r, n, num, gcd;
for (; i < l && lcm; i++) {
// 每個數字都要做運算,雖可確保正確,但沒有效率!
if (!isNaN(num = n = Math.abs(parseInt(number_array[i])))) {
gcd = lcm;
// Euclidean algorithm.
while (r = n % gcd)
n = gcd, gcd = r;
lcm = num / gcd * lcm;
}
}
return lcm;
};
/**
* Get <a href="https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm"
* accessdate="2013/8/3 19:45">Extended Euclidean algorithm</a><br />
*
* @param {Integer}n1
* number 1
* @param {Integer}n2
* number 2
* @returns [ GCD, m1, m2 ]: GCD = m1 * n1 + m2 * n2
*
* @see division_with_remainder() @ data.math
* @since 2013/8/3 20:24:30
*/
function extended_GCD(n1, n2) {
var remainder, quotient, using_g1 = false, using_bigint,
// 前一group [dividend 應乘的倍數, divisor 應乘的倍數]
m1g1 = 1, m2g1 = 0;
if (typeof n1 === 'bigint' || typeof n2 === 'bigint') {
// convert all numbers to the same type.
n1 = BigInt(n1);
n2 = BigInt(n2);
m1g1 = BigInt(m1g1);
m2g1 = BigInt(m2g1);
using_bigint = true;
}
// 前前group [dividend 應乘的倍數, divisor 應乘的倍數]
var m1g2 = /* 0 */m2g1, m2g2 = /* 1 */m1g1;
while (remainder = n1 % n2) {
quotient = (n1 - remainder) / n2;
if (!using_bigint) {
// assert: typeof quotient === 'number'
quotient = Math.floor(quotient);
}
// 現 group = remainder = 前前group - quotient * 前一group
if (using_g1 = !using_g1)
m1g1 -= quotient * m1g2, m2g1 -= quotient * m2g2;
else
m1g2 -= quotient * m1g1, m2g2 -= quotient * m2g1;
// swap numbers
n1 = n2;
n2 = remainder;
}
return using_g1 ? [ n2, m1g1, m2g1 ] : [ n2, m1g2, m2g2 ];
}
// extended GCD algorithm
_.EGCD = extended_GCD;
/**
* 帶餘除法 Euclidean division。<br />
* 除非設定 closest,否則預設 remainder ≥ 0.
*
* @param {Number}dividend
* 被除數。
* @param {Number}divisor
* 除數。
* @param {Boolean}[closest]
* get the closest quotient
*
* @returns {Array} [ {Integer}quotient 商, {Number}remainder 餘數 ]
*
* @see http://stackoverflow.com/questions/14997165/fastest-way-to-get-a-positive-modulo-in-c-c
* @see extended_GCD() @ data.math
*
* @since 2015/10/31 10:4:45
*/
function division_with_remainder(dividend, divisor, closest) {
if (false)
return [ Math.floor(dividend / divisor),
// 轉正。保證餘數值非負數。
(dividend % divisor + divisor) % divisor ];
var remainder = dividend % divisor;
if (closest) {
if (remainder != 0
// 0 !== 0n
&& Math.absolute(remainder + remainder) > Math.absolute(divisor))
if (remainder < 0)
remainder += Math.absolute(divisor);
else
remainder -= Math.absolute(divisor);
} else if (remainder < 0) {
// assert: (-0 < 0) === false
remainder += Math.absolute(divisor);
}
dividend = (dividend - remainder) / divisor;
if (typeof dividend === 'number') {
dividend = Math.round(dividend);
} else {
// assert: typeof dividend === 'bigint'
}
return [ dividend, remainder ];
}
// 帶餘數除法 division with remainder
_.division = division_with_remainder;
/**
* 取得所有分母為 denominator,分子分母互質的循環小數的循環節位數。<br />
* Repeating decimal: get period (repetend length)
*
* @param {Natural}denominator
* 分母
* @param {Boolean}with_transient
* 亦取得非循環節部分位數
* @param {Natural}min
* 必須最小長度,在測試大量數字時使用。若發現長度必小於 min 則即時跳出。效果不俗 (test
* Euler_26(1e7))。
*
* @returns {Array}[{Number}period length 循環節位數 < denominator,
* {Number}transient 非循環節部分位數 ]
*
* @see https://en.wikipedia.org/wiki/Repeating_decimal#Reciprocals_of_composite_integers_coprime_to_10
*/
function period_length(denominator, with_transient, min) {
// 去除所有 2 或 5 的因子。
var non_repeating = 0, non_repeating_5 = 0;
while (denominator % 5 === 0)
denominator /= 5, non_repeating_5++;
while (denominator % 2 === 0)
denominator /= 2, non_repeating++;
if (non_repeating < non_repeating_5)
non_repeating = non_repeating_5;
if (denominator === 1 || denominator <= min)
return with_transient ? [ 0, non_repeating ] : 0;
for (var length = 1, remainder = 1;; length++) {
remainder = remainder * DEFAULT_BASE % denominator;
if (remainder === 1)
return with_transient ? [ length, non_repeating ] : length;
}
}
_.period_length = period_length;
// ---------------------------------------------------------------------//
/**
* 從數集 set 中挑出某些數,使其積最接近指定的數 target。<br />
* To picks some numbers from set, so the product is approximately the
* target number.
*
* TODO: improve/optimize
*
* @param {Array}set
* number set of {Natural}
* @param {Natural}target
* target number
* @param {Object}[options]
* 附加參數/設定特殊功能與選項
*
* @returns {Array}某些數,其積最接近 target。
*
* @see http://stackoverflow.com/questions/19572043/given-a-target-sum-and-a-set-of-integers-find-the-closest-subset-of-numbers-tha
*/
function closest_product(set, target, options) {
var status, minor_data;
if (Array.isArray(options)) {
status = options;
minor_data = status[0];
options = minor_data.options;
} else {
// 初始化
if (!options)
options = new Boolean;
else if (typeof options === 'number')
options = {
direction : options
};
else if (typeof options === 'boolean')
options = {
sorted : options
};
minor_data = [ Infinity ];
minor_data.options = options;
// status = [ [minor, set of minor], product, set of product ]
status = [ minor_data, ZERO_EXPONENT, [] ];
if (!options.sorted)
set = set.clone()
// 由小至大排序。
.sort(library_namespace.ascending);
}
// direction = -1: 僅接受小於 target 的積。
// direction = +1: 僅接受大於 target 的積。
var direction = options.direction,
//
product = status[1], selected = status[2];
set.some(function(natural, index) {
if (selected[index])
// 已經處理過,跳過。
return;
var _product = product * natural, _selected,
/** {Number}差 ≥ 0 */
difference = Math.abs(target - _product),
// 是否發現新極小值。採用 minor_data 而不 cache 是因為此間 minor_data 可能已經改變。
_status = difference <= minor_data[0];
library_namespace.debug(target + '=' + (target - _product) + '+'
+ natural + '×' + product + ', ' + product + '='
+ (set.filter(function(n, index) {
return selected[index];
}).join('⋅') || 1), 6, 'closest_product');
if (target < _product) {
library_namespace.debug('target < _product, direction: '
+ direction, 6, 'closest_product');
if (!_status || direction < 0) {
library_namespace.debug('積已經過大,之後不會有合適的。', 5,
'closest_product');
return true;
}
}
_selected = selected.clone();
_selected[index] = true;
if (_status && (!(direction > 0) || target <= _product)) {
_status = set.filter(function(n, index) {
return _selected[index];
}).join(closest_product.separator);
if (difference === minor_data[0]) {
if (minor_data.includes(_status)) {
library_namespace.debug('已經處理過相同的,跳過。', 5,
'closest_product');
return;
}
minor_data.push(_status);
} else {
minor_data.clear();
minor_data.push(difference, _status);
}
library_namespace.debug('發現極小值:' + target + '=' + difference
+ '+' + natural + '×' + product + ', ' + product + '='
+ (set.filter(function(n, index) {
return selected[index];
}).join('⋅') || 1), 3, 'closest_product');
}
_status = [ minor_data, _product, _selected ];
library_namespace.debug('繼續探究是否有更小的差:' + _status.join(';'), 4,
'closest_product');
closest_product(set, target, _status);
});
return minor_data.length > 1 && minor_data;
}
closest_product.separator = MULTIPLICATION_SIGN;
_.closest_product = closest_product;
// TODO:將數列分為積最接近的兩組。
/**
* Get <a
* href="https://en.wikipedia.org/wiki/Modular_multiplicative_inverse"
* accessdate="2013/8/3 20:10">modular multiplicative inverse</a> (模反元素)
*
* TODO:<br />
* untested!
*
* @param {Integer}number
* number
* @param {Integer}modulo
* modulo
*
* @returns {Integer} modular multiplicative inverse
*
* @since 2013/8/3 20:24:30
*/
function modular_inverse(number, modulo) {
number = extended_GCD(number, modulo);
if (number[0] == 1)
return (number = number[1]) < 0 ? number + modulo : number;
}
_.modular_inverse = modular_inverse;
// factorial_cache[ n ] = n!
// factorial_cache = [ 0! = 1, 1!, 2!, ... ]
var factorial_cache = [ 1 ], factorial_cache_to;
/**
* Get the factorial (階乘) of (natural).<br />
*
* @param {ℕ⁰:Natural+0}natural
* safe integer. 0–18
*
* @returns {Natural}natural的階乘.
*
* @see https://en.wikipedia.org/wiki/Factorial
*/
function factorial(natural) {
var length = factorial_cache.length;
if (length <= natural && !factorial_cache_to) {
var f = factorial_cache[--length];
while (length++ < natural)
if (isFinite(f *= length))
factorial_cache.push(f);
else {
factorial_cache_to = length - 1;
break;
}
}
return natural < length ? factorial_cache[natural] : Infinity;
}
// var factorial_map = CeL.math.factorial.map(9);
// generate factorial map
factorial.map = function(natural) {
if (!natural)
natural = 9;
if (factorial_cache.length <= natural && !factorial_cache_to)
factorial(natural);
return factorial_cache.slice(0, natural + 1);
};
_.factorial = factorial;
// ---------------------------------------------------------------------//
/**
* http://www.math.umbc.edu/~campbell/NumbThy/Class/Programming/JavaScript.html
* http://aoki2.si.gunma-u.ac.jp/JavaScript/
*/
/**
* 得到開方數,相當於 Math.floor(Math.sqrt(number)) === Math.sqrt(number) | 0. get
* integer square root. TODO: use 牛頓法
*
* @param {Number}
* positive number
*
* @return r, r^2 ≤ number < (r+1)^2
*
* @see <a href="http://www.azillionmonkeys.com/qed/sqroot.html"
* accessdate="2010/3/11 18:37">Paul Hsieh's Square Root page</a><br />
* <a
* href="http://www.embeddedrelated.com/usenet/embedded/show/114789-1.php"
* accessdate="2010/3/11 18:34">Suitable Integer Square Root Algorithm
* for 32-64-Bit Integers on Inexpensive Microcontroller? |
* Comp.Arch.Embedded | EmbeddedRelated.com</a>
*/
function floor_sqrt(number) {
// return Math.sqrt(number) | 0;
if (!Number.isFinite(number = Math.floor(number)))
return;
var g = 0, v, h, t;
while ((t = g << 1) < (v = number - g * g)) {
// library_namespace.debug(t + ', ' + v);
h = 1;
while (h * (h + t) <= v)
// 因為型別轉關係,還是保留 << 而不用 *2
h <<= 1;// h *= 2;
g += h >> 1;// h / 2;//
}
if (false)
library_namespace.debug('end: ' + t + ', ' + v);
return g;
}
_.floor_sqrt = floor_sqrt;
// count digits of integer: using .digit_length()
function ceil_log(number, base) {
if (!number)
return 0;
if (!base)
base = DEFAULT_BASE;
// assert: base >= 2, base === (base | 0)
number = Math.abs(number);
// ideal
return Math.ceil(base === 10 ? Math.log10(number) : base === 2 ? Math
.log2(number)
// TODO: base = 2^n
: Math.log(number) / Math.log(base));
// slow... should use multiply by exponents
// Logarithm
var log = 0;
if (number < ZERO_EXPONENT) {
while (number < ZERO_EXPONENT) {
number *= base;
if (false)
library_namespace.debug(number);
log--;
}
if (number !== ZERO_EXPONENT)
// 修正。
log++;
} else {
while (number > ZERO_EXPONENT) {
// 因為可能損失 base^exp + (...) 之剩餘部分,因此不能僅採用 Math.floor(number /
// base)
// 但如此較費時。
number /= base;
if (false)
library_namespace.log(number);
log++;
}
}
return log;
}
// add binding
_.ceil_log = ceil_log;
/** {Object}all possible last 2 digits of square number */
var square_ending = Object.create(null);
[ 0, 1, 4, 9, 16, 21, 24, 25, 29, 36, 41, 44, 49, 56, 61, 64, 69, 76, 81,
84, 89, 96 ].forEach(function(n) {
square_ending[n] = null;
});
// Squarity testing
// 檢測 ({Natural}number) 是否為完全平方數
// a square number or perfect square. TODO: use 牛頓法
// is square number, n²
function is_square(number) {
// 快速判定 possible_square(number)
// https://www.johndcook.com/blog/2008/11/17/fast-way-to-test-whether-a-number-is-a-square/
// 0x0213 = parseInt('1111110111101100', 2).toString(0x10)
if (0xFDEC & (1 << (number & 0xF))) {
return false;
}
// TRUE only if number % 16 === 0, 1, 4, 9
// %16 有4個: http://oeis.org/A023105
if (!((number % 100) in square_ending)) {
return false;
}
number = Math.sqrt(number);
return number === (number | 0) && number;
// another method
// https://en.wikipedia.org/wiki/Methods_of_computing_square_roots
// https://gmplib.org/manual/Perfect-Square-Algorithm.html
var sqrt = floor_sqrt(number);
return sqrt * sqrt === number && sqrt;
}
_.is_square = is_square;
// 檢測 ({Natural}f1 * {Natural}f2) 是否為完全平方數
function product_is_square(f1, f2) {
if (f1 === f2) {
return true;
// e.g., r * p^2, r * p^2
}
// e.g., p^2 * r, q^2 * r
var product = f1 * f2;
if (Number.isSafeInteger(product)) {
return is_square(product);
}
// 除法不比較快。
if (f1 > f2) {
// swap
var tmp = f1;
f1 = f2;
f2 = tmp;
}
if (!Number.isSafeInteger(f2)) {
library_namespace.error('The number ' + f2
+ ' is NOT a safe number!');
}
// assert: f1 < f2
if (f2 % f1 === 0) {
// e.g., r, r * p^2
return is_square(f2 / f1);
}
if (is_square(f1)) {
// e.g., p^2, q^2
return is_square(f2);
}
if (is_square(f2)) {
return false;
}
for (var index = 0, length = Math.min(10, primes.length); index < length; index++) {
var p = primes[index], p2 = p * p;
tmp = false;
while (f1 % p2 === 0) {
f1 /= p2;
tmp = true;
}
while (f2 % p2 === 0) {
f2 /= p2;
tmp = true;
}
if (f1 % p === 0) {
while (f1 % p === 0 && f2 % p === 0) {
f1 /= p;
f2 /= p;
tmp = true;
}
}
if (tmp) {
product = f1 * f2;
if (Number.isSafeInteger(product)) {
return is_square(product);
}
}
}
// 找GCD。較慢,但沒辦法。
var gcd = GCD(f1, f2);
return is_square(f1 / gcd)
//
&& is_square(f2 / gcd);
}
_.product_is_square = product_is_square;
/**
* <code>
n(n+1)/2=T, n∈ℕ, n=?
n(n+1)/2=T, n=?
n = 1/2 (sqrt(8 T+1)-1)
Reduce[(n (1 + n))/2 == T, n]
n = 1/2 (sqrt(8 T+1)-1)
Reduce[n(3n−1)/2==P, n]
n = 1/6 (sqrt(24 P+1)+1)
// hexagonal
Reduce[n(2n−1)==H, n]
n = 1/4 (sqrt(8 H+1)+1)
</code>
*/
function is_triangular(natural) {
// https://en.wikipedia.org/wiki/Triangular_number
var sqrt = is_square(8 * natural + 1);
return sqrt && sqrt % 2 === 1;
}
_.is_triangular = is_triangular;
function is_generalized_pentagonal(generalized) {
// https://en.wikipedia.org/wiki/Pentagonal_number
return is_square(24 * generalized + 1);
}
_.is_generalized_pentagonal = is_generalized_pentagonal;
function is_pentagonal(natural) {
// https://en.wikipedia.org/wiki/Pentagonal_number
var sqrt = is_square(24 * natural + 1);
return sqrt && sqrt % 6 === 5;
}
_.is_pentagonal = is_pentagonal;
// 素勾股數 primitive Pythagorean triple
var 素勾股數 = [], last_Pythagorean_m = 2;
/**
* primitive Pythagorean triples 素勾股數組/素商高數組/素畢氏三元數
*
* @param {Natural}limit
* limit of m. 若欲改成 limit of 斜邊,請輸入斜邊長後自行 filter。
*
* @returns {Array}primitive Pythagorean triple list
*
* @see https://en.wikipedia.org/wiki/Pythagorean_triple#Generating_a_triple
* The triple generated by Euclid's formula is primitive if and only if
* m and n are coprime and m − n is odd.
*/
function Pythagorean_list(limit) {
if (last_Pythagorean_m < limit) {
for (var m = last_Pythagorean_m; m < limit; m++) {
for (var m2 = m * m, m_2 = 2 * m, n, n2 = n = m % 2 === 0 ? 1
: 0;
// 設 m > n 互質且均是正整數,m 和 n 有一個是偶數,
// 計算出來的 (a, b, c) 就是素勾股數。所有素勾股數可用列式找出
n < m; n += 2, n2 = n * n) {
if (GCD(m, n) === 1) {
var a = m2 - n2, b = m_2 * n, c = m2 + n2;
// let a < b < c
素勾股數.push(a < b ? [ a, b, c ] : [ b, a, c ]);
}
}
}
last_Pythagorean_m = limit;
}
return 素勾股數;
}
_.Pythagorean_list = Pythagorean_list;
// ---------------------------------------------------------------------//
// Catalan_number[0] = 1
var Catalan_number_list = [ 1 ];
// Catalan numbers
// @see https://en.wikipedia.org/wiki/Catalan_number
function Catalan_number(NO) {
if (NO < Catalan_number_list.length) {
// use cache
return Catalan_number_list[NO];
}
var n = Catalan_number_list.length - 1,
//
this_Catalan_number = Catalan_number_list[n];
for (; n < NO; n++) {
this_Catalan_number = this_Catalan_number * (4 * n + 2) / (n + 2);
Catalan_number_list.push(this_Catalan_number);
}
return this_Catalan_number;
}
_.Catalan_number_list = Catalan_number_list;
_.Catalan_number = Catalan_number;
// ---------------------------------------------------------------------//
/** {Array}Collatz_conjecture_steps[number] = steps. cache 以加快速度。 */
var Collatz_conjecture_steps_cache = [ , 1 ];
if (false) {
// 此法費時 1.5 倍, 12s → 19s
Collatz_conjecture_steps_cache = new Array(1000001);
Collatz_conjecture_steps_cache[1] = 1;
}
// assert: Collatz_conjecture_steps_cache[1] === 1 (因程式判別方法需要此項)
// Collatz conjecture
// https://en.wikipedia.org/wiki/Collatz_conjecture
function Collatz_conjecture(natural) {
if (!(natural > 0))
return;
var chain = [ natural ];
while (natural > 1) {
chain.push(natural % 2 === 0 ? natural /= 2
: (natural = natural * 3 + 1));
}
// Collatz_conjecture_steps_cache[natural] = chain.length;
// return all terms
return chain;
}
// 為計算 steps 特殊化。
// assert: CeL.Collatz_conjecture.steps(natural) ===
// CeL.Collatz_conjecture(natural).length
function Collatz_conjecture_steps(natural) {
if (!(natural > 0))
return;
var chain = [];
while (!(natural in Collatz_conjecture_steps_cache)) {
chain.push(natural);
if (natural % 2 === 0)
natural /= 2;
else
natural = natural * 3 + 1;
}
var steps = Collatz_conjecture_steps_cache[natural] + chain.length, s = steps;
// 紀錄 steps。
chain.forEach(function(natural) {
Collatz_conjecture_steps_cache[natural] = s--;
});
return steps;
}
/**
* <code>
backwards 反向:
1000000: 153 steps
999999: 259 steps
999667: 290 steps
999295: 396 steps
997823: 440 steps
970599: 458 steps
939497: 507 steps
837799: 525 steps
</code>
*/
// search the longest chain / sequence below ((natural))
function Collatz_conjecture_longest(natural) {
if (!(natural > 0))
return;
// maximum steps
var max_steps = 0, max_steps_natural;
// brute force
for (var n = 1, steps, _n; n <= natural; n++) {
if (n in Collatz_conjecture_steps_cache)
steps = Collatz_conjecture_steps_cache[_n = n];
else {
steps = Collatz_conjecture_steps(_n = n);
// 預先快速處理所有 2倍數字。採用此方法,約可增加 5% 速度。不採用此方法,n 正反向速度差不多。
while (_n * 2 <= natural) {
Collatz_conjecture_steps_cache[_n *= 2] = ++steps;
}
}
if (max_steps < steps) {
library_namespace.debug(natural + ': ' + steps + ' steps', 3,
'Collatz_conjecture.longest');
max_steps = steps;
max_steps_natural = _n;
}
}
return [ max_steps_natural, max_steps ];
}
_.Collatz_conjecture = Collatz_conjecture;
Collatz_conjecture.steps = Collatz_conjecture_steps;
Collatz_conjecture.longest = Collatz_conjecture_longest;
// ---------------------------------------------------------------------//
// https://en.wikipedia.org/wiki/Memoization
/** {Array}質數列表。 cache / memoization 以加快速度。 */
var primes = [ 2, 3, 5 ],
/**
* last prime tested.<br />
* assert: last_prime_tested is ((6n ± 1)). 因此最起碼應該從 5 開始。
*
* @type {Natural}
*/
last_prime_tested = primes.at(-1);
// https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
// the sieve of Eratosthenes 篩法
function prime_sieve(limit, limit_index) {
// var list = _.number_array(limit + 1, 0, Int8Array);
var list = new Array(limit + 1);
// 重建 re-build list (table)
primes.forEach(function(prime) {
for (var number = prime; number <= limit;) {
// list[number += prime] = 1;
list[number += prime] = true;
}
});
for (var n = last_prime_tested; n <= limit;) {
if (list[++n])
continue;
// n is prime
// library_namespace.debug(n + ' is prime');
primes.push(n);
if (limit_index && primes.length > limit_index)
break;
// 登記所有倍數。
for (var number = n; number <= limit;) {
// list[number += prime] = 1;
list[number += prime] = true;
}
}
last_prime_tested = primes.at(-1);
return primes;
}
_.prime_sieve = prime_sieve;
// integer: number to test
function test_is_prime(integer, index, sqrt) {
// assert: Number.isInteger(integer), integer ≥ 0
index |= 0;
if (!sqrt)
sqrt = floor_sqrt(integer);
// 採用試除法, use trial division。
// 從第一個質數一直除到 ≤ sqrt(integer) 之質數
for (var prime, length = primes.length; index < length;) {
if (integer % (prime = primes[index++]) === 0)
// return: prime factor found
return integer === prime ? false : prime;
if (sqrt < prime)
return false;
}
// 質數列表中的質數尚無法檢測 integer。
}
/**
* Get the prime[index] or prime list.
*
* @param {Natural}[index]
* prime index starts from 1
* @param {Natural}[limit]
* the upper boundary of prime value
*
* @returns {Natural}prime value
*/
function prime(index, limit) {
if (!(index > 0)) {
if (limit > 0) {
index = prime_pi(limit);
return primes.slice(0, index);
}
return primes;
}
if (primes.length < index) {
if (false && index - primes.length > 1e6) {
// using the sieve of Eratosthenes 篩法
// 沒比較快。
// 詳細數量應採 prime π(x)。
// https://zh.wikipedia.org/wiki/%E8%B3%AA%E6%95%B8%E5%AE%9A%E7%90%86
prime_sieve(limit || index * 10, index);
} else {
// assert: last_prime_tested is ((6n ± 1))
/**
* {Boolean}p1 === true: last_prime_tested is 6n+1.<br />
* else: last_prime_tested is 6n-1
*/
var p1 = last_prime_tested % 6 === 1;
for (; primes.length < index
&& Number.isSafeInteger(last_prime_tested);) {
last_prime_tested += (p1 = !p1) ? 2 : 4;
// 實質為 https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
if (!test_is_prime(last_prime_tested, 2))
primes.push(last_prime_tested);
if (limit && limit <= last_prime_tested)
break;
}
library_namespace.debug('last prime tested = '
+ last_prime_tested, 2, 'prime');
}
}
return primes[index - 1];
}
_.prime = prime;
// prime #5484598 = 94906249, the biggest prime <
// Math.sqrt(Number.MAX_SAFE_INTEGER) - 1.
// the 2nd biggest prime is 94906247.
// CeL.prime(CeL.prime_pi(Number.MAX_SAFE_INTEGER = 2^53 - 1)) =
// 9007199254740881
function prime_pi(value) {
value = Math.floor(Math.abs(value));
if (last_prime_tested < value)
prime(value, value);
// +1: index of function prime() starts from 1!
return primes.search_sorted(value, true) + 1;
}
_.prime_pi = prime_pi;
/**
* Get the primorial (質數階乘, p_n#) of (NO).<br />
*
* @param {Natural}NO
* safe integer. 1–13
*
* @returns {Natural}p_NO的質數階乘.
*
* @see https://en.wikipedia.org/wiki/Primorial
*/
function primorial(NO) {
if (!(NO >= 1))
return MULTIPLICATIVE_IDENTITY;
prime(NO);
var index = 0, product = MULTIPLICATIVE_IDENTITY;
while (index < NO)
product *= primes[index++];
return product;
}
/**
* Get the primorial (質數階乘, n#) of (natural).<br />
*
* @param {Natural}natural
* safe integer. 2–42
*
* @returns {Natural}natural的質數階乘.
*
* @see https://en.wikipedia.org/wiki/Primorial
*/
function primorial_natural(natural) {
// 2: primes[0]
if (!(natural >= 2))
return MULTIPLICATIVE_IDENTITY;
var index = 0, length = prime_pi(natural), product = MULTIPLICATIVE_IDENTITY;
while (index < length)
product *= primes[index++];
return product;
}
_.primorial = primorial;
primorial.natural = primorial_natural;
// return multiplicand × multiplier % modulus
// assert: 三者皆為 natural number, and Number.isSafeInteger() is OK.
// max(multiplicand, multiplier) < modulus. 否則會出現錯誤!
function multiply_modulo(multiplicand, multiplier, modulus) {
var quotient = multiplicand * multiplier;
if (Number.isSafeInteger(quotient))
return quotient % modulus;
// 避免 overflow
if (multiplicand > multiplier)
quotient = multiplicand, multiplicand = multiplier,
multiplier = quotient;
if (quotient === 1)
throw new Error('Please use data.math.integer instead!');
quotient = Math.floor(modulus / multiplicand);
quotient = (multiplicand * (multiplier % quotient) - Math
.floor(multiplier / quotient)
* (modulus % multiplicand))
% modulus;
return quotient;
}
_.multiply_modulo = multiply_modulo;
// return integer ^ exponent % modulus
// assert: 三者皆為 natural number, and Number.isSafeInteger() is OK. 否則會出現錯誤!
function power_modulo(integer, exponent, modulus) {
for (var remainder = 1, power = integer % modulus;;) {
if (exponent % 2 === 1)
remainder = multiply_modulo(remainder, power, modulus);
if ((exponent >>= 1) === 0)
return remainder;
if ((power = multiply_modulo(power, power, modulus)) === 1)
return remainder;
}
}
_.power_modulo = power_modulo;
function power_modulo(natural, exponent, modulus) {
var remainder = 1;
for (natural %= modulus; exponent > 0; natural = natural * natural
% modulus, exponent >>= 1)
if (exponent % 2 === 1)
remainder = remainder * natural % modulus;
return remainder;
}
_.power_modulo = power_modulo;
// Miller–Rabin primality test
// return true: is composite, undefined: probable prime (PRP) / invalid
// number
// https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test
function Miller_Rabin(natural, times) {
if (natural % 2 === 0)
return natural !== 2;
if (!(natural < sqrt_max_integer) || natural < 2)
return;
var n_1 = natural - 1,