src/math/core/Complex.js
/**
* The script is part of konpeito.
*
* AUTHOR:
* natade (http://twitter.com/natadea)
*
* LICENSE:
* The MIT license https://opensource.org/licenses/MIT
*/
import Polyfill from "../tools/Polyfill.js";
import Probability from "./tools/Probability.js";
import Random from "./tools/Random.js";
import Matrix from "./Matrix.js";
import BigInteger from "./BigInteger.js";
import BigDecimal from "./BigDecimal.js";
import Fraction from "./Fraction.js";
import MathContext from "./context/MathContext.js";
import KonpeitoFloat from "./base/KonpeitoFloat.js";
/**
* Complex type argument.
* - Complex
* - number
* - boolean
* - string
* - Array<number>
* - {_re:number,_im:number}
* - {doubleValue:number}
* - {toString:function}
*
* Initialization can be performed as follows.
* - 1200, "1200", "12e2", "1.2e3"
* - "3 + 4i", "4j + 3", [3, 4].
* @typedef {Complex|number|boolean|string|Array<number>|{_re:number,_im:number}|{doubleValue:number}|{toString:function}} KComplexInputData
*/
/**
* Random number generation class used within Complex.
* @type {Random}
* @ignore
*/
const random_class = new Random();
/**
* Collection of functions used in Complex.
* @ignore
*/
class ComplexTool {
/**
* Create data for complex numbers from strings.
* @param {string} text - Target strings.
* @returns {{real : number, imag : number}}
*/
static ToComplexFromString(text) {
let str = text.replace(/\s/g, "").toLowerCase();
str = str.replace(/infinity|inf/g, "1e100000");
// 複素数の宣言がない場合
if(!(/[ij]/.test(str))) {
return {
real : parseFloat(str),
imag : 0.0
};
}
// この時点で複素数である。
// 以下真面目に調査
let re = 0;
let im = 0;
let buff;
// 最後が$なら右側が実数、最後が[+-]なら左側が実数
buff = str.match(/[+-]?(([0-9]+(\.[0-9]+)?(e[+-]?[0-9]+)?)|(nan))($|[+-])/);
if(buff) {
re = parseFloat(buff[0]);
}
// 複素数は数値が省略される場合がある
buff = str.match(/[+-]?(([0-9]+(\.[0-9]+)?(e[+-]?[0-9]+)?)|(nan))?[ij]/);
if(buff) {
buff = buff[0].substring(0, buff[0].length - 1);
// i, +i, -j のように実数部がなく、数値もない場合
if((/^[-+]$/.test(buff)) || buff.length === 0) {
im = buff === "-" ? -1 : 1;
}
else {
im = parseFloat(buff);
}
}
return {
real : re,
imag : im
};
}
}
/**
* Complex number class. (immutable)
*/
export default class Complex extends KonpeitoFloat {
/**
* Create a complex number.
*
* Initialization can be performed as follows.
* - 1200, "1200", "12e2", "1.2e3"
* - "3 + 4i", "4j + 3", [3, 4].
* @param {KComplexInputData} number - Complex number. See how to use the function.
*/
constructor(number) {
super();
// 行列で使うためイミュータブルは必ず守ること。
if(arguments.length === 1) {
const obj = number;
if(obj instanceof Complex) {
/**
* The real part of this Comlex.
* @private
* @type {number}
*/
this._re = obj._re;
/**
* The imaginary part of this Comlex.
* @private
* @type {number}
*/
this._im = obj._im;
}
else if(typeof obj === "number") {
this._re = obj;
this._im = 0.0;
}
else if(typeof obj === "string") {
const x = ComplexTool.ToComplexFromString(obj);
this._re = x.real;
this._im = x.imag;
}
else if(obj instanceof Array) {
if(obj.length === 2) {
this._re = obj[0];
this._im = obj[1];
}
else {
throw "Complex Unsupported argument " + arguments;
}
}
else if(typeof obj === "boolean") {
this._re = obj ? 1 : 0;
this._im = 0.0;
}
else if("doubleValue" in obj) {
this._re = obj.doubleValue;
this._im = 0.0;
}
else if(("_re" in obj) && ("_im" in obj)) {
this._re = obj._re;
this._im = obj._im;
}
else if(obj instanceof Object) {
const x = ComplexTool.ToComplexFromString(obj.toString());
this._re = x.real;
this._im = x.imag;
}
else {
throw "Complex Unsupported argument " + arguments;
}
}
else {
throw "Complex Many arguments : " + arguments.length;
}
}
/**
* Create an entity object of this class.
* @param {KComplexInputData} number
* @returns {Complex}
*/
static create(number) {
if(number instanceof Complex) {
return number;
}
else {
return new Complex(number);
}
}
/**
* Convert number to Complex type.
* @param {KComplexInputData} number
* @returns {Complex}
*/
static valueOf(number) {
return Complex.create(number);
}
/**
* Convert to Complex.
* If type conversion is unnecessary, return the value as it is.
* @param {KComplexInputData} number
* @returns {Complex}
* @ignore
*/
static _toComplex(number) {
if(number instanceof Complex) {
return number;
}
else if(number instanceof Matrix) {
return Matrix._toComplex(number);
}
else {
return new Complex(number);
}
}
/**
* Convert to real number.
* @param {KComplexInputData} number
* @returns {number}
* @ignore
*/
static _toDouble(number) {
if(typeof number === "number") {
return number;
}
const complex_number = Complex._toComplex(number);
if(complex_number.isReal()) {
return complex_number.real;
}
else {
throw "not support complex numbers.[" + number + "]";
}
}
/**
* Convert to integer.
* @param {KComplexInputData} number
* @returns {number}
* @ignore
*/
static _toInteger(number) {
return Math.trunc(Complex._toDouble(number));
}
/**
* Deep copy.
* @returns {Complex}
*/
clone() {
return this;
}
/**
* Convert to string.
* @returns {string}
*/
toString() {
/**
* @type {function(number): string }
*/
const formatG = function(x) {
let numstr = x.toPrecision(6);
if(numstr.indexOf(".") !== -1) {
numstr = numstr.replace(/\.?0+$/, ""); // 1.00 , 1.10
numstr = numstr.replace(/\.?0+e/, "e"); // 1.0e , 1.10e
}
else if(/inf/i.test(numstr)) {
if(x === Number.POSITIVE_INFINITY) {
return "Inf";
}
else {
return "-Inf";
}
}
else if(/nan/i.test(numstr)) {
return "NaN";
}
return numstr;
};
if(!this.isReal()) {
if(this._re === 0) {
return formatG(this._im) + "i";
}
else if((this._im >= 0) || (Number.isNaN(this._im))) {
return formatG(this._re) + " + " + formatG(this._im) + "i";
}
else {
return formatG(this._re) + " - " + formatG(-this._im) + "i";
}
}
else {
return formatG(this._re);
}
}
/**
* Convert to JSON.
* @returns {string}
*/
toJSON() {
if(!this.isReal()) {
if(this._re === 0) {
return this._im.toString() + "i";
}
else if((this._im >= 0) || (Number.isNaN(this._im))) {
return this._re.toString() + "+" + this._im.toString() + "i";
}
else {
return this._re.toString() + this._im.toString() + "i";
}
}
else {
return this._re.toString();
}
}
/**
* The real part of this Comlex.
* @returns {number} real(A)
*/
get real() {
return this._re;
}
/**
* The imaginary part of this Comlex.
* @returns {number} imag(A)
*/
get imag() {
return this._im;
}
/**
* norm.
* @returns {number} |A|
*/
get norm() {
if(this._im === 0) {
return Math.abs(this._re);
}
else if(this._re === 0) {
return Math.abs(this._im);
}
else {
return Math.sqrt(this._re * this._re + this._im * this._im);
}
}
/**
* The argument of this complex number.
* @returns {number} arg(A)
*/
get arg() {
if(this._im === 0) {
return this._re >= 0 ? 0 : Math.PI;
}
else if(this._re === 0) {
return Math.PI * (this._im >= 0.0 ? 0.5 : -0.5);
}
else {
return Math.atan2(this._im, this._re);
}
}
/**
* Return number of decimal places for real and imaginary parts.
* - Used to make a string.
* @returns {number} Number of decimal places.
*/
getDecimalPosition() {
/**
* @type {function(number): number }
*/
const getDecimal = function(x) {
if(!Number.isFinite(x)) {
return 0;
}
let a = x;
let point = 0;
for(let i = 0; i < 20; i++) {
if(Math.abs(a - Math.round(a)) <= Number.EPSILON) {
break;
}
a *= 10;
point++;
}
return point;
};
return Math.max( getDecimal(this.real), getDecimal(this.imag) );
}
/**
* The positive or negative sign of this number.
* - +1 if positive, -1 if negative, 0 if 0.
* @returns {Complex}
*/
sign() {
if(!this.isFinite()) {
if(this.isNaN() || this._im === Infinity || this._im === -Infinity) {
return Complex.NaN;
}
if(this._re === Infinity) {
return Complex.ONE;
}
else {
return Complex.MINUS_ONE;
}
}
if(this._im === 0) {
if(this._re === 0) {
return Complex.ZERO;
}
else {
return new Complex(this._re > 0 ? 1 : -1);
}
}
return this.div(this.norm);
}
// ----------------------
// 四則演算
// ----------------------
/**
* Add.
* @param {KComplexInputData} number
* @returns {Complex} A + B
*/
add(number) {
const A = this;
const B = new Complex(number);
B._re = A._re + B._re;
B._im = A._im + B._im;
return B;
}
/**
* Subtract.
* @param {KComplexInputData} number
* @returns {Complex} A - B
*/
sub(number) {
const A = this;
const B = new Complex(number);
B._re = A._re - B._re;
B._im = A._im - B._im;
return B;
}
/**
* Multiply.
* @param {KComplexInputData} number
* @returns {Complex} A * B
*/
mul(number) {
const A = this;
const B = new Complex(number);
if((A._im === 0) && (B._im === 0)) {
B._re = A._re * B._re;
return B;
}
else if((A._re === 0) && (B._re === 0)) {
B._re = - A._im * B._im;
B._im = 0;
return B;
}
else {
const re = A._re * B._re - A._im * B._im;
const im = A._im * B._re + A._re * B._im;
B._re = re;
B._im = im;
return B;
}
}
/**
* Inner product/Dot product.
* @param {KComplexInputData} number
* @returns {Complex} A * conj(B)
*/
dot(number) {
const A = this;
const B = new Complex(number);
if((A._im === 0) && (B._im === 0)) {
B._re = A._re * B._re;
return B;
}
else if((A._re === 0) && (B._re === 0)) {
B._re = A._im * B._im;
B._im = 0;
return B;
}
else {
const re = A._re * B._re + A._im * B._im;
const im = - A._im * B._re + A._re * B._im;
B._re = re;
B._im = im;
return B;
}
}
/**
* Divide.
* @param {KComplexInputData} number
* @returns {Complex} A / B
*/
div(number) {
const A = this;
const B = new Complex(number);
if((A._im === 0) && (B._im === 0)) {
B._re = A._re / B._re;
return B;
}
else if((A._re === 0) && (B._re === 0)) {
B._re = A._im / B._im;
B._im = 0;
return B;
}
else {
const re = A._re * B._re + A._im * B._im;
const im = A._im * B._re - A._re * B._im;
const denominator = 1.0 / (B._re * B._re + B._im * B._im);
B._re = re * denominator;
B._im = im * denominator;
return B;
}
}
/**
* Modulo, positive remainder of division.
* - Result has same sign as the Dividend.
* @param {KComplexInputData} number - Divided value (real number only).
* @returns {Complex} A rem B
*/
rem(number) {
const A = this;
const B = new Complex(number);
if((A._im !== 0) || (B._im !== 0)) {
throw "calculation method is undefined.";
}
if(!A.isFinite() || !B.isFinite() || B.isZero()) {
return Complex.NaN;
}
B._re = A._re - B._re * (Math.trunc(A._re / B._re));
return B;
}
/**
* Modulo, positive remainder of division.
* - Result has same sign as the Divisor.
* @param {KComplexInputData} number - Divided value (real number only).
* @returns {Complex} A mod B
*/
mod(number) {
const A = this;
const B = new Complex(number);
if((A._im !== 0) || (B._im !== 0)) {
throw "calculation method is undefined.";
}
if(B.isZero()) {
return A;
}
const ret = A.rem(B);
if(!A.equalsState(B)) {
return ret.add(B);
}
else {
return ret;
}
}
/**
* Inverse number of this value.
* @returns {Complex} 1 / A
*/
inv() {
if(this._im === 0) {
return new Complex(1.0 / this._re);
}
else if(this._re === 0) {
return new Complex([0, - 1.0 / this._im]);
}
return Complex.ONE.div(this);
}
// ----------------------
// 他の型に変換用
// ----------------------
/**
* boolean value.
* @returns {boolean}
*/
get booleanValue() {
return !this.isZero() && !this.isNaN();
}
/**
* integer value.
* @returns {number}
*/
get intValue() {
if(!this.isFinite()) {
return this.isNaN() ? NaN : (this.isPositiveInfinity() ? Infinity : -Infinity);
}
const value = this._re;
const delta = Math.abs(value - Math.trunc(value));
if(delta < Number.EPSILON) {
return Math.round(value);
}
else {
return Math.trunc(value);
}
}
/**
* floating point.
* @returns {number}
*/
get doubleValue() {
if(!this.isFinite()) {
return this.isNaN() ? NaN : (this.isPositiveInfinity() ? Infinity : -Infinity);
}
const value = this._re;
const delta = Math.abs(value - Math.trunc(value));
if(delta < Number.EPSILON) {
return Math.round(value);
}
else {
return value;
}
}
// ----------------------
// konpeito で扱う数値型へ変換
// ----------------------
/**
* return BigInteger.
* @returns {BigInteger}
*/
toBigInteger() {
return new BigInteger(this.intValue);
}
/**
* return BigDecimal.
* @param {MathContext} [mc] - MathContext setting after calculation.
* @returns {BigDecimal}
*/
toBigDecimal(mc) {
if(mc) {
return new BigDecimal([this.doubleValue, mc]);
}
else {
return new BigDecimal(this.doubleValue);
}
}
/**
* return Fraction.
* @returns {Fraction}
*/
toFraction() {
return new Fraction(this.doubleValue);
}
/**
* return Complex.
* @returns {Complex}
*/
toComplex() {
return this;
}
/**
* return Matrix.
* @returns {Matrix}
*/
toMatrix() {
return new Matrix(this);
}
// ----------------------
// 比較
// ----------------------
/**
* Equals.
* @param {KComplexInputData} number
* @param {KComplexInputData} [tolerance=Number.EPSILON] - Calculation tolerance of calculation.
* @returns {boolean} A === B
*/
equals(number, tolerance) {
const A = this;
const B = Complex._toComplex(number);
// 無限大、非数の値も含めて一度確認
if(A.isNaN() || B.isNaN()) {
return false;
}
if((A._re === B._re) && (A._im === B._im)) {
return true;
}
// 誤差を含んだ値の比較
const tolerance_ = tolerance ? Complex._toDouble(tolerance) : Number.EPSILON;
return (Math.abs(A._re - B._re) < tolerance_) && (Math.abs(A._im - B._im) < tolerance_);
}
/**
* Numeric type match.
* @param {KComplexInputData} number
* @returns {boolean}
*/
equalsState(number) {
const A = this;
const B = Complex._toComplex(number);
/**
* @param {Complex} num
* @returns {number}
*/
const getState = function(num) {
if(num.isZero()) {
return 0;
}
if(!num.isFinite()) {
if(num.isPositiveInfinity()) {
return 4;
}
else if(num.isNegativeInfinity()) {
return 5;
}
else {
return 3;
}
}
return num.isPositive() ? 1 : 2;
};
const A_type = getState(A);
const B_type = getState(B);
return A_type === B_type;
}
/**
* Compare values.
* @param {KComplexInputData} number
* @param {KComplexInputData} [tolerance=Number.EPSILON] - Calculation tolerance of calculation.
* @returns {number} A > B ? 1 : (A === B ? 0 : -1)
*/
compareTo(number, tolerance) {
const A = this;
const B = Complex._toComplex(number);
if(!A.isFinite() || !B.isFinite()) {
if(A.equals(B)) {
return 0;
}
else if(
A.isNaN() || B.isNaN() ||
(A.real === Infinity && A.imag === -Infinity) ||
(A.real === -Infinity && A.imag === Infinity) ||
(B.real === Infinity && B.imag === -Infinity) ||
(B.real === -Infinity && B.imag === Infinity) ) {
return NaN;
}
else if(A.isFinite()) {
return B.real + B.imag < 0 ? 1 : -1;
}
else if(B.isFinite()) {
return A.real + A.imag > 0 ? 1 : -1;
}
else {
return NaN;
}
}
const tolerance_ = tolerance ? Complex._toDouble(tolerance) : Number.EPSILON;
const a = A.real + A.imag;
const b = B.real + B.imag;
if((Math.abs(a - b) <= tolerance_)) {
return 0;
}
return a > b ? 1 : -1;
}
/**
* Maximum number.
* @param {KComplexInputData} number
* @returns {Complex} max([A, B])
*/
max(number) {
const x = Complex._toComplex(number);
if(this.compareTo(x) >= 0) {
return this;
}
else {
return x;
}
}
/**
* Minimum number.
* @param {KComplexInputData} number
* @returns {Complex} min([A, B])
*/
min(number) {
const x = Complex._toComplex(number);
if(this.compareTo(x) <= 0) {
return this;
}
else {
return x;
}
}
/**
* Clip number within range.
* @param {KComplexInputData} min
* @param {KComplexInputData} max
* @returns {Complex} min(max(x, min), max)
*/
clip(min, max) {
const min_ = Complex._toComplex(min);
const max_ = Complex._toComplex(max);
const arg_check = min_.compareTo(max_);
if(arg_check === 1) {
throw "clip(min, max) error. (min > max)->(" + min_ + " > " + max_ + ")";
}
else if(arg_check === 0) {
return min_;
}
if(this.compareTo(max_) === 1) {
return max_;
}
else if(this.compareTo(min_) === -1) {
return min_;
}
return this;
}
// ----------------------
// 丸め
// ----------------------
/**
* Floor.
* @returns {Complex} floor(A)
*/
floor() {
return new Complex([Math.floor(this._re), Math.floor(this._im)]);
}
/**
* Ceil.
* @returns {Complex} ceil(A)
*/
ceil() {
return new Complex([Math.ceil(this._re), Math.ceil(this._im)]);
}
/**
* Rounding to the nearest integer.
* @returns {Complex} round(A)
*/
round() {
return new Complex([Math.round(this._re), Math.round(this._im)]);
}
/**
* To integer rounded down to the nearest.
* @returns {Complex} fix(A), trunc(A)
*/
fix() {
return new Complex([Math.trunc(this._re), Math.trunc(this._im)]);
}
/**
* Fraction.
* @returns {Complex} fract(A)
*/
fract() {
return new Complex([this._re - Math.floor(this._re), this._im - Math.floor(this._im)]);
}
// ----------------------
// 複素数
// ----------------------
/**
* Absolute value.
* @returns {Complex} abs(A)
*/
abs() {
return new Complex(this.norm);
}
/**
* Complex conjugate.
* @returns {Complex} real(A) - imag(A)j
*/
conj() {
if(this._im === 0) {
return this;
}
// 共役複素数
return new Complex([this._re, -this._im]);
}
/**
* this * -1
* @returns {Complex} -A
*/
negate() {
return new Complex([-this._re, -this._im]);
}
// ----------------------
// 指数
// ----------------------
/**
* Power function.
* @param {KComplexInputData} number
* @returns {Complex} pow(A, B)
*/
pow(number) {
const A = this;
const B = new Complex(number);
// -2 ^ 0.5 ... 複素数
// -2 ^ 1 ... 実数
// 2 ^ 0.5 ... 実数
if(B.isReal()) {
if(A.isReal() && (A.isNotNegative() || B.isInteger())) {
B._re = Math.pow(A._re, B._re);
return B;
}
else {
const r = Math.pow(A.norm, B._re);
const s = A.arg * B._re;
B._re = r * Math.cos(s);
B._im = r * Math.sin(s);
return B;
}
}
else {
return B.mul(A.log()).exp();
}
}
/**
* Square.
* @returns {Complex} pow(A, 2)
*/
square() {
if(this._im === 0.0) {
return new Complex(this._re * this._re);
}
return this.mul(this);
}
/**
* Square root.
* @returns {Complex} sqrt(A)
*/
sqrt() {
if(this.isReal()) {
if(this.isNotNegative()) {
return new Complex(Math.sqrt(this._re));
}
else {
return new Complex([0, Math.sqrt(-this._re)]);
}
}
const r = Math.sqrt(this.norm);
const s = this.arg * 0.5;
return new Complex([r * Math.cos(s), r * Math.sin(s)]);
}
/**
* Cube root.
* @param {KComplexInputData} [n=0] - Value type(0,1,2)
* @returns {Complex} cbrt(A)
*/
cbrt(n) {
const type = Complex._toInteger(n !== undefined ? n : 0);
const x = this.log().div(3).exp();
if(type === 0) {
return x;
}
else if(type === 1) {
return x.mul([-0.5, Math.sqrt(3) * 0.5]);
}
else {
return x.mul([-0.5, - Math.sqrt(3) * 0.5]);
}
}
/**
* Reciprocal square root.
* @returns {Complex} rsqrt(A)
*/
rsqrt() {
if(this.isReal()) {
if(this.isNotNegative()) {
return new Complex(1.0 / Math.sqrt(this._re));
}
else {
return new Complex([0, - 1.0 / Math.sqrt(-this._re)]);
}
}
return this.sqrt().inv();
}
/**
* Logarithmic function.
* @returns {Complex} log(A)
*/
log() {
if(this.isReal() && this.isNotNegative()) {
return new Complex(Math.log(this._re));
}
// 負の値が入っているか、もともと複素数が入っている場合は、複素対数関数
return new Complex([Math.log(this.norm), this.arg]);
}
/**
* Exponential function.
* @returns {Complex} exp(A)
*/
exp() {
if(this.isReal()) {
return new Complex(Math.exp(this._re));
}
// 複素指数関数
const r = Math.exp(this._re);
return new Complex([r * Math.cos(this._im), r * Math.sin(this._im)]);
}
/**
* e^x - 1
* @returns {Complex} expm1(A)
*/
expm1() {
return this.exp().sub(1);
}
/**
* ln(1 + x)
* @returns {Complex} log1p(A)
*/
log1p() {
return this.add(1).log();
}
/**
* log_2(x)
* @returns {Complex} log2(A)
*/
log2() {
return this.log().div(Complex.LN2);
}
/**
* log_10(x)
* @returns {Complex} log10(A)
*/
log10() {
return this.log().div(Complex.LN10);
}
// ----------------------
// 三角関数
// ----------------------
/**
* Sine function.
* @returns {Complex} sin(A)
*/
sin() {
if(this.isReal()) {
return new Complex(Math.sin(this._re));
}
// オイラーの公式より
// sin x = (e^ix - e^-ex) / 2i
const a = this.mul(Complex.I).exp();
const b = this.mul(Complex.I.negate()).exp();
return a.sub(b).div([0, 2]);
}
/**
* Cosine function.
* @returns {Complex} cos(A)
*/
cos() {
if(this.isReal()) {
return new Complex(Math.cos(this._re));
}
// オイラーの公式より
// cos x = (e^ix + e^-ex) / 2
const a = this.mul(Complex.I).exp();
const b = this.mul(Complex.I.negate()).exp();
return a.add(b).div(2);
}
/**
* Tangent function.
* @returns {Complex} tan(A)
*/
tan() {
if(this.isReal()) {
return new Complex(Math.tan(this._re));
}
// 三角関数の相互関係 tan x = sin x / cos x
return this.sin().div(this.cos());
}
/**
* Atan (arc tangent) function.
* - Return the values of [-PI/2, PI/2].
* @returns {Complex} atan(A)
*/
atan() {
if(this.isReal()) {
return new Complex(Math.atan(this._re));
}
// 逆正接 tan-1 x = i/2 log( i+x / i-x )
return Complex.I.div(Complex.TWO).mul(Complex.I.add(this).div(Complex.I.sub(this)).log());
}
/**
* Atan (arc tangent) function.
* Return the values of [-PI, PI] .
* Supports only real numbers.
* @param {KComplexInputData} [number] - X
* @returns {Complex} atan2(Y, X)
*/
atan2(number) {
if(arguments.length === 0) {
return new Complex(this.arg);
}
// y.atan2(x) とする。
const y = this;
const x = Complex._toComplex(number);
if(y.isReal() && x.isReal()) {
return new Complex(Math.atan2(y._re, x._re));
}
// 複素数のatan2は未定義である(実装不可能)
throw "calculation method is undefined.";
}
// ----------------------
// 双曲線関数
// ----------------------
/**
* Arc sine function.
* @returns {Complex} asin(A)
*/
asin() {
// 逆正弦
return this.mul(Complex.I).add(Complex.ONE.sub(this.square()).sqrt()).log().mul(Complex.MINUS_I);
}
/**
* Arc cosine function.
* @returns {Complex} acos(A)
*/
acos() {
// 逆余弦
return this.add(Complex.I.mul(Complex.ONE.sub(this.square()).sqrt())).log().mul(Complex.MINUS_I);
}
/**
* Hyperbolic sine function.
* @returns {Complex} sinh(A)
*/
sinh() {
// 双曲線正弦
const y = this.exp();
return y.sub(y.inv()).mul(0.5);
}
/**
* Inverse hyperbolic sine function.
* @returns {Complex} asinh(A)
*/
asinh() {
// 逆双曲線正弦 Math.log(x + Math.sqrt(x * x + 1));
if(this.isInfinite()) {
return this;
}
return this.add(this.mul(this).add(1).sqrt()).log();
}
/**
* Hyperbolic cosine function.
* @returns {Complex} cosh(A)
*/
cosh() {
// 双曲線余弦
return this.exp().add(this.negate().exp()).mul(0.5);
}
/**
* Inverse hyperbolic cosine function.
* @returns {Complex} acosh(A)
*/
acosh() {
// 逆双曲線余弦 Math.log(x + Math.sqrt(x * x - 1));
// Octave だと log(0.5+(0.5*0.5-1)^0.5) !== acosh(0.5) になる。
// おそらく log(0.5-(0.5*0.5-1)^0.5) の式に切り替わるようになっている
// これは2つの値を持っているためだと思われるので合わせてみる
if(this.isZero()) {
return new Complex([0, Math.PI * 0.5]);
}
if(this.compareTo(Complex.ONE) >= 1) {
return this.add(this.square().sub(1).sqrt()).log();
}
else {
return this.sub(this.square().sub(1).sqrt()).log();
}
}
/**
* Hyperbolic tangent function.
* @returns {Complex} tanh(A)
*/
tanh() {
// 双曲線正接
if(this.isNaN()) {
return Complex.NaN;
}
const y = this.mul(2).exp();
if(y.isZero()) {
return Complex.MINUS_ONE;
}
else if(y.isPositiveInfinity()) {
return Complex.ONE;
}
return y.sub(1).div(y.add(1));
}
/**
* Inverse hyperbolic tangent function.
* @returns {Complex} atanh(A)
*/
atanh() {
// 逆双曲線正接
if(this.isInfinite() && this.isReal()) {
return new Complex([0, Math.PI * 0.5]);
}
return this.add(1).div(this.negate().add(1)).log().mul(0.5);
}
/**
* Secant function.
* @returns {Complex} sec(A)
*/
sec() {
// 正割
return this.cos().inv();
}
/**
* Reverse secant function.
* @returns {Complex} asec(A)
*/
asec() {
// 逆正割
return this.inv().acos();
}
/**
* Hyperbolic secant function.
* @returns {Complex} sech(A)
*/
sech() {
// 双曲線正割
return this.exp().add(this.negate().exp()).inv().mul(2);
}
/**
* Inverse hyperbolic secant function.
* @returns {Complex} asech(A)
*/
asech() {
// 逆双曲線正割
if(this.isInfinite() && this.isReal()) {
return new Complex([0, Math.PI * 0.5]);
}
if(this.isPositive() || (this.compareTo(Complex.MINUS_ONE) == -1)) {
return this.inv().add(this.square().inv().sub(1).sqrt()).log();
}
else {
return this.inv().sub(this.square().inv().sub(1).sqrt()).log();
}
}
/**
* Cotangent function.
* @returns {Complex} cot(A)
*/
cot() {
// 余接
return this.tan().inv();
}
/**
* Inverse cotangent function.
* @returns {Complex} acot(A)
*/
acot() {
// 逆余接
return this.inv().atan();
}
/**
* Hyperbolic cotangent function.
* @returns {Complex} coth(A)
*/
coth() {
// 双曲線余接
if(this.isZero()) {
return Complex.POSITIVE_INFINITY;
}
return this.tanh().inv();
}
/**
* Inverse hyperbolic cotangent function.
* @returns {Complex} acoth(A)
*/
acoth() {
// 逆双曲線余接
if(this.isInfinite()) {
return Complex.ZERO;
}
return this.add(1).div(this.sub(1)).log().mul(0.5);
}
/**
* Cosecant function.
* @returns {Complex} csc(A)
*/
csc() {
// 余割
return this.sin().inv();
}
/**
* Inverse cosecant function.
* @returns {Complex} acsc(A)
*/
acsc() {
// 逆余割
return this.inv().asin();
}
/**
* Hyperbolic cosecant function.
* @returns {Complex} csch(A)
*/
csch() {
// 双曲線余割
return this.exp().sub(this.negate().exp()).inv().mul(2);
}
/**
* Inverse hyperbolic cosecant function.
* @returns {Complex} acsch(A)
*/
acsch() {
// 逆双曲線余割
return this.inv().add(this.square().inv().add(1).sqrt()).log();
}
// ----------------------
// 確率・統計系
// ----------------------
/**
* Logit function.
* @returns {Complex} logit(A)
*/
logit() {
return this.log().sub(Complex.ONE.sub(this).log());
}
// ----------------------
// 信号処理系
// ----------------------
/**
* Normalized sinc function.
* @returns {Complex} sinc(A)
*/
sinc() {
if(this.isReal()) {
if(this._re === 0) {
return(Complex.ONE);
}
const x = Math.PI * this._re;
return new Complex(Math.sin(x) / x);
}
const x = this.mul(Complex.PI);
return new Complex( x.sin().div(x) );
}
// ----------------------
// 乱数
// ----------------------
/**
* Create random values [0, 1) with uniform random numbers.
* @param {Random} [random] - Class for creating random numbers.
* @returns {Complex}
*/
static rand(random) {
const rand = (random !== undefined && random instanceof Random) ? random : random_class;
return new Complex(rand.nextDouble());
}
/**
* Create random values with normal distribution.
* @param {Random} [random] - Class for creating random numbers.
* @returns {Complex}
*/
static randn(random) {
const rand = (random !== undefined && random instanceof Random) ? random : random_class;
return new Complex(rand.nextGaussian());
}
// ----------------------
// テスト系
// ----------------------
/**
* Return true if the value is integer.
* @param {KComplexInputData} [tolerance=Number.EPSILON] - Calculation tolerance of calculation.
* @returns {boolean}
*/
isInteger(tolerance) {
const tolerance_ = tolerance ? Complex._toDouble(tolerance) : Number.EPSILON;
return this.isReal() && (Math.abs(this._re - Math.trunc(this._re)) < tolerance_);
}
/**
* Returns true if the vallue is complex integer (including normal integer).
* @param {KComplexInputData} [tolerance=Number.EPSILON] - Calculation tolerance of calculation.
* @returns {boolean} real(A) === integer && imag(A) === integer
*/
isComplexInteger(tolerance) {
const tolerance_ = tolerance ? Complex._toDouble(tolerance) : Number.EPSILON;
// 複素整数
return (Math.abs(this._re - Math.trunc(this._re)) < tolerance_) &&
(Math.abs(this._im - Math.trunc(this._im)) < tolerance_);
}
/**
* this === 0
* @param {KComplexInputData} [tolerance=Number.EPSILON] - Calculation tolerance of calculation.
* @returns {boolean} A === 0
*/
isZero(tolerance) {
const tolerance_ = tolerance ? Complex._toDouble(tolerance) : Number.EPSILON;
return (Math.abs(this._re) < tolerance_) && (Math.abs(this._im) < tolerance_);
}
/**
* this === 1
* @param {KComplexInputData} [tolerance=Number.EPSILON] - Calculation tolerance of calculation.
* @returns {boolean} A === 1
*/
isOne(tolerance) {
const tolerance_ = tolerance ? Complex._toDouble(tolerance) : Number.EPSILON;
return (Math.abs(this._re - 1.0) < tolerance_) && (Math.abs(this._im) < tolerance_);
}
/**
* Returns true if the vallue is complex number (imaginary part is not 0).
* @param {KComplexInputData} [tolerance=Number.EPSILON] - Calculation tolerance of calculation.
* @returns {boolean} imag(A) !== 0
*/
isComplex(tolerance) {
const tolerance_ = tolerance ? Complex._toDouble(tolerance) : Number.EPSILON;
return (Math.abs(this._im) >= tolerance_);
}
/**
* Return true if the value is real number.
* @param {KComplexInputData} [tolerance=Number.EPSILON] - Calculation tolerance of calculation.
* @returns {boolean} imag(A) === 0
*/
isReal(tolerance) {
const tolerance_ = tolerance ? Complex._toDouble(tolerance) : Number.EPSILON;
return (Math.abs(this._im) < tolerance_);
}
/**
* this === NaN
* @returns {boolean} isNaN(A)
*/
isNaN() {
return isNaN(this._re) || isNaN(this._im);
}
/**
* Return true if this real part of the complex positive.
* @returns {boolean} real(x) > 0
*/
isPositive() {
// Number.EPSILONは使用しない。どちらにぶれるか不明な点及び
// わずかな負の数だった場合に、sqrtでエラーが発生するため
return 0.0 < this._re;
}
/**
* real(this) < 0
* @returns {boolean} real(x) < 0
*/
isNegative() {
return 0.0 > this._re;
}
/**
* real(this) >= 0
* @returns {boolean} real(x) >= 0
*/
isNotNegative() {
return 0.0 <= this._re;
}
/**
* this === Infinity
* @returns {boolean} isPositiveInfinity(A)
*/
isPositiveInfinity() {
return this._re === Number.POSITIVE_INFINITY || this._im === Number.POSITIVE_INFINITY;
}
/**
* this === -Infinity
* @returns {boolean} isNegativeInfinity(A)
*/
isNegativeInfinity() {
return this._re === Number.NEGATIVE_INFINITY || this._im === Number.NEGATIVE_INFINITY;
}
/**
* this === Infinity or -Infinity
* @returns {boolean} isPositiveInfinity(A) || isNegativeInfinity(A)
*/
isInfinite() {
return this.isPositiveInfinity() || this.isNegativeInfinity();
}
/**
* Return true if the value is finite number.
* @returns {boolean} !isNaN(A) && !isInfinite(A)
*/
isFinite() {
return !this.isNaN() && !this.isInfinite();
}
// ----------------------
// 確率
// ----------------------
/**
* Log-gamma function.
* - Calculate from real values.
* @returns {Complex}
*/
gammaln() {
return new Complex(Probability.gammaln(this._re));
}
/**
* Gamma function.
* - Calculate from real values.
* @returns {Complex}
*/
gamma() {
return new Complex(Probability.gamma(this._re));
}
/**
* Incomplete gamma function.
* - Calculate from real values.
* @param {KComplexInputData} a
* @param {string} [tail="lower"] - lower (default) , "upper"
* @returns {Complex}
*/
gammainc(a, tail) {
const a_ = Complex._toDouble(a);
return new Complex(Probability.gammainc(this._re, a_, tail));
}
/**
* Probability density function (PDF) of the gamma distribution.
* - Calculate from real values.
* @param {KComplexInputData} k - Shape parameter.
* @param {KComplexInputData} s - Scale parameter.
* @returns {Complex}
*/
gampdf(k, s) {
const k_ = Complex._toDouble(k);
const s_ = Complex._toDouble(s);
return new Complex(Probability.gampdf(this._re, k_, s_));
}
/**
* Cumulative distribution function (CDF) of gamma distribution.
* - Calculate from real values.
* @param {KComplexInputData} k - Shape parameter.
* @param {KComplexInputData} s - Scale parameter.
* @returns {Complex}
*/
gamcdf(k, s) {
const k_ = Complex._toDouble(k);
const s_ = Complex._toDouble(s);
return new Complex(Probability.gamcdf(this._re, k_, s_));
}
/**
* Inverse function of cumulative distribution function (CDF) of gamma distribution.
* - Calculate from real values.
* @param {KComplexInputData} k - Shape parameter.
* @param {KComplexInputData} s - Scale parameter.
* @returns {Complex}
*/
gaminv(k, s) {
const k_ = Complex._toDouble(k);
const s_ = Complex._toDouble(s);
return new Complex(Probability.gaminv(this._re, k_, s_));
}
/**
* Beta function.
* - Calculate from real values.
* @param {KComplexInputData} y
* @returns {Complex}
*/
beta(y) {
const y_ = Complex._toDouble(y);
return new Complex(Probability.beta(this._re, y_));
}
/**
* Incomplete beta function.
* - Calculate from real values.
* @param {KComplexInputData} a
* @param {KComplexInputData} b
* @param {string} [tail="lower"] - lower (default) , "upper"
* @returns {Complex}
*/
betainc(a, b, tail) {
const a_ = Complex._toDouble(a);
const b_ = Complex._toDouble(b);
return new Complex(Probability.betainc(this._re, a_, b_, tail));
}
/**
* Probability density function (PDF) of beta distribution.
* - Calculate from real values.
* @param {KComplexInputData} a
* @param {KComplexInputData} b
* @returns {Complex}
*/
betapdf(a, b) {
const a_ = Complex._toDouble(a);
const b_ = Complex._toDouble(b);
return new Complex(Probability.betapdf(this._re, a_, b_));
}
/**
* Cumulative distribution function (CDF) of beta distribution.
* - Calculate from real values.
* @param {KComplexInputData} a
* @param {KComplexInputData} b
* @returns {Complex}
*/
betacdf(a, b) {
const a_ = Complex._toDouble(a);
const b_ = Complex._toDouble(b);
return new Complex(Probability.betacdf(this._re, a_, b_));
}
/**
* Inverse function of cumulative distribution function (CDF) of beta distribution.
* - Calculate from real values.
* @param {KComplexInputData} a
* @param {KComplexInputData} b
* @returns {Complex}
*/
betainv(a, b) {
const a_ = Complex._toDouble(a);
const b_ = Complex._toDouble(b);
return new Complex(Probability.betainv(this._re, a_, b_));
}
/**
* Factorial function, x!.
* - Calculate from real values.
* @returns {Complex}
*/
factorial() {
return new Complex(Probability.factorial(this._re));
}
/**
* Binomial coefficient, number of all combinations, nCk.
* - Calculate from real values.
* @param {KComplexInputData} k
* @returns {Complex}
*/
nchoosek(k) {
const k_ = Complex._toDouble(k);
return new Complex(Probability.nchoosek(this._re, k_));
}
/**
* Error function.
* - Calculate from real values.
* @returns {Complex}
*/
erf() {
return new Complex(Probability.erf(this._re));
}
/**
* Complementary error function.
* - Calculate from real values.
* @returns {Complex}
*/
erfc() {
return new Complex(Probability.erfc(this._re));
}
/**
* Inverse function of Error function.
* - Calculate from real values.
* @returns {Complex}
*/
erfinv() {
return new Complex(Probability.erfinv(this._re));
}
/**
* Inverse function of Complementary error function.
* - Calculate from real values.
* @returns {Complex}
*/
erfcinv() {
return new Complex(Probability.erfcinv(this._re));
}
/**
* Probability density function (PDF) of normal distribution.
* - Calculate from real values.
* @param {KComplexInputData} [u=0.0] - Average value.
* @param {KComplexInputData} [s=1.0] - Variance value.
* @returns {Complex}
*/
normpdf(u, s) {
const u_ = u !== undefined ? Complex._toDouble(u) : 0.0;
const s_ = s !== undefined ? Complex._toDouble(s) : 1.0;
return new Complex(Probability.normpdf(this._re, u_, s_));
}
/**
* Cumulative distribution function (CDF) of normal distribution.
* - Calculate from real values.
* @param {KComplexInputData} [u=0.0] - Average value.
* @param {KComplexInputData} [s=1.0] - Variance value.
* @returns {Complex}
*/
normcdf(u, s) {
const u_ = u !== undefined ? Complex._toDouble(u) : 0.0;
const s_ = s !== undefined ? Complex._toDouble(s) : 1.0;
return new Complex(Probability.normcdf(this._re, u_, s_));
}
/**
* Inverse function of cumulative distribution function (CDF) of normal distribution.
* - Calculate from real values.
* @param {KComplexInputData} [u=0.0] - Average value.
* @param {KComplexInputData} [s=1.0] - Variance value.
* @returns {Complex}
*/
norminv(u, s) {
const u_ = u !== undefined ? Complex._toDouble(u) : 0.0;
const s_ = s !== undefined ? Complex._toDouble(s) : 1.0;
return new Complex(Probability.norminv(this._re, u_, s_));
}
/**
* Probability density function (PDF) of binomial distribution.
* - Calculate from real values.
* @param {KComplexInputData} n
* @param {KComplexInputData} p
* @returns {Complex}
*/
binopdf(n, p) {
const n_ = Complex._toDouble(n);
const p_ = Complex._toDouble(p);
return new Complex(Probability.binopdf(this._re, n_, p_));
}
/**
* Cumulative distribution function (CDF) of binomial distribution.
* - Calculate from real values.
* @param {KComplexInputData} n
* @param {KComplexInputData} p
* @param {string} [tail="lower"] - lower (default) , "upper"
* @returns {Complex}
*/
binocdf(n, p, tail) {
const n_ = Complex._toDouble(n);
const p_ = Complex._toDouble(p);
return new Complex(Probability.binocdf(this._re, n_, p_, tail));
}
/**
* Inverse function of cumulative distribution function (CDF) of binomial distribution.
* - Calculate from real values.
* @param {KComplexInputData} n
* @param {KComplexInputData} p
* @returns {Complex}
*/
binoinv(n, p) {
const n_ = Complex._toDouble(n);
const p_ = Complex._toDouble(p);
return new Complex(Probability.binoinv(this._re, n_, p_));
}
/**
* Probability density function (PDF) of Poisson distribution.
* - Calculate from real values.
* @param {KComplexInputData} lambda
* @returns {Complex}
*/
poisspdf(lambda) {
const lambda_ = Complex._toDouble(lambda);
return new Complex(Probability.poisspdf(this._re, lambda_));
}
/**
* Cumulative distribution function (CDF) of Poisson distribution.
* - Calculate from real values.
* @param {KComplexInputData} lambda
* @returns {Complex}
*/
poisscdf(lambda) {
const lambda_ = Complex._toDouble(lambda);
return new Complex(Probability.poisscdf(this._re, lambda_));
}
/**
* Inverse function of cumulative distribution function (CDF) of Poisson distribution.
* - Calculate from real values.
* @param {KComplexInputData} lambda
* @returns {Complex}
*/
poissinv(lambda) {
const lambda_ = Complex._toDouble(lambda);
return new Complex(Probability.poissinv(this._re, lambda_));
}
/**
* Probability density function (PDF) of Student's t-distribution.
* - Calculate from real values.
* @param {KComplexInputData} v - The degrees of freedom. (DF)
* @returns {Complex}
*/
tpdf(v) {
const v_ = Complex._toDouble(v);
return new Complex(Probability.tpdf(this._re, v_));
}
/**
* Cumulative distribution function (CDF) of Student's t-distribution.
* - Calculate from real values.
* @param {KComplexInputData} v - The degrees of freedom. (DF)
* @returns {Complex}
*/
tcdf(v) {
const v_ = Complex._toDouble(v);
return new Complex(Probability.tcdf(this._re, v_));
}
/**
* Inverse of cumulative distribution function (CDF) of Student's t-distribution.
* - Calculate from real values.
* @param {KComplexInputData} v - The degrees of freedom. (DF)
* @returns {Complex}
*/
tinv(v) {
const v_ = Complex._toDouble(v);
return new Complex(Probability.tinv(this._re, v_));
}
/**
* Cumulative distribution function (CDF) of Student's t-distribution that can specify tail.
* - Calculate from real values.
* @param {KComplexInputData} v - The degrees of freedom. (DF)
* @param {KComplexInputData} tails - Tail. (1 = the one-tailed distribution, 2 = the two-tailed distribution.)
* @returns {Complex}
*/
tdist(v, tails) {
const v_ = Complex._toDouble(v);
const tails_ = Complex._toInteger(tails);
return new Complex(Probability.tdist(this._re, v_, tails_));
}
/**
* Inverse of cumulative distribution function (CDF) of Student's t-distribution in two-sided test.
* - Calculate from real values.
* @param {KComplexInputData} v - The degrees of freedom. (DF)
* @returns {Complex}
*/
tinv2(v) {
const v_ = Complex._toDouble(v);
return new Complex(Probability.tinv2(this._re, v_));
}
/**
* Probability density function (PDF) of chi-square distribution.
* - Calculate from real values.
* @param {KComplexInputData} k - The degrees of freedom. (DF)
* @returns {Complex}
*/
chi2pdf(k) {
const k_ = Complex._toDouble(k);
return new Complex(Probability.chi2pdf(this._re, k_));
}
/**
* Cumulative distribution function (CDF) of chi-square distribution.
* - Calculate from real values.
* @param {KComplexInputData} k - The degrees of freedom. (DF)
* @returns {Complex}
*/
chi2cdf(k) {
const k_ = Complex._toDouble(k);
return new Complex(Probability.chi2cdf(this._re, k_));
}
/**
* Inverse function of cumulative distribution function (CDF) of chi-square distribution.
* - Calculate from real values.
* @param {KComplexInputData} k - The degrees of freedom. (DF)
* @returns {Complex}
*/
chi2inv(k) {
const k_ = Complex._toDouble(k);
return new Complex(Probability.chi2inv(this._re, k_));
}
/**
* Probability density function (PDF) of F-distribution.
* - Calculate from real values.
* @param {KComplexInputData} d1 - The degree of freedom of the molecules.
* @param {KComplexInputData} d2 - The degree of freedom of the denominator
* @returns {Complex}
*/
fpdf(d1, d2) {
const d1_ = Complex._toDouble(d1);
const d2_ = Complex._toDouble(d2);
return new Complex(Probability.fpdf(this._re, d1_, d2_));
}
/**
* Cumulative distribution function (CDF) of F-distribution.
* - Calculate from real values.
* @param {KComplexInputData} d1 - The degree of freedom of the molecules.
* @param {KComplexInputData} d2 - The degree of freedom of the denominator
* @returns {Complex}
*/
fcdf(d1, d2) {
const d1_ = Complex._toDouble(d1);
const d2_ = Complex._toDouble(d2);
return new Complex(Probability.fcdf(this._re, d1_, d2_));
}
/**
* Inverse function of cumulative distribution function (CDF) of F-distribution.
* - Calculate from real values.
* @param {KComplexInputData} d1 - The degree of freedom of the molecules.
* @param {KComplexInputData} d2 - The degree of freedom of the denominator
* @returns {Complex}
*/
finv(d1, d2) {
const d1_ = Complex._toDouble(d1);
const d2_ = Complex._toDouble(d2);
return new Complex(Probability.finv(this._re, d1_, d2_));
}
// ----------------------
// ビット演算系
// ----------------------
/**
* Logical AND.
* - Calculated as an integer.
* @param {KComplexInputData} number
* @returns {Complex} A & B
*/
and(number) {
const n_src = Math.round(this.real);
const n_tgt = Math.round(Complex._toDouble(number));
return new Complex(n_src & n_tgt);
}
/**
* Logical OR.
* - Calculated as an integer.
* @param {KComplexInputData} number
* @returns {Complex} A | B
*/
or(number) {
const n_src = Math.round(this.real);
const n_tgt = Math.round(Complex._toDouble(number));
return new Complex(n_src | n_tgt);
}
/**
* Logical Exclusive-OR.
* - Calculated as an integer.
* @param {KComplexInputData} number
* @returns {Complex} A ^ B
*/
xor(number) {
const n_src = Math.round(this.real);
const n_tgt = Math.round(Complex._toDouble(number));
return new Complex(n_src ^ n_tgt);
}
/**
* Logical Not. (mutable)
* - Calculated as an integer.
* @returns {Complex} !A
*/
not() {
const n_src = Math.round(this.real);
return new Complex(!n_src);
}
/**
* this << n
* - Calculated as an integer.
* @param {KComplexInputData} n
* @returns {Complex} A << n
*/
shift(n) {
const src = Math.round(this.real);
const number = Math.round(Complex._toDouble(n));
return new Complex(src << number);
}
// ----------------------
// factor
// ----------------------
/**
* Factorization.
* - Calculated as an integer.
* - Calculate up to `9007199254740991`.
* @returns {Complex[]} factor
*/
factor() {
const x = this.round().toBigInteger().factor();
const y = [];
for(let i = 0; i < x.length; i++) {
y.push(new Complex(x[i]));
}
return y;
}
// ----------------------
// gcd, lcm
// ----------------------
/**
* Euclidean algorithm.
* - Calculated as an integer.
* @param {KComplexInputData} number
* @returns {Complex} gcd(x, y)
*/
gcd(number) {
const x = Math.round(this.real);
const y = Math.round(Complex._toDouble(number));
const result = new BigInteger(x).gcd(y);
return new Complex(result);
}
/**
* Extended Euclidean algorithm.
* - Calculated as an integer.
* @param {KComplexInputData} number
* @returns {Array<Complex>} [a, b, gcd(x, y)], Result of calculating a*x + b*y = gcd(x, y).
*/
extgcd(number) {
const x = Math.round(this.real);
const y = Math.round(Complex._toDouble(number));
const result = new BigInteger(x).extgcd(y);
return [new Complex(result[0]), new Complex(result[1]), new Complex(result[2])];
}
/**
* Least common multiple.
* - Calculated as an integer.
* @param {KComplexInputData} number
* @returns {Complex} lcm(x, y)
*/
lcm(number) {
const x = Math.round(this.real);
const y = Math.round(Complex._toDouble(number));
const result = new BigInteger(x).lcm(y);
return new Complex(result);
}
// ----------------------
// mod
// ----------------------
/**
* Modular exponentiation.
* - Calculated as an integer.
* @param {KComplexInputData} exponent
* @param {KComplexInputData} m
* @returns {Complex} A^B mod m
*/
modPow(exponent, m) {
const A = Math.round(this.real);
const B = Math.round(Complex._toDouble(exponent));
const m_ = Math.round(Complex._toDouble(m));
const result = new BigInteger(A).modPow(B, m_);
return new Complex(result);
}
/**
* Modular multiplicative inverse.
* - Calculated as an integer.
* @param {KComplexInputData} m
* @returns {Complex} A^(-1) mod m
*/
modInverse(m) {
const A = Math.round(this.real);
const m_ = Math.round(Complex._toDouble(m));
const result = new BigInteger(A).modInverse(m_);
return new Complex(result);
}
// ----------------------
// その他の演算
// ----------------------
/**
* Multiply a multiple of ten.
* @param {KComplexInputData} n
* @returns {Complex} x * 10^n
*/
scaleByPowerOfTen(n) {
return this.mul(Complex.TEN.pow(n));
}
// ----------------------
// 素数
// ----------------------
/**
* Return true if the value is prime number.
* - Calculated as an integer.
* - Calculate up to `9007199254740991`.
* @returns {boolean} - If the calculation range is exceeded, null is returned.
*/
isPrime() {
const src = new BigInteger(Math.round(this.real));
return src.isPrime();
}
/**
* Return true if the value is prime number by Miller-Labin prime number determination method.
*
* Attention : it takes a very long time to process.
* - Calculated as an integer.
* @param {KComplexInputData} [certainty=100] - Repeat count (prime precision).
* @returns {boolean}
*/
isProbablePrime(certainty) {
const src = new BigInteger(Math.round(this.real));
return src.isProbablePrime(certainty !== undefined ? Math.round(Complex._toDouble(certainty)) : undefined);
}
/**
* Next prime.
* @param {KComplexInputData} [certainty=100] - Repeat count (prime precision).
* @param {KComplexInputData} [search_max=100000] - Search range of next prime.
* @returns {Complex}
*/
nextProbablePrime(certainty, search_max) {
const src = new BigInteger(Math.round(this.real));
const p1 = certainty !== undefined ? Math.round(Complex._toDouble(certainty)) : undefined;
const p2 = search_max !== undefined ? Math.round(Complex._toDouble(search_max)) : undefined;
return new Complex(src.nextProbablePrime(p1, p2));
}
// ----------------------
// 定数
// ----------------------
/**
* 1
* @returns {Complex} 1
*/
static get ONE() {
return DEFINE.ONE;
}
/**
* 2
* @returns {Complex} 2
*/
static get TWO() {
return DEFINE.TWO;
}
/**
* 10
* @returns {Complex} 10
*/
static get TEN() {
return DEFINE.TEN;
}
/**
* 0
* @returns {Complex} 0
*/
static get ZERO() {
return DEFINE.ZERO;
}
/**
* -1
* @returns {Complex} -1
*/
static get MINUS_ONE() {
return DEFINE.MINUS_ONE;
}
/**
* i, j
* @returns {Complex} i
*/
static get I() {
return DEFINE.I;
}
/**
* - i, - j
* @returns {Complex} - i
*/
static get MINUS_I() {
return DEFINE.MINUS_I;
}
/**
* PI.
* @returns {Complex} 3.14...
*/
static get PI() {
return DEFINE.PI;
}
/**
* 0.25 * PI.
* @returns {Complex} 0.78...
*/
static get QUARTER_PI() {
return DEFINE.QUARTER_PI;
}
/**
* 0.5 * PI.
* @returns {Complex} 1.57...
*/
static get HALF_PI() {
return DEFINE.HALF_PI;
}
/**
* 2 * PI.
* @returns {Complex} 6.28...
*/
static get TWO_PI() {
return DEFINE.TWO_PI;
}
/**
* E, Napier's constant.
* @returns {Complex} 2.71...
*/
static get E() {
return DEFINE.E;
}
/**
* log_e(2)
* @returns {Complex} ln(2)
*/
static get LN2() {
return DEFINE.LN2;
}
/**
* log_e(10)
* @returns {Complex} ln(10)
*/
static get LN10() {
return DEFINE.LN10;
}
/**
* log_2(e)
* @returns {Complex} log_2(e)
*/
static get LOG2E() {
return DEFINE.LOG2E;
}
/**
* log_10(e)
* @returns {Complex} log_10(e)
*/
static get LOG10E() {
return DEFINE.LOG10E;
}
/**
* sqrt(2)
* @returns {Complex} sqrt(2)
*/
static get SQRT2() {
return DEFINE.SQRT2;
}
/**
* sqrt(0.5)
* @returns {Complex} sqrt(0.5)
*/
static get SQRT1_2() {
return DEFINE.SQRT1_2;
}
/**
* 0.5
* @returns {Complex} 0.5
*/
static get HALF() {
return DEFINE.HALF;
}
/**
* Positive infinity.
* @returns {Complex} Infinity
*/
static get POSITIVE_INFINITY() {
return DEFINE.POSITIVE_INFINITY;
}
/**
* Negative Infinity.
* @returns {Complex} -Infinity
*/
static get NEGATIVE_INFINITY() {
return DEFINE.NEGATIVE_INFINITY;
}
/**
* Not a Number.
* @returns {Complex} NaN
*/
static get NaN() {
return DEFINE.NaN;
}
// ----------------------
// 互換性
// ----------------------
/**
* The positive or negative sign of this number.
* - +1 if positive, -1 if negative, 0 if 0.
* @returns {Complex}
*/
signum() {
return this.sign();
}
/**
* Subtract.
* @param {KComplexInputData} number
* @returns {Complex} A - B
*/
subtract(number) {
return this.sub(number);
}
/**
* Multiply.
* @param {KComplexInputData} number
* @returns {Complex} A * B
*/
multiply(number) {
return this.mul(number);
}
/**
* Divide.
* @param {KComplexInputData} number
* @returns {Complex} fix(A / B)
*/
divide(number) {
return this.div(number);
}
/**
* Remainder of division.
* - Result has same sign as the Dividend.
* @param {KComplexInputData} number
* @returns {Complex} A % B
*/
remainder(number) {
return this.rem(number);
}
/**
* To integer rounded down to the nearest.
* @returns {Complex} fix(A), trunc(A)
*/
trunc() {
return this.fix();
}
}
/**
* Collection of constant values used in the class.
* @ignore
*/
const DEFINE = {
/**
* 0
*/
ZERO : new Complex(0),
/**
* 1
*/
ONE : new Complex(1),
/**
* 2
*/
TWO : new Complex(2),
/**
* 10
*/
TEN : new Complex(10),
/**
* -1
*/
MINUS_ONE : new Complex(-1),
/**
* i, j
*/
I : new Complex([0, 1]),
/**
* - i, - j
*/
MINUS_I : new Complex([0, -1]),
/**
* PI.
*/
PI : new Complex(Math.PI),
/**
* 0.25 * PI.
*/
QUARTER_PI : new Complex(0.25 * Math.PI),
/**
* 0.5 * PI.
*/
HALF_PI : new Complex(0.5 * Math.PI),
/**
* 2 * PI.
*/
TWO_PI : new Complex(2.0 * Math.PI),
/**
* E, Napier's constant.
*/
E : new Complex(Math.E),
/**
* log_e(2)
*/
LN2 : new Complex(Math.LN2),
/**
* log_e(10)
*/
LN10 : new Complex(Math.LN10),
/**
* log_2(e)
*/
LOG2E : new Complex(Math.LOG2E),
/**
* log_10(e)
*/
LOG10E : new Complex(Math.LOG10E),
/**
* sqrt(2)
*/
SQRT2 : new Complex(Math.SQRT2),
/**
* sqrt(0.5)
*/
SQRT1_2 : new Complex(Math.SQRT1_2),
/**
* 0.5
*/
HALF : new Complex(0.5),
/**
* Positive infinity.
*/
POSITIVE_INFINITY : new Complex(Number.POSITIVE_INFINITY),
/**
* Negative Infinity.
*/
NEGATIVE_INFINITY : new Complex(Number.NEGATIVE_INFINITY),
/**
* Not a Number.
*/
NaN : new Complex(Number.NaN)
};