/** * @name CeL rational number function * @fileoverview * 本檔案包含了分數/有理數 (rational number)/比例值 (ratio) 的 functions,相當/類似於 BigRational, BigQuotient (numerator and denominator), BigDecimal。
* 在純 javascript 的環境下,藉由原生計算功能,盡可能提供高效的大數計算。
* * @example * * CeL.run('data.math.rational'); * * * @since 2013/9/8 13:42:58 * @see * https://en.wikipedia.org/wiki/Rational_number */ /* TODO: 以 BigInt() 重寫。 http://reference.wolfram.com/mathematica/ref/RootApproximant.html 規格書: rational = new Rational(numerator, denominator, base); rational = new Rational(10783, 2775); rational = new Rational('10783/2775'); rational = new Rational('3+2458/2775'); rational = new Rational('3 2458/2775'); //https://en.wikipedia.org/wiki/Vinculum_(symbol) rational = new Rational('3.88¯576'); //Brackets rational = new Rational('3.88(576)'); numerator 10783 denominator 2775 integer part 整數部分 == quotient == continued fraction[0] fractional part 分數/小數部分 == remainder / denominator mixed fraction 帶分數 == integer part + fractional part vulgar fraction 真分數/假分數 == decimal approximation (numerical value) 無限小數 3.88576576576576576576576576 //https://en.wikipedia.org/wiki/Overline#Math_and_science repeating decimal 循環小數 3.88¯576 continued fraction 連分數 == [3; 1, 7, 1, 3, 15, 1, 1, 2] Egyptian fraction expansion 古埃及分數 最簡分數(irreducible fraction, reduced proper fraction) 約分 reduce */ 'use strict'; if (typeof CeL === 'function') CeL.run( { name: 'data.math.rational', require: 'data.code.compatibility.|data.native.|data.math.integer.', no_extend: 'random,compare', code: function (library_namespace) { // requiring var Integer = library_namespace.data.math.integer, // radix point / radix character / decimal mark radix_point = Integer.radix_point(), // 為正規 radix。 valid_radix = Integer.valid_radix, DEFAULT_BASE = Integer.DEFAULT_BASE ; // ---------------------------------------------------------------------// // basic constants. 定義基本常數。 var // copy from data.math MULTIPLICATIVE_IDENTITY = library_namespace.MULTIPLICATIVE_IDENTITY, // copy from data.math ZERO_EXPONENT = library_namespace.ZERO_EXPONENT, // copy from data.math.integer. //{Integer} KEY_NUMERATOR = 'numerator', //{Integer|Undefined}integer > 0 KEY_DENOMINATOR = 'denominator', //{Boolean|Undefined}最簡分數 KEY_IRREDUCIBLE = 'irreducible', // 應與 parseInt() 一致。 DEFAULT_RADIX = parseInt('10'), // 可辨認之數字字串。 // [ full , sign, integer part 整數部分, sign of fractional part 小數部分, numerator, denominator ] PATTERN_FRACTION = /([+\-]?)(?:(\d+)([ +\-]))?(\d+)\/(\d+)/, // [ full , sign, integer part 整數部分, fractional part 小數部分, repeating decimal 循環小數1, repeating decimal 循環小數2 ] PATTERN_DECIMAL = /([+\-]?)(\d*)\.(\d*)(?:¯(\d+)|\((\d+)\))?/ ; // ---------------------------------------------------------------------// // 初始調整並規範基本常數。 // ---------------------------------------------------------------------// // 工具函數 function do_modified(rational, not_amount) { if (!not_amount) delete rational[KEY_IRREDUCIBLE]; } // ---------------------------------------------------------------------// // definition of module integer /** * 任意大小、帶正負號的有理數。rational number instance. * * @example * * * * @class Rational 的 constructor * @constructor */ function Rational(number) { if (!(this instanceof Rational)) return 1 === arguments.length && is_Rational(number) ? number : assignment.apply(new Rational, arguments); if (arguments.length > 0) assignment.apply(this, arguments); } // instance public interface ------------------- // https://en.wikipedia.org/wiki/Operation_(mathematics) var OP_REFERENCE = { '+': add, '-': subtract, '*': multiply, '/': divide, '^': power, '=': assignment, '==': compare }; Object.assign(Rational.prototype, OP_REFERENCE, { reduce: reduce, // 下面全部皆為 assignment,例如 '+' 實為 '+='。 assignment: assignment, // add_assignment add: add, // subtract_assignment subtract: subtract, // multiply_assignment multiply: multiply, // divide_assignment divide: divide, div: divide, power: power, pow: power, square: square, square_root: square_root, sqrt: square_root, // 至此為 assignment。 clone: clone, //https://en.wikipedia.org/wiki/Absolute_value abs: function (negative) { do_modified(this, true); this[KEY_NUMERATOR].abs(negative); return this; }, // 變換正負號。 negate: function () { do_modified(this, true); this[KEY_NUMERATOR].negate(); return this; }, is_positive: function () { return this.compare(0) > 0; }, is_negative: function () { return this[KEY_NUMERATOR].is_negative(); }, // 正負符號。 // https://en.wikipedia.org/wiki/Sign_(mathematics) // https://en.wikipedia.org/wiki/Sign_function sign: function (negative) { return this[KEY_NUMERATOR].sign(negative); }, to_continued_fraction: to_continued_fraction, to_repeating_decimal: to_repeating_decimal, integer_part: function () { var n = this[KEY_NUMERATOR].clone().expand_exponent(); if (KEY_DENOMINATOR in this) n.divide(this[KEY_DENOMINATOR]); return n; }, toPrecision: toPrecision, factorize: factorize, log: log, is_0: function (little_natural) { return this[KEY_NUMERATOR].is_0(little_natural); }, isFinite: function () { return this[KEY_NUMERATOR].isFinite(); }, //compare_amount: compare_amount, compare: compare, equals: function (number) { return this.compare(number) === 0; }, // is proper fraction is_proper: function () { return (KEY_DENOMINATOR in this) && this[KEY_NUMERATOR].compare(this[KEY_DENOMINATOR]) < 0; }, op: Integer.set_operate(OP_REFERENCE), valueOf: valueOf, toString: toString }); // class public interface --------------------------- function is_Rational(value) { return value instanceof Rational; } function Rational_compare(number1, number2) { if (typeof number1 === 'number' && typeof number2 === 'number') return number1 - number2; if (!is_Rational(number1)) number1 = new Rational(number1); return number1.compare(number2); } //get the extreme value (極端值: max/min) of input values function extreme(values, get_minima) { var index = values.length, extreme_value, value, compare; if (!index) // ES6: Math.max: If no arguments are given, the result is −∞. return get_minima ? Infinity : -Infinity; extreme_value = values[--index]; while (0 < index--) { // WARNING 注意: 當碰上許多大數時,會出現需要多次轉換 extreme_value 成 Rational 的效能低下情形! //但若許多數字不同底,而最大的是 String,則可能獲得部分效能。 if (Number.isNaN(compare = Rational_compare(extreme_value, value = values[index]))) // ES6: Math.max: If any value is NaN, the result is NaN. return NaN; if (get_minima ? compare > 0 : compare < 0) extreme_value = value; //依規範,必須掃描一次,確定沒 NaN。不可中途跳出。 if (false && (get_minima ? compare > 0 : compare < 0) //當有改變時才偵測。 && typeof (extreme_value = value) === 'number' && !Number.isFinite(extreme_value = value)) break; } return extreme_value; } // TODO: max function random(max) { return new Rational(Integer.random(), Integer.random()); } function is_0(value, little_natural) { if (typeof value === 'string') value = new Rational(value); return value === (little_natural || 0) || (is_Rational(value) ? value[KEY_NUMERATOR] : Integer(value)).is_0(little_natural); } Object.assign(Rational, { from_continued_fraction: function (sequence, length, base) { var convergent = Integer.convergent_of(sequence, length, base); return new Rational(convergent[0], convergent[1]); }, random: random, max: function Rational_max() { // get max() return extreme(arguments); }, min: function Rational_min() { // get min() return extreme(arguments, true); }, compare: Rational_compare, // little_natural: little natural number, e.g., 1 is_0: is_0, is_Rational: is_Rational }); // ---------------------------------------------------------------------// // 因 clone 頗為常用,作特殊處置以增進效率。 function clone() { var rational = new Rational; rational[KEY_NUMERATOR] = this[KEY_NUMERATOR].clone(); if (KEY_DENOMINATOR in this) rational[KEY_DENOMINATOR] = this[KEY_DENOMINATOR].clone(); if (KEY_IRREDUCIBLE in this) rational[KEY_IRREDUCIBLE] = this[KEY_IRREDUCIBLE]; return rational; } function assignment(numerator, denominator, base) { do_modified(this); var matched, tmp; if (is_Rational(numerator)) { [KEY_DENOMINATOR, KEY_IRREDUCIBLE].forEach(function (key) { if (key in numerator) this[key] = numerator[key]; else delete this[key]; }, this); this[KEY_NUMERATOR] = numerator = numerator[KEY_NUMERATOR]; } else if (typeof numerator === 'string' && (matched = numerator.match(PATTERN_FRACTION))) { //rational = new Rational('10783/2775'); //rational = new Rational('3+2458/2775'); //rational = new Rational('3 2458/2775'); if (base === undefined && valid_radix(denominator)) // shift arguments base = denominator, denominator = undefined; if (!valid_radix(base)) base = DEFAULT_RADIX; // [ full , sign, integer part 整數部分, sign of fractional part 小數部分, numerator, denominator ] if (matched[3] === '-' && matched[1] !== '-') library_namespace.error('assignment: Invalid number sign!'); this[KEY_DENOMINATOR] = tmp = new Integer(matched[5], base, DEFAULT_BASE); this[KEY_NUMERATOR] = numerator = new Integer(matched[4], base, DEFAULT_BASE); if (matched[2]) numerator.add((new Integer(matched[2], base, DEFAULT_BASE)).multiply(tmp)); if (matched[1] === '-') // 將正負符號擺在 [KEY_NUMERATOR],確保 [KEY_DENOMINATOR] 不為負。 numerator.negate(); } else if (typeof numerator === 'string' && (matched = numerator.match(PATTERN_DECIMAL))) { //https://en.wikipedia.org/wiki/Vinculum_(symbol) //rational = new Rational('3.88¯576'); //Brackets //rational = new Rational('3.88(576)'); if (base === undefined && valid_radix(denominator)) // shift arguments base = denominator, denominator = undefined; if (!valid_radix(base)) base = DEFAULT_RADIX; // [ full , sign, integer part 整數部分, fractional part 小數部分, repeating decimal 循環小數1, repeating decimal 循環小數2 ] // e.g., 1111.222¯33333 → 1111 + (22233333 - 222) / 99999000 // 處理完小數部分之 numerator。 if (matched[4] || (matched[4] = matched[5])) { // 有循環節(period)。 numerator = new Integer(matched[3] + matched[4], base, DEFAULT_BASE); if (matched[3]) numerator.add(new Integer(matched[3], base, DEFAULT_BASE), true); tmp = (base - 1).toString(base).repeat(matched[4].length); } else { // 無循環節(period)。 numerator = new Integer(matched[3] || 0, base, DEFAULT_BASE); tmp = MULTIPLICATIVE_IDENTITY; } if (matched[3]) tmp += '0'.repeat(matched[3].length); if (tmp === MULTIPLICATIVE_IDENTITY) delete this[KEY_DENOMINATOR]; else // assert: {String} tmp this[KEY_DENOMINATOR] = new Integer(tmp, base, DEFAULT_BASE); if (matched[2]) numerator.add(new Integer(matched[2], base, DEFAULT_BASE).multiply(this[KEY_DENOMINATOR])); if (matched[1] === '-') // 將正負符號擺在 [KEY_NUMERATOR],確保 [KEY_DENOMINATOR] 不為負。 numerator.negate(); this[KEY_NUMERATOR] = numerator; } else { delete this[KEY_DENOMINATOR]; if ((this[KEY_NUMERATOR] = numerator = new Integer(tmp = numerator, base, DEFAULT_BASE)).isNaN()) library_namespace.error('assignment: Invalid number: [' + tmp + '].'); // 確保不使用 exponent,使 exponent 為 0。 else if ((tmp = numerator.get_exponent()) > 0) numerator.expand_exponent(); else if (tmp < 0) { (this[KEY_DENOMINATOR] = new Integer(numerator.get_base(), numerator.get_base())) .power(-tmp); numerator.get_exponent(0); } } if (denominator) // rational = new Rational(10783, 2775); this.divide(valid_radix(base) && typeof denominator === 'string' ? new Rational(denominator, MULTIPLICATIVE_IDENTITY, base) : denominator); if ((KEY_DENOMINATOR in this) && (!numerator.isFinite() || numerator.is_0() // [KEY_DENOMINATOR] 預設即為 MULTIPLICATIVE_IDENTITY。 || this[KEY_DENOMINATOR].is_0(MULTIPLICATIVE_IDENTITY) )) delete this[KEY_DENOMINATOR]; return this; } function reduce() { if ((KEY_DENOMINATOR in this) && !this[KEY_IRREDUCIBLE]) { var gcd = this[KEY_NUMERATOR].clone().Euclidean_algorithm(this[KEY_DENOMINATOR].clone())[1]; this[KEY_NUMERATOR].divide(gcd); if (this[KEY_DENOMINATOR].equals(gcd)) delete this[KEY_DENOMINATOR]; else this[KEY_DENOMINATOR].divide(gcd); this[KEY_IRREDUCIBLE] = true; } return this; } /** * 測試大小/比大小 * @param number the number to compare * @return {Number} 0:==, <0:<, >0:> * @_name _module_.prototype.compare_to */ function compare(number) { if (typeof number === 'string') number = new Rational(number); if (is_Rational(number) && (KEY_DENOMINATOR in number.reduce())) return this.reduce()[KEY_NUMERATOR].clone().multiply(number[KEY_DENOMINATOR]) // .compare(KEY_DENOMINATOR in this ? this[KEY_DENOMINATOR].clone().multiply(number[KEY_NUMERATOR]) : number[KEY_NUMERATOR]); else { if (is_Rational(number)) number = number[KEY_NUMERATOR]; return this.reduce()[KEY_NUMERATOR] // .compare(KEY_DENOMINATOR in this ? this[KEY_DENOMINATOR].clone().multiply(number) : number); } } // ---------------------------------------------------------------------// //四則運算,即加減乘除, + - * / (+-×÷)**[=] //https://en.wikipedia.org/wiki/Elementary_arithmetic // 正規化(normalize) operand // return is_Integer function normalize_operand(operand, rational) { // TODO: defferent base return Integer.is_Integer(operand) && operand.get_base() === rational[KEY_NUMERATOR].get_base() || is_Rational(operand) && operand[KEY_NUMERATOR].get_base() === rational[KEY_NUMERATOR].get_base() ? operand : new Rational(operand); } // Addition 和: addend + addend = sum function add(addend, is_subtract) { if (!is_0(addend = normalize_operand(addend, this))) { // assert: addend != 0. do_modified(this.reduce()); addend = Rational(addend); if (is_Rational(addend) && !(KEY_DENOMINATOR in addend.reduce())) addend = addend[KEY_NUMERATOR]; // assert: addend is non-Rational or reduced Rational with denominator. if (is_Rational(addend) // 分母相同時,直接相加減分子。 ? (KEY_DENOMINATOR in this) && addend[KEY_DENOMINATOR].equals(this[KEY_DENOMINATOR]) // 分母相同(=1)時,直接相加減分子。 : !(KEY_DENOMINATOR in this)) this[KEY_NUMERATOR].add(is_Rational(addend) ? addend[KEY_DENOMINATOR] : addend, is_subtract); // 分母相同,毋須更動。 else { // n1/d1 ± n2/d2 = (n1d2 ± n2d1)/d1d2 // assert: d1 != d2 var denominator_need_multiply; if (is_Rational(addend)) { // 僅在 (KEY_DENOMINATOR in addend) 時,才須處理分母。 if (KEY_DENOMINATOR in this) //為不干擾 this[KEY_NUMERATOR].add() 之操作,另作 cache。 denominator_need_multiply = addend[KEY_DENOMINATOR]; else // 為不干擾 addend,另外創建。 this[KEY_DENOMINATOR] = addend[KEY_DENOMINATOR].clone(); this[KEY_NUMERATOR].multiply(addend[KEY_DENOMINATOR]); addend = addend[KEY_NUMERATOR]; } this[KEY_NUMERATOR].add(KEY_DENOMINATOR in this ? this[KEY_DENOMINATOR].clone().multiply(addend) : addend, is_subtract); if (denominator_need_multiply) this[KEY_DENOMINATOR].multiply(denominator_need_multiply); } } return this; } // Subtraction 差: minuend − subtrahend = difference function subtract(subtrahend) { return this.add(subtrahend, true); } // Multiplication 乘: multiplicand × multiplier = product function multiply(multiplier) { if (!this[KEY_NUMERATOR].isNaN()) if (is_0(multiplier = normalize_operand(multiplier, this))) { do_modified(this); this[KEY_NUMERATOR].assignment(this[KEY_NUMERATOR].isFinite() ? 0 : NaN); delete this[KEY_DENOMINATOR]; } else if (!is_0(multiplier, MULTIPLICATIVE_IDENTITY)) { do_modified(this.reduce()); if (is_Rational(multiplier) && !(KEY_DENOMINATOR in multiplier.reduce())) multiplier = multiplier[KEY_NUMERATOR]; // assert: multiplier is non-Rational or reduced Rational with denominator. if (is_Rational(multiplier)) { this[KEY_NUMERATOR].multiply(multiplier[KEY_NUMERATOR]); if (KEY_DENOMINATOR in this) this[KEY_DENOMINATOR].multiply(multiplier[KEY_DENOMINATOR]); else this[KEY_DENOMINATOR] = multiplier[KEY_DENOMINATOR].clone(); } else this[KEY_NUMERATOR].multiply(multiplier); } return this; } // Division 除: dividend ÷ divisor = quotient function divide(denominator) { if (!this[KEY_NUMERATOR].isNaN()) if (!denominator || is_0(denominator = normalize_operand(denominator, this))) { do_modified(this); this[KEY_NUMERATOR].assignment(1 / 0); delete this[KEY_DENOMINATOR]; } else if (!is_0(denominator, MULTIPLICATIVE_IDENTITY)) { do_modified(this.reduce()); if (is_Rational(denominator)) { if (KEY_DENOMINATOR in denominator.reduce()) this[KEY_NUMERATOR].multiply(denominator[KEY_DENOMINATOR]); denominator = denominator[KEY_NUMERATOR]; } if (KEY_DENOMINATOR in this) this[KEY_DENOMINATOR].multiply(denominator); else this[KEY_DENOMINATOR] = denominator.clone(); } return this; } // ---------------------------------------------------------------------// // https://en.wikipedia.org/wiki/Minimal_polynomial_(field_theory) // return p[{Integer}], p[0] + p[1]*this = 0 function minimal_polynomial() { this.reduce(); var polynomial = [this[KEY_NUMERATOR], this[KEY_DENOMINATOR]]; // translate to {Number} if possible. polynomial.forEach(function (coefficient, index) { var value = coefficient.valueOf(); polynomial[index] = Number.isSafeInteger(value) ? value : coefficient.clone(); }); return polynomial; } function continued_fraction_toString() { return '[' + this.join(',').replace(/,/, ';') + ']'; } function to_continued_fraction() { var c = KEY_DENOMINATOR in this // http://www.mathpath.org/concepts/cont.frac.htm ? this[KEY_NUMERATOR].clone().Euclidean_algorithm(this[KEY_DENOMINATOR].clone())[0] // : [this[KEY_NUMERATOR].clone()]; Object.defineProperty(c, 'toString', { enumerable: false, value: continued_fraction_toString }); return c; } // precise divide, to repeating decimal // radix===1: get {Integer} object instead of {String} // return [ integer part, non-repeating fractional part, period (repeating decimal part) ] // https://en.wikipedia.org/wiki/Repeating_decimal function to_repeating_decimal(radix, period_limit) { return this[KEY_NUMERATOR].precise_divide(this[KEY_DENOMINATOR] || MULTIPLICATIVE_IDENTITY, radix, period_limit); } // precision: 不包含小數點,共取 precision 位,precision > 0。 function toPrecision(precision) { if (!(0 < precision) || !Number.isSafeInteger(precision |= 0)) return this.toString(); var d, tmp, is_negative = this[KEY_NUMERATOR].is_negative(); if (precision < 1e2 // 當循環節(period)相對於 precision 過長,採用 .to_repeating_decimal() 無實益。 || ((KEY_DENOMINATOR in this) && precision < 4 * this[KEY_DENOMINATOR].valueOf()) || !(d = this.to_repeating_decimal(DEFAULT_RADIX, precision))) { //general method d = new Integer(this[KEY_NUMERATOR], DEFAULT_RADIX); if (KEY_DENOMINATOR in this) d.divide(tmp = new Integer(this[KEY_DENOMINATOR], DEFAULT_RADIX), precision + tmp.length + Math.max(tmp.get_exponent(), 0)); d = d.toString(); d = d.match(/(-?\d*)\.(\d+)/) || d.match(/(-?\d*)/); d = [d[1], d[2] || '', '']; } // assert: d = [ {String}integer part, {String}non-repeating fractional part, {String}period (repeating decimal part) ] if (d[0].length === precision + (is_negative ? 1 : 0) && d[0].charAt(d[0].length - 1) !== '0') return d[0]; if (is_negative) //減少負擔。 d[0] = d[0].slice(1); if (d[0] === '0') { precision++; if (tmp = d[1].match(/^0+/)) precision += tmp[0].length; } if (d[0].length < precision) { // 會用到小數部分。 d[0] += radix_point + d[1]; // -2: radix_point, 判斷用之位數1位 if (d[0].length - 2 < precision) { if (!d[2]) d[2] = '0'; d[0] += d[2].repeat(Math.ceil((precision - d[0].length + 2) / d[2].length)); } // 34.56 3 d = (5 <= (d[0].charAt(precision + 1) | 0) ? d[0].slice(0, precision) + ((d[0].charAt(precision) | 0) + 1) : d[0].slice(0, precision + 1)); } else d = d[0].charAt(0) + radix_point + (5 <= (d[0].charAt(precision) | 0) ? d[0].slice(1, --precision) + ((d[0].charAt(precision) | 0) + 1) : d[0].slice(1, precision)) + 'e+' + (d[0].length - 1); return is_negative ? '-' + d : d; } // ---------------------------------------------------------------------// // advanced functions function factorize(radix, limit) { var f, d, factors = this.reduce()[KEY_NUMERATOR].factorize(radix, limit); if (KEY_DENOMINATOR in this) { d = this[KEY_DENOMINATOR].factorize(radix, limit); // TODO: sort for (f in d) factors[f] = -d[f]; } return factors; } function square() { do_modified(this.reduce()); this[KEY_NUMERATOR].square(); if (KEY_DENOMINATOR in this) this[KEY_DENOMINATOR].square(); return this; } // Exponentiation 冪/乘方: base ^ exponent = power // https://en.wikipedia.org/wiki/Exponentiation // https://en.wikipedia.org/wiki/Exponentiation_by_squaring function power(exponent) { do_modified(this.reduce()); this[KEY_NUMERATOR].power(exponent); if (KEY_DENOMINATOR in this) this[KEY_DENOMINATOR].power(exponent); return this; } // WARNING 注意: this will get floor(sqrt(this)),結果僅會回傳整數! function square_root(precision) { do_modified(this.reduce()); if (KEY_DENOMINATOR in this) { this[KEY_NUMERATOR].divide(this[KEY_DENOMINATOR], precision); delete this[KEY_DENOMINATOR]; } this[KEY_NUMERATOR].square_root(precision); return this; } function log(base) { var value = this[KEY_NUMERATOR].log(base); if (KEY_DENOMINATOR in this) value -= this[KEY_DENOMINATOR].log(base); return value; } // ---------------------------------------------------------------------// // WARNING 注意: 若回傳非 Number.isSafeInteger(),則會有誤差,不能等於最佳近似值。 function valueOf(type) { var n = this[KEY_NUMERATOR].valueOf(), d; if (KEY_DENOMINATOR in this) if (isFinite(d = this[KEY_DENOMINATOR].valueOf()) || isFinite(n)) n /= d; else n = this[KEY_NUMERATOR].ratio_to(this[KEY_DENOMINATOR]); return n; } // default: numerator/denominator // e.g., 1 2/3 var TYPE_MIX = 1, // e.g., 1.2¯3 TYPE_DECIMAL = 2, // Parentheses, e.g., 1.2(3) TYPE_PARENTHESES = 3; function toString(type) { if (!(KEY_DENOMINATOR in this)) return this[KEY_NUMERATOR].toString(); var string = this[KEY_NUMERATOR]; if (type === TYPE_DECIMAL || type === TYPE_PARENTHESES) { string = string.precise_divide(this[KEY_DENOMINATOR]); if (string[2]) { string[1] += type === TYPE_DECIMAL // e.g., 1.2¯3 ? '¯' + string[2] // Parentheses, e.g., 1.2(3) : '(' + string[2] + ')'; // using the combining overline (U+0305) // https://en.wikipedia.org/wiki/Vinculum_(symbol)#Computer_entry_of_the_symbol // https://en.wikipedia.org/wiki/Overline //string[1] += string[2].replace(/(.)/g, '$1̅'); } if (string[1]) string[0] += radix_point + string[1]; return string[0]; } return (type === TYPE_MIX ? (string = string.clone()).division(this[KEY_DENOMINATOR]).toString() + ' ' + string.abs().toString() : string.toString()) // + '/' + this[KEY_DENOMINATOR].toString(); } // ---------------------------------------------------------------------// return Rational; } });