Home Reference Source

src/math/core/Fraction.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 BigInteger from "./BigInteger.js";
import BigDecimal from "./BigDecimal.js";
import MathContext from "./context/MathContext.js";
import Complex from "./Complex.js";
import Matrix from "./Matrix.js";
import KonpeitoInteger from "./base/KonpeitoInteger.js";

/**
 * Fraction type argument.
 * - Fraction
 * - BigInteger
 * - BigDecimal
 * - number
 * - boolean
 * - string
 * - Array<KBigIntegerInputData>
 * - {numerator:KBigIntegerInputData,denominator:KBigIntegerInputData}
 * - {doubleValue:number}
 * - {toString:function}
 * 
 * Initialization can be performed as follows.
 * - 10, "10", "10/1", "10.0/1.0", ["10", "1"], [10, 1]
 * - 0.01, "0.01", "0.1e-1", "1/100", [1, 100], [2, 200], ["2", "200"]
 * - "1/3", "0.[3]", "0.(3)", "0.'3'", "0."3"", [1, 3], [2, 6]
 * - "3.555(123)" = 3.555123123123..., "147982 / 41625"
 * @typedef {Fraction|BigInteger|BigDecimal|number|boolean|string|Array<import("./BigInteger.js").KBigIntegerInputData>|{numerator:import("./BigInteger.js").KBigIntegerInputData,denominator:import("./BigInteger.js").KBigIntegerInputData}|{doubleValue:number}|{toString:function}} KFractionInputData
 */

/**
 * Collection of functions used in Fraction.
 * @ignore
 */
class FractionTool {

	/**
	 * Create data for Fraction from strings.
	 * @param ntext {string}
	 * @return {Fraction}
	 */
	static to_fraction_data_from_number_string(ntext) {
		let scale = 0;
		let buff;
		let is_negate = false;
		// 正規化
		let text = ntext.replace(/\s/g, "").toLowerCase();
		// +-の符号があるか
		const number_text = [];
		buff = text.match(/^[+-]+/);
		if(buff !== null) {
			buff = buff[0];
			text = text.substr(buff.length);
			if(buff.indexOf("-") !== -1) {
				is_negate = true;
				number_text.push("-");
			}
		}
		// 整数部があるか
		buff = text.match(/^[0-9]+/);
		if(buff !== null) {
			buff = buff[0];
			text = text.substr(buff.length);
			number_text.push(buff);
		}
		// 浮動小数点の計算がない場合はここで完了
		if(text.length === 0) {
			return new Fraction([new BigInteger([number_text.join(""), 10]), BigInteger.ONE]);
		}
		// 巡回小数点指定があるか
		let cyclic_decimal = null;
		if(/[()'"[\]]/.test(text)) {
			const match_data = text.match(/([^.]*)\.(\d*)[(['"](\d+)[)\]'"](.*)/);
			if(match_data === null) {
				throw "Fraction Unsupported argument " + text;
			}
			// 巡回少数の場所
			const cyclic_decimal_scale = match_data[2].length;
			const cyclic_decimal_text = match_data[3];
			// 巡回少数以外を抽出
			if(cyclic_decimal_scale === 0) {
				text = match_data[1] + match_data[4];
			}
			else {
				text = match_data[1] + "." + match_data[2] + match_data[4];
			}

			const numerator = new BigInteger([cyclic_decimal_text, 10]);
			const denominator_string = [];
			for(let i = 0; i < cyclic_decimal_text.length; i++) {
				denominator_string.push("9");
			}
			const denominator = new BigInteger([denominator_string.join(""), 10]);
			cyclic_decimal = new Fraction([numerator, denominator]);
			cyclic_decimal = cyclic_decimal.scaleByPowerOfTen(-cyclic_decimal_scale);
		}
		// 小数部があるか
		buff = text.match(/^\.[0-9]+/);
		if(buff !== null) {
			buff = buff[0];
			text = text.substr(buff.length);
			buff = buff.substr(1);
			scale = scale + buff.length;
			number_text.push(buff);
		}
		// 指数表記があるか
		buff = text.match(/^e[+-]?[0-9]+/);
		if(buff !== null) {
			buff = buff[0].substr(1);
			scale = scale - parseInt(buff, 10);
		}

		let f = null;
		{
			let numerator = null;
			let denominator = null;
			// 出力用の文字を作成
			if(scale === 0) {
				numerator = new BigInteger([number_text.join(""), 10]);
				denominator = BigInteger.ONE;
			}
			if(scale < 0) {
				for(let i = 0; i < -scale; i++) {
					number_text.push("0");
				}
				numerator = new BigInteger([number_text.join(""), 10]);
				denominator = BigInteger.ONE;
			}
			else if(scale > 0) {
				numerator = new BigInteger([number_text.join(""), 10]);
				const denominator_string = ["1"];
				for(let i = 0; i < scale; i++) {
					denominator_string.push("0");
				}
				denominator = new BigInteger([denominator_string.join(""), 10]);
			}
			f = new Fraction([numerator, denominator]);
		}
		if(cyclic_decimal) {
			if(!is_negate) {
				f = f.add(cyclic_decimal);
			}
			else {
				f = f.sub(cyclic_decimal);
			}
		}
		return f;
	}

	/**
	 * Create data for Fraction from fractional string.
	 * @param ntext {string}
	 * @return {Fraction}
	 */
	static to_fraction_data_from_fraction_string(ntext) {
		// 特殊な状態
		if(/nan|inf/i.test(ntext)) {
			const ret = new Fraction();
			ret.denominator = BigInteger.ONE;
			if(/nan/i.test(ntext)) {
				ret.numerator = BigInteger.NaN;
			}
			else if(!/-/.test(ntext)) {
				ret.numerator = BigInteger.POSITIVE_INFINITY;
			}
			else {
				ret.numerator = BigInteger.NEGATIVE_INFINITY;
			}
			return ret;
		}
		if(ntext.indexOf("/") === -1) {
			return FractionTool.to_fraction_data_from_number_string(ntext);
		}
		else {
			const fraction_value = ntext.split("/");
			const numerator_value = FractionTool.to_fraction_data_from_number_string(fraction_value[0]);
			const denominator_value = FractionTool.to_fraction_data_from_number_string(fraction_value[1]);
			return numerator_value.div(denominator_value);
		}
	}

	/**
	 * Create data for Fraction from number.
	 * @param number {number|boolean}
	 * @return {Fraction}
	 */
	static to_fraction_data_from_number(number) {
		const value = typeof number !== "boolean" ? number : (number ? 1 : 0);
		let numerator = null;
		let denominator = null;
		if(!isFinite(value)) {
			const ret = new Fraction();
			ret.denominator = BigInteger.ONE;
			if(value === Infinity) {
				ret.numerator = BigInteger.POSITIVE_INFINITY;
			}
			else if(value === - Infinity) {
				ret.numerator = BigInteger.NEGATIVE_INFINITY;
			}
			else {
				ret.numerator = BigInteger.NaN;
			}
			return ret;
		}
		// 0.0
		else if(value === 0.0) {
			const ret = new Fraction();
			ret.denominator = BigInteger.ONE;
			ret.numerator = BigInteger.ZERO;
			return ret;
		}
		// 整数
		else if( Math.abs(value - Math.round(value)) <= Number.EPSILON) {
			numerator = new BigInteger(Math.round(value));
			denominator = BigInteger.ONE;
		}
		// 浮動小数
		else {
			let scale = Math.trunc(Math.log(Math.abs(value)) / Math.log(10));
			let x = value / Math.pow(10, scale);
			// スケールを逆にする
			scale = - scale;
			for(let i = 0; i < 14; i++) {
				x = x * 10;
				scale = scale + 1;
				if(Math.abs(x - Math.round(x)) <= Number.EPSILON) {
					break;
				}
			}
			// 最も下の桁は四捨五入する
			x = Math.round(x * 1e14) / 1e14;
			if(scale <= 0) {
				numerator = new BigInteger(value);
				denominator = BigInteger.ONE;
			}
			else {
				numerator = new BigInteger(x);
				const denominator_string = ["1"];
				for(let i = 0; i < scale; i++) {
					denominator_string.push("0");
				}
				denominator = new BigInteger([denominator_string.join(""), 10]);
			}
		}
		return new Fraction([numerator, denominator]);
	}

	/**
	 * Normalization.
	 * - Reduce fraction using gcd.
	 * - Add the sign to the numerator.
	 * - If the number is zero, the denominator is one.
	 * @param value {Fraction}
	 * @returns {void}
	 */
	static normalization(value) {
		if(value.denominator.equals(BigInteger.ONE)) {
			return;
		}
		if(value.denominator.equals(BigInteger.MINUS_ONE)) {
			value.numerator = value.numerator.negate();
			value.denominator = BigInteger.ONE;
			return;
		}
		if(value.numerator.equals(BigInteger.ZERO)) {
			value.denominator = BigInteger.ONE;
			return;
		}
		const gcd = value.numerator.gcd(value.denominator);
		let numerator = value.numerator.div(gcd);
		let denominator = value.denominator.div(gcd);
		if(denominator.sign() < 0) {
			numerator = numerator.negate();
			denominator = denominator.negate();
		}
		value.numerator = numerator;
		value.denominator = denominator;
	}

}

/**
 * Fraction class (immutable).
 */
export default class Fraction extends KonpeitoInteger {

	/**
	 * Create an fraction.
	 * 
	 * Initialization can be performed as follows.
	 * - 10, "10", "10/1", "10.0/1.0", ["10", "1"], [10, 1]
	 * - 0.01, "0.01", "0.1e-1", "1/100", [1, 100], [2, 200], ["2", "200"]
	 * - "1/3", "0.[3]", "0.(3)", "0.'3'", "0."3"", [1, 3], [2, 6]
	 * - "3.555(123)" = 3.555123123123..., "147982 / 41625"
	 * @param {KFractionInputData} [number] - Fraction data. See how to use the function.
	 */
	constructor(number) {
		super();
		
		// 分子
		/**
		 * numerator
		 * @type {BigInteger}
		 */
		this.numerator = null;

		// 分母
		/**
		 * denominator
		 * @type {BigInteger}
		 */
		this.denominator = null;

		if(arguments.length === 0) {
			this.numerator = BigInteger.ZERO;
			this.denominator = BigInteger.ONE;
		}
		else if(arguments.length === 1) {
			let is_normalization = false;
			if((typeof number === "number") || (typeof number === "boolean")) {
				const x = FractionTool.to_fraction_data_from_number(number);
				this.numerator = x.numerator;
				this.denominator = x.denominator;
			}
			else if(typeof number === "string") {
				const x = FractionTool.to_fraction_data_from_fraction_string(number);
				this.numerator = x.numerator;
				this.denominator = x.denominator;
			}
			else if(number instanceof BigInteger) {
				this.numerator = number;
				this.denominator = BigInteger.ONE;
			}
			else if(number instanceof Fraction) {
				this.numerator = number.numerator;
				this.denominator = number.denominator;
			}
			else if((number instanceof Array) && (number.length === 2)) {
				this.numerator = (number[0] instanceof BigInteger) ? number[0] : new BigInteger(number[0]);
				this.denominator = (number[1] instanceof BigInteger) ? number[1] : new BigInteger(number[1]);
				is_normalization = true;
			}
			else if(number instanceof BigDecimal) {
				const bigint = number.unscaledValue();
				if(!bigint.isFinite()) {
					this.numerator = bigint;
					this.denominator = BigInteger.ONE;
				}
				else {
					const value = new Fraction(number.unscaledValue());
					const x = value.scaleByPowerOfTen(-number.scale());
					this.numerator = x.numerator;
					this.denominator = x.denominator;
				}
			}
			else if(typeof number === "object") {
				if("doubleValue" in number) {
					const x = FractionTool.to_fraction_data_from_number(number.doubleValue);
					this.numerator = x.numerator;
					this.denominator = x.denominator;
				}
				else if(("numerator" in number) && ("denominator" in number)) {
					this.numerator = (number.numerator instanceof BigInteger) ? number.numerator : new BigInteger(number.numerator);
					this.denominator = (number.denominator instanceof BigInteger) ? number.denominator : new BigInteger(number.denominator);
					is_normalization = true;
				}
				else {
					const x1 = FractionTool.to_fraction_data_from_fraction_string(number.toString());
					this.numerator = x1.numerator;
					this.denominator = x1.denominator;
				}
			}
			else {
				throw "Fraction Unsupported argument " + number;
			}
			if(is_normalization) {
				FractionTool.normalization(this);
			}
		}
		else {
			throw "Fraction Unsupported argument " + number;
		}
	}

	/**
	 * Create an entity object of this class.
	 * @param {KFractionInputData} number 
	 * @returns {Fraction}
	 */
	static create(number) {
		if(number instanceof Fraction) {
			return number;
		}
		else {
			return new Fraction(number);
		}
	}

	/**
	 * Convert number to Fraction type.
	 * @param {KFractionInputData} number 
	 * @returns {Fraction}
	 */
	static valueOf(number) {
		return Fraction.create(number);
	}

	/**
	 * Convert to Fraction.
	 * @param {KFractionInputData} number 
	 * @returns {Fraction}
	 * @ignore
	 */
	static _toFraction(number) {
		if(number instanceof Fraction) {
			return number;
		}
		else {
			return new Fraction(number);
		}
	}

	/**
	 * Convert to real number.
	 * @param {KFractionInputData} number 
	 * @returns {number}
	 * @ignore
	 */
	static _toFloat(number) {
		if(typeof number === "number") {
			return number;
		}
		else if(number instanceof Fraction) {
			return number.doubleValue;
		}
		else {
			return (new Fraction(number)).doubleValue;
		}
	}

	/**
	 * Convert to integer.
	 * @param {KFractionInputData} number 
	 * @returns {number}
	 * @ignore
	 */
	static _toInteger(number) {
		if(typeof number === "number") {
			return Math.trunc(number);
		}
		else if(number instanceof Fraction) {
			return number.intValue;
		}
		else {
			return (new Fraction(number)).intValue;
		}
	}

	/**
	 * Deep copy.
	 * @returns {Fraction} 
	 */
	clone() {
		return new Fraction(this);
	}

	/**
	 * Absolute value.
	 * @returns {Fraction} abs(A)
	 */
	abs() {
		if(!this.isFinite()) {
			return this.isNegativeInfinity() ? Fraction.POSITIVE_INFINITY : this;
		}
		if(this.sign() >= 0) {
			return this;
		}
		return this.negate();
	}

	/**
	 * this * -1
	 * @returns {Fraction} -A
	 */
	negate() {
		if(!this.isFinite()) {
			if(this.isPositiveInfinity()) {
				return Fraction.NEGATIVE_INFINITY;
			}
			else if(this.isNegativeInfinity()) {
				return Fraction.POSITIVE_INFINITY;
			}
			else {
				return this;
			}
		}
		return new Fraction([this.numerator.negate(), this.denominator]);
	}

	/**
	 * The positive or negative sign of this number.
	 * - +1 if positive, -1 if negative, 0 if 0.
	 * @returns {number}
	 */
	sign() {
		if(!this.isFinite()) {
			return this.isNaN() ? NaN : (this.isPositiveInfinity() ? 1 : -1);
		}
		return this.numerator.sign();
	}
	
	/**
	 * Convert to string.
	 * @returns {string} 
	 */
	toString() {
		if(!this.isFinite()) {
			return this.isNaN() ? "NaN" : (this.isPositiveInfinity() ? "Infinity" : "-Infinity");
		}
		if(this.isInteger()) {
			return this.numerator.toString();
		}
		return this.toFractionString();
	}

	/**
	 * Convert to JSON.
	 * @returns {string} 
	 */
	toJSON() {
		return this.toString();
	}

	/**
	 * Convert to string. For example, output `1/3` if `0.333...`.
	 * @returns {string} 
	 */
	toFractionString() {
		if(!this.isFinite()) {
			return this.isNaN() ? "NaN" : (this.isPositiveInfinity() ? "Infinity" : "-Infinity");
		}
		return this.numerator.toString() + "/" + this.denominator.toString();
	}

	/**
	 * Convert to string. For example, output `0.(3)` if `0.333...`.
	 * @param {KFractionInputData} [depth_max] - Maximum number of divisions.
	 * @returns {string} 
	 */
	toPlainString(depth_max) {
		if(!this.isFinite()) {
			return this.isNaN() ? "NaN" : (this.isPositiveInfinity() ? "Infinity" : "-Infinity");
		}
		if(this.isInteger()) {
			return this.numerator.toString();
		}
		const sign = this.numerator.sign();
		let src = this.numerator.abs();
		const tgt = this.denominator;
		let output = null;
		const output_array = [];
		/**
		 * @type {any}
		 */
		const remaind_map = {};
		let check_max;
		if(depth_max !== undefined) {
			check_max = Fraction._toInteger(depth_max);
		}
		else {
			check_max = (src.toString().length + tgt.toString().length) * 10;
		}
		for(let i = 0; i < check_max; i++) {
			const result = src.divideAndRemainder(tgt);
			const result_divide		= result[0];
			const result_remaind	= result[1];
			const remstr = result_remaind.toString();
			output_array.push(result_divide.toString());
			// 同一の余りがあるということは循環小数
			if(remaind_map[remstr] !== undefined) {
				output = output_array.join("");
				const n = output.indexOf(".") + remaind_map[remstr] + 1;
				output = output.substr(0, n) + "(" + output.substr(n, output.length - n) + ")";
				break;
			}
			else {
				remaind_map[remstr] = i;
			}
			if(result_remaind.isZero()) {
				break;
			}
			if(i === 0) {
				output_array.push(".");
			}
			src = result_remaind.scaleByPowerOfTen(1);
		}
		if(output === null) {
			output = output = output_array.join("");
		}
		return (sign < 0 ? "-" : "") + output;
	}

	// ----------------------
	// 四則演算
	// ----------------------
	
	/**
	 * Add.
	 * @param {KFractionInputData} num
	 * @return {Fraction} A + B
	 */
	add(num) {
		const x = this;
		const y = Fraction._toFraction(num);
		if(!x.isFinite() || !y.isFinite()) {
			if(x.isNaN() || y.isNaN() || (x.isInfinite() && y.isInfinite() && !x.equalsState(y))) {
				return Fraction.NaN;
			}
			else if(x.isPositiveInfinity() || y.isPositiveInfinity()) {
				return Fraction.POSITIVE_INFINITY;
			}
			else {
				return Fraction.NEGATIVE_INFINITY;
			}
		}
		let f;
		if(x.isInteger() && y.isInteger()) {
			f = new Fraction([ x.numerator.add(y.numerator), BigInteger.ONE]);
		}
		else {
			f = new Fraction([
				x.numerator.mul(y.denominator).add(y.numerator.mul(x.denominator)),
				x.denominator.mul(y.denominator)
			]);
		}
		return f;
	}

	/**
	 * Subtract.
	 * @param {KFractionInputData} num
	 * @return {Fraction} A - B
	 */
	sub(num) {
		const x = this;
		const y = Fraction._toFraction(num);
		if(!x.isFinite() || !y.isFinite()) {
			if(x.isNaN() || y.isNaN() || x.equalsState(y)) {
				return Fraction.NaN;
			}
			else if(x.isNegativeInfinity() || y.isPositiveInfinity()) {
				return Fraction.NEGATIVE_INFINITY;
			}
			else {
				return Fraction.POSITIVE_INFINITY;
			}
		}
		let f;
		if(x.isInteger() && y.isInteger()) {
			f = new Fraction([ x.numerator.sub(y.numerator), BigInteger.ONE]);
		}
		else {
			f = new Fraction([
				x.numerator.mul(y.denominator).sub(y.numerator.mul(x.denominator)),
				x.denominator.mul(y.denominator)
			]);
		}
		return f;
	}

	/**
	 * Multiply.
	 * @param {KFractionInputData} num
	 * @return {Fraction} A * B
	 */
	mul(num) {
		const x = this;
		const y = Fraction._toFraction(num);
		if(!x.isFinite() || !y.isFinite()) {
			if(x.isNaN() || y.isNaN() || (x.isZero() || y.isZero())) {
				return Fraction.NaN;
			}
			else if(x.sign() * y.sign() > 0) {
				return Fraction.POSITIVE_INFINITY;
			}
			else {
				return Fraction.NEGATIVE_INFINITY;
			}
		}
		let f;
		if(x.isInteger() && y.isInteger()) {
			f = new Fraction([ x.numerator.mul(y.numerator), BigInteger.ONE]);
		}
		else {
			f = new Fraction([ x.numerator.mul(y.numerator), x.denominator.mul(y.denominator) ]);
		}
		return f;
	}

	/**
	 * Divide.
	 * @param {KFractionInputData} num
	 * @return {Fraction} A / B
	 */
	div(num) {
		const x = this;
		const y = Fraction._toFraction(num);
		if(!x.isFinite() || !y.isFinite()) {
			if(x.isNaN() || y.isNaN() || (x.isInfinite() && y.isInfinite())) {
				return Fraction.NaN;
			}
			else if(x.isInfinite()) {
				if(x.sign() * y.sign() >= 0) {
					return Fraction.POSITIVE_INFINITY;
				}
				else {
					return Fraction.NEGATIVE_INFINITY;
				}
			}
			else {
				return Fraction.ZERO;
			}
		}
		else if(y.isZero()) {
			if(x.isZero()) {
				return Fraction.NaN;
			}
			else {
				return x.sign() >= 0 ? Fraction.POSITIVE_INFINITY : Fraction.NEGATIVE_INFINITY;
			}
		}
		let f;
		if(x.isInteger() && y.isInteger()) {
			f = new Fraction([ x.numerator, y.numerator]);
		}
		else {
			f = new Fraction([ x.numerator.mul(y.denominator), y.numerator.mul(x.denominator)]);
		}
		return f;
	}

	/**
	 * Inverse number of this value.
	 * @return {Fraction} 1 / A
	 */
	inv() {
		{
			if(!this.isFinite()) {
				return this.isNaN() ? Fraction.NaN : Fraction.ZERO;
			}
			if(this.isZero()) {
				return Fraction.NaN;
			}
		}
		return new Fraction([ this.denominator, this.numerator]);
	}

	/**
	 * Modulo, positive remainder of division.
	 * - Result has same sign as the Dividend.
	 * @param {KFractionInputData} num
	 * @return {Fraction} A rem B
	 */
	rem(num) {
		const x = this;
		const y = Fraction._toFraction(num);
		if(!x.isFinite() || !y.isFinite() || y.isZero()) {
			return Fraction.NaN;
		}
		// x - y * fix(x/y)
		return x.sub(y.mul(x.div(y).fix()));
	}

	/**
	 * Modulo, positive remainder of division.
	 * - Result has same sign as the Divisor.
	 * @param {KFractionInputData} num
	 * @returns {Fraction} A mod B
	 */
	mod(num) {
		const x = this;
		const y = Fraction._toFraction(num);
		if(y.isZero()) {
			return x;
		}
		const ret = x.rem(y);
		if(!x.equalsState(y)) {
			return ret.add(y);
		}
		else {
			return ret;
		}
	}

	// ----------------------
	// 指数
	// ----------------------
	
	/**
	 * Power function.
	 * - Supports only integers.
	 * @param {KFractionInputData} num
	 * @returns {Fraction} pow(A, B)
	 */
	pow(num) {
		const x = this;
		const y = Fraction._toFraction(num);
		{
			if(x.isNaN() || y.isNaN()) {
				return Fraction.NaN;
			}
			if(y.isZero()) {
				return Fraction.ONE;
			}
			else if(x.isZero()) {
				return Fraction.ZERO;
			}
			else if(x.isOne()) {
				return Fraction.ONE;
			}
			else if(x.isInfinite()) {
				if(x.isPositiveInfinity()) {
					return Fraction.POSITIVE_INFINITY;
				}
				else {
					if(y.isPositiveInfinity()) {
						return Fraction.NaN;
					}
					else {
						return new Fraction(Infinity * Math.pow(-1, Math.round(y.doubleValue)));
					}
				}
			}
			else if(y.isInfinite()) {
				if(x.isNegative()) {
					// 複素数
					return Fraction.NaN;
				}
				if(x.compareTo(Fraction.ONE) < 0) {
					if(y.isPositiveInfinity()) {
						return Fraction.ZERO;
					}
					else if(y.isNegativeInfinity()) {
						return Fraction.POSITIVE_INFINITY;
					}
				}
				else {
					if(y.isPositiveInfinity()) {
						return Fraction.POSITIVE_INFINITY;
					}
					else if(y.isNegativeInfinity()) {
						return Fraction.ZERO;
					}
				}
			}
		}
		const numerator = x.numerator.pow(y.intValue);
		const denominator = x.denominator.pow(y.intValue);
		return new Fraction([ numerator, denominator ]);
	}

	/**
	 * Square.
	 * @returns {Fraction} pow(A, 2)
	 */
	square() {
		return this.mul(this);
	}

	// ----------------------
	// その他の演算
	// ----------------------
	
	/**
	 * Factorial function, x!.
	 * - Supports only integers.
	 * @returns {Fraction} n!
	 */
	factorial() {
		if(!this.isFinite()) {
			return this;
		}
		return new Fraction([this.toBigInteger().factorial(), Fraction.ONE]);
	}

	/**
	 * Multiply a multiple of ten.
	 * - Supports only integers.
	 * @param {KFractionInputData} n
	 * @returns {Fraction}
	 */
	scaleByPowerOfTen(n) {
		if(!this.isFinite()) {
			return this;
		}
		const scale = Fraction._toInteger(n);
		if(scale === 0) {
			return this;
		}
		let f;
		if(scale > 0) {
			f = new Fraction([ this.numerator.scaleByPowerOfTen(scale), this.denominator]);
		}
		else if(scale < 0) {
			f = new Fraction([ this.numerator, this.denominator.scaleByPowerOfTen(-scale)]);
		}
		return f;
	}

	// ----------------------
	// 他の型に変換用
	// ----------------------
	
	/**
	 * boolean value.
	 * @returns {boolean}
	 */
	get booleanValue() {
		return this.numerator.booleanValue;
	}

	/**
	 * integer value.
	 * @returns {number}
	 */
	get intValue() {
		if(!this.isFinite()) {
			return this.isNaN() ? NaN : (this.isPositiveInfinity() ? Infinity : -Infinity);
		}
		if(this.isInteger()) {
			return Math.trunc(this.numerator.doubleValue);
		}
		return Math.trunc(this.doubleValue);
	}

	/**
	 * floating point.
	 * @returns {number}
	 */
	get doubleValue() {
		if(!this.isFinite()) {
			return this.isNaN() ? NaN : (this.isPositiveInfinity() ? Infinity : -Infinity);
		}
		if(this.isInteger()) {
			return this.numerator.doubleValue;
		}
		const x = new BigDecimal([this.numerator, MathContext.UNLIMITED]);
		const y = new BigDecimal([this.denominator, MathContext.UNLIMITED]);
		return x.div(y, {context : MathContext.DECIMAL64}).doubleValue;
	}

	// ----------------------
	// konpeito で扱う数値型へ変換
	// ----------------------
	
	/**
	 * return BigInteger.
	 * @returns {BigInteger}
	 */
	toBigInteger() {
		return new BigInteger(this.fix().numerator);
	}
	
	/**
	 * return BigDecimal.
	 * @param {MathContext} [mc] - MathContext setting after calculation. 
	 * @returns {BigDecimal}
	 */
	toBigDecimal(mc) {
		if(!this.isFinite()) {
			return new BigDecimal(this.doubleValue);
		}
		if(this.isInteger()) {
			return new BigDecimal(this.numerator);
		}
		const x = new BigDecimal([this.numerator, MathContext.UNLIMITED]);
		const y = new BigDecimal([this.denominator, MathContext.UNLIMITED]);
		if(mc) {
			return x.div(y, {context: mc});
		}
		else {
			return x.div(y, {context: BigDecimal.getDefaultContext()});
		}
	}
	
	/**
	 * return Fraction.
	 * @returns {Fraction}
	 */
	toFraction() {
		return this;
	}
	
	/**
	 * return Complex.
	 * @returns {Complex}
	 */
	toComplex() {
		return new Complex(this);
	}
	
	/**
	 * return Matrix.
	 * @returns {Matrix}
	 */
	toMatrix() {
		return new Matrix(this);
	}

	// ----------------------
	// 比較
	// ----------------------
	
	/**
	 * Equals.
	 * @param {KFractionInputData} num
	 * @returns {boolean} A === B
	 */
	equals(num) {
		const x = this;
		const y = Fraction._toFraction(num);
		if(!x.isFinite() || !y.isFinite()) {
			if(x.isNaN() || y.isNaN()) {
				return false;
			}
			else if(x.equalsState(y)) {
				return true;
			}
			else {
				return false;
			}
		}
		return x.numerator.equals(y.numerator) && x.denominator.equals(y.denominator);
	}

	/**
	 * Numeric type match.
	 * @param {KFractionInputData} number 
	 * @returns {boolean}
	 */
	equalsState(number) {
		const x = this;
		const y = Fraction._toFraction(number);
		return x.numerator.equalsState(y.numerator);
	}

	/**
	 * Compare values.
	 * @param {KFractionInputData} num
	 * @returns {number} A > B ? 1 : (A === B ? 0 : -1)
	 */
	compareTo(num) {
		const x = this;
		const y = Fraction._toFraction(num);
		if(!x.isFinite() || !y.isFinite()) {
			return x.numerator.compareTo(y.numerator);
		}
		return x.sub(y).sign();
	}

	/**
	 * Maximum number.
	 * @param {KFractionInputData} number
	 * @returns {Fraction} max([A, B])
	 */
	max(number) {
		const val = Fraction._toFraction(number);
		if(this.isNaN() || val.isNaN()) {
			return Fraction.NaN;
		}
		if(this.compareTo(val) >= 0) {
			return this;
		}
		else {
			return val;
		}
	}

	/**
	 * Minimum number.
	 * @param {KFractionInputData} number
	 * @returns {Fraction} min([A, B])
	 */
	min(number) {
		const val = Fraction._toFraction(number);
		if(this.isNaN() || val.isNaN()) {
			return Fraction.NaN;
		}
		if(this.compareTo(val) >= 0) {
			return val;
		}
		else {
			return this;
		}
	}

	/**
	 * Clip number within range.
	 * @param {KFractionInputData} min 
	 * @param {KFractionInputData} max
	 * @returns {Fraction} min(max(x, min), max)
	 */
	clip(min, max) {
		const min_ = Fraction._toFraction(min);
		const max_ = Fraction._toFraction(max);
		if(this.isNaN() || min_.isNaN() || max_.isNaN()) {
			return Fraction.NaN;
		}
		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 {Fraction} floor(A)
	 */
	floor() {
		if(this.isInteger() || !this.isFinite()) {
			return this;
		}
		const x = this.fix();
		if(this.sign() > 0) {
			return x;
		}
		else {
			return new Fraction([x.numerator.sub(BigInteger.ONE), Fraction.ONE]);
		}
	}

	/**
	 * Ceil.
	 * @returns {Fraction} ceil(A)
	 */
	ceil() {
		if(this.isInteger() || !this.isFinite()) {
			return this;
		}
		const x = this.fix();
		if(this.sign() > 0) {
			return new Fraction([x.numerator.add(BigInteger.ONE), Fraction.ONE]);
		}
		else {
			return x;
		}
	}
	
	/**
	 * Rounding to the nearest integer.
	 * @returns {Fraction} round(A)
	 */
	round() {
		if(this.isInteger() || !this.isFinite()) {
			return this;
		}
		const x = this.floor();
		const fract = this.sub(x);
		if(fract.compareTo(Fraction.HALF) >= 0) {
			return new Fraction([x.numerator.add(BigInteger.ONE), Fraction.ONE]);
		}
		else {
			return x;
		}
	}

	/**
	 * To integer rounded down to the nearest.
	 * @returns {Fraction} fix(A), trunc(A)
	 */
	fix() {
		if(this.isInteger() || !this.isFinite()) {
			return this;
		}
		return new Fraction([this.numerator.div(this.denominator), Fraction.ONE]);
	}

	/**
	 * Fraction.
	 * @returns {Fraction} fract(A)
	 */
	fract() {
		if(!this.isFinite()) {
			return Fraction.NaN;
		}
		if(this.isInteger()) {
			return Fraction.ZERO;
		}
		return this.sub(this.floor());
	}

	// ----------------------
	// テスト系
	// ----------------------
	
	/**
	 * Return true if the value is integer.
	 * @return {boolean}
	 */
	isInteger() {
		if(!this.isFinite()) {
			return false;
		}
		return this.denominator.equals(BigInteger.ONE);
	}

	/**
	 * this === 0
	 * @return {boolean} A === 0
	 */
	isZero() {
		if(!this.isFinite()) {
			return false;
		}
		return this.numerator.isZero();
	}

	/**
	 * this === 1
	 * @return {boolean} A === 1
	 */
	isOne() {
		if(!this.isFinite()) {
			return false;
		}
		return this.numerator.equals(BigInteger.ONE) && this.denominator.equals(BigInteger.ONE);
	}

	/**
	 * this > 0
	 * @returns {boolean}
	 */
	isPositive() {
		return this.numerator.isPositive();
	}

	/**
	 * this < 0
	 * @returns {boolean}
	 */
	isNegative() {
		return this.numerator.isNegative();
	}

	/**
	 * this >= 0
	 * @returns {boolean}
	 */
	isNotNegative() {
		return this.numerator.isNotNegative();
	}
	
	/**
	 * this === NaN
	 * @returns {boolean} isNaN(A)
	 */
	isNaN() {
		return this.numerator.isNaN();
	}
	
	/**
	 * this === Infinity
	 * @returns {boolean} isPositiveInfinity(A)
	 */
	isPositiveInfinity() {
		return this.numerator.isPositiveInfinity();
	}

	/**
	 * this === -Infinity
	 * @returns {boolean} isNegativeInfinity(A)
	 */
	isNegativeInfinity() {
		return this.numerator.isNegativeInfinity();
	}

	/**
	 * this === Infinity or -Infinity
	 * @returns {boolean} isPositiveInfinity(A) || isNegativeInfinity(A)
	 */
	isInfinite() {
		return this.numerator.isInfinite();
	}
	
	/**
	 * Return true if the value is finite number.
	 * @returns {boolean} !isNaN(A) && !isInfinite(A)
	 */
	isFinite() {
		return this.numerator.isFinite();
	}

	/**
	 * Return true if the value is repeating decimal.
	 * @returns {boolean} 
	 */
	isRepeatingDecimal() {
		if(!this.isFinite()) {
			return false;
		}
		return !this.isInteger() && !(this.denominator.rem(2).isZero() || this.denominator.rem(5).isZero());
	}

	// ----------------------
	// ビット演算系
	// ----------------------
	
	/**
	 * Logical AND.
	 * - Calculated as an integer.
	 * @param {KFractionInputData} number 
	 * @returns {Fraction} A & B
	 */
	and(number) {
		const n_src = this;
		const n_tgt = Fraction._toFraction(number);
		const src	= n_src.round().toBigInteger();
		const tgt	= n_tgt.round().toBigInteger();
		return new Fraction(src.and(tgt));
	}

	/**
	 * Logical OR.
	 * - Calculated as an integer.
	 * @param {KFractionInputData} number 
	 * @returns {Fraction} A | B
	 */
	or(number) {
		const n_src = this;
		const n_tgt = Fraction._toFraction(number);
		const src	= n_src.round().toBigInteger();
		const tgt	= n_tgt.round().toBigInteger();
		return new Fraction(src.or(tgt));
	}

	/**
	 * Logical Exclusive-OR.
	 * - Calculated as an integer.
	 * @param {KFractionInputData} number 
	 * @returns {Fraction} A ^ B
	 */
	xor(number) {
		const n_src = this;
		const n_tgt = Fraction._toFraction(number);
		const src	= n_src.round().toBigInteger();
		const tgt	= n_tgt.round().toBigInteger();
		return new Fraction(src.xor(tgt));
	}

	/**
	 * Logical Not. (mutable)
	 * - Calculated as an integer.
	 * @returns {Fraction} !A
	 */
	not() {
		const n_src = this;
		const src	= n_src.round().toBigInteger();
		return new Fraction(src.not());
	}
	
	/**
	 * this << n
	 * - Calculated as an integer.
	 * @param {KFractionInputData} n
	 * @returns {Fraction} A << n
	 */
	shift(n) {
		const src		= this.round().toBigInteger();
		const number	= Fraction._toInteger(n);
		return new Fraction(src.shift(number));
	}
	
	// ----------------------
	// factor
	// ----------------------

	/**
	 * Factorization.
	 * - Calculated as an integer.
	 * - Calculate up to `9007199254740991`.
	 * @returns {Fraction[]} factor
	 */
	factor() {
		const x = this.round().toBigInteger().factor();
		const y = [];
		for(let i = 0; i < x.length; i++) {
			y.push(new Fraction(x[i]));
		}
		return y;
	}

	// ----------------------
	// gcd, lcm
	// ----------------------
	
	/**
	 * Euclidean algorithm.
	 * - Calculated as an integer.
	 * @param {KFractionInputData} number 
	 * @returns {Fraction} gcd(x, y)
	 */
	gcd(number) {
		const x = this.round().toBigInteger();
		const y = Fraction._toFraction(number).toBigInteger();
		const result = x.gcd(y);
		return new Fraction(result);
	}

	/**
	 * Extended Euclidean algorithm.
	 * - Calculated as an integer.
	 * @param {KFractionInputData} number 
	 * @returns {Array<Fraction>} [a, b, gcd(x, y)], Result of calculating a*x + b*y = gcd(x, y).
	 */
	extgcd(number) {
		const x = this.round().toBigInteger();
		const y = Fraction._toFraction(number).toBigInteger();
		const result = x.extgcd(y);
		return [new Fraction(result[0]), new Fraction(result[1]), new Fraction(result[2])];
	}

	/**
	 * Least common multiple.
	 * - Calculated as an integer.
	 * @param {KFractionInputData} number 
	 * @returns {Fraction} lcm(x, y)
	 */
	lcm(number) {
		const x = this.round().toBigInteger();
		const y = Fraction._toFraction(number).toBigInteger();
		const result = x.lcm(y);
		return new Fraction(result);
	}

	// ----------------------
	// mod
	// ----------------------

	/**
	 * Modular exponentiation.
	 * - Calculated as an integer.
	 * @param {KFractionInputData} exponent
	 * @param {KFractionInputData} m 
	 * @returns {Fraction} A^B mod m
	 */
	modPow(exponent, m) {
		const A = this.round().toBigInteger();
		const B = Fraction._toFraction(exponent).toBigInteger();
		const m_ = Fraction._toFraction(m).toBigInteger();
		const result = A.modPow(B, m_);
		return new Fraction(result);
	}

	/**
	 * Modular multiplicative inverse.
	 * - Calculated as an integer.
	 * @param {KFractionInputData} m
	 * @returns {Fraction} A^(-1) mod m
	 */
	modInverse(m) {
		const A = this.round().toBigInteger();
		const m_ = Fraction._toFraction(m).toBigInteger();
		const result = A.modInverse(m_);
		return new Fraction(result);
	}
	
	// ----------------------
	// 素数
	// ----------------------
	
	/**
	 * 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 = this.round().toBigInteger();
		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 {KFractionInputData} [certainty=100] - Repeat count (prime precision).
	 * @returns {boolean}
	 */
	isProbablePrime(certainty) {
		const src = this.round().toBigInteger();
		return src.isProbablePrime(certainty !== undefined ? Fraction._toInteger(certainty) : undefined);
	}

	/**
	 * Next prime.
	 * @param {KFractionInputData} [certainty=100] - Repeat count (prime precision).
	 * @param {KFractionInputData} [search_max=100000] - Search range of next prime.
	 * @returns {Fraction}
	 */
	nextProbablePrime(certainty, search_max) {
		const src = this.round().toBigInteger();
		const p1 = certainty !== undefined ? Fraction._toInteger(certainty) : undefined;
		const p2 = search_max !== undefined ? Fraction._toInteger(search_max) : undefined;
		return new Fraction(src.nextProbablePrime(p1, p2));
	}

	// ----------------------
	// 定数
	// ----------------------
	
	/**
	 * -1
	 * @returns {Fraction} -1
	 */
	static get MINUS_ONE() {
		if(DEFINE.MINUS_ONE === null) {
			DEFINE.MINUS_ONE = new Fraction([BigInteger.MINUS_ONE, BigInteger.ONE]);
		}
		return DEFINE.MINUS_ONE;
	}

	/**
	 * 0
	 * @returns {Fraction} 0
	 */
	static get ZERO() {
		if(DEFINE.ZERO === null) {
			DEFINE.ZERO = new Fraction([BigInteger.ZERO, BigInteger.ONE]);
		}
		return DEFINE.ZERO;
	}

	/**
	 * 0.5
	 * @returns {Fraction} 0.5
	 */
	static get HALF() {
		if(DEFINE.HALF === null) {
			DEFINE.HALF = new Fraction([BigInteger.ONE, BigInteger.TWO]);
		}
		return DEFINE.HALF;
	}
	
	/**
	 * 1
	 * @returns {Fraction} 1
	 */
	static get ONE() {
		if(DEFINE.ONE === null) {
			DEFINE.ONE = new Fraction([BigInteger.ONE, BigInteger.ONE]);
		}
		return DEFINE.ONE;
	}
	
	/**
	 * 2
	 * @returns {Fraction} 2
	 */
	static get TWO() {
		if(DEFINE.TWO === null) {
			DEFINE.TWO = new Fraction([BigInteger.TWO, BigInteger.ONE]);
		}
		return DEFINE.TWO;
	}
	
	/**
	 * 10
	 * @returns {Fraction} 10
	 */
	static get TEN() {
		if(DEFINE.TEN === null) {
			DEFINE.TEN = new Fraction([BigInteger.TEN, BigInteger.ONE]);
		}
		return DEFINE.TEN;
	}

	/**
	 * Positive infinity.
	 * @returns {Fraction} Infinity
	 */
	static get POSITIVE_INFINITY() {
		if(DEFINE.POSITIVE_INFINITY === null) {
			DEFINE.POSITIVE_INFINITY = new Fraction(Number.POSITIVE_INFINITY);
		}
		return DEFINE.POSITIVE_INFINITY;
	}
	
	/**
	 * Negative Infinity.
	 * @returns {Fraction} -Infinity
	 */
	static get NEGATIVE_INFINITY() {
		if(DEFINE.NEGATIVE_INFINITY === null) {
			DEFINE.NEGATIVE_INFINITY = new Fraction(Number.NEGATIVE_INFINITY);
		}
		return DEFINE.NEGATIVE_INFINITY;
	}

	/**
	 * Not a Number.
	 * @returns {Fraction} NaN
	 */
	static get NaN() {
		if(DEFINE.NaN === null) {
			DEFINE.NaN = new Fraction(Number.NaN);
		}
		return DEFINE.NaN;
	}
	
	// ----------------------
	// 互換性
	// ----------------------
	
	/**
	 * The positive or negative sign of this number.
	 * - +1 if positive, -1 if negative, 0 if 0.
	 * @returns {number}
	 */
	signum() {
		return this.sign();
	}

	/**
	 * Subtract.
	 * @param {KFractionInputData} number
	 * @returns {Fraction} A - B
	 */
	subtract(number) {
		return this.sub(number);
	}

	/**
	 * Multiply.
	 * @param {KFractionInputData} number
	 * @returns {Fraction} A * B
	 */
	multiply(number) {
		return this.mul(number);
	}

	/**
	 * Divide.
	 * @param {KFractionInputData} number
	 * @returns {Fraction} fix(A / B)
	 */
	divide(number) {
		return this.div(number);
	}

	/**
	 * Remainder of division.
	 * - Result has same sign as the Dividend.
	 * @param {KFractionInputData} number
	 * @returns {Fraction} A % B
	 */
	remainder(number) {
		return this.rem(number);
	}

}

/**
 * Collection of constant values used in the class.
 * @ignore
 */
const DEFINE = {

	/**
	 * -1
	 * @type {any}
	 */
	MINUS_ONE : null,

	/**
	 * 0
	 * @type {any}
	 */
	ZERO : null,
	
	/**
	 * 1
	 * @type {any}
	 */
	ONE : null,

	/**
	 * 0.5
	 * @type {any}
	 */
	HALF : null,

	/**
	 * 2
	 * @type {any}
	 */
	TWO : null,

	/**
	 * 10
	 * @type {any}
	 */
	TEN : null,
	
	/**
	 * Positive infinity.
	 * @type {any}
	 */
	POSITIVE_INFINITY : null,

	/**
	 * Negative Infinity.
	 * @type {any}
	 */
	NEGATIVE_INFINITY : null,

	/**
	 * Not a Number.
	 * @type {any}
	 */
	NaN : null,

};