simple-squiggle

A restricted subset of Squiggle
Log | Files | Refs | README

number.js (21348B)


      1 "use strict";
      2 
      3 Object.defineProperty(exports, "__esModule", {
      4   value: true
      5 });
      6 exports.cosh = exports.cbrt = exports.atanh = exports.asinh = exports.acosh = exports.DBL_EPSILON = void 0;
      7 exports.digits = digits;
      8 exports.expm1 = void 0;
      9 exports.format = format;
     10 exports.isInteger = isInteger;
     11 exports.log2 = exports.log1p = exports.log10 = void 0;
     12 exports.nearlyEqual = nearlyEqual;
     13 exports.roundDigits = roundDigits;
     14 exports.sinh = exports.sign = void 0;
     15 exports.splitNumber = splitNumber;
     16 exports.tanh = void 0;
     17 exports.toEngineering = toEngineering;
     18 exports.toExponential = toExponential;
     19 exports.toFixed = toFixed;
     20 exports.toPrecision = toPrecision;
     21 
     22 var _is = require("./is.js");
     23 
     24 /**
     25  * @typedef {{sign: '+' | '-' | '', coefficients: number[], exponent: number}} SplitValue
     26  */
     27 
     28 /**
     29  * Check if a number is integer
     30  * @param {number | boolean} value
     31  * @return {boolean} isInteger
     32  */
     33 function isInteger(value) {
     34   if (typeof value === 'boolean') {
     35     return true;
     36   }
     37 
     38   return isFinite(value) ? value === Math.round(value) : false;
     39 }
     40 /**
     41  * Calculate the sign of a number
     42  * @param {number} x
     43  * @returns {number}
     44  */
     45 
     46 
     47 var sign = /* #__PURE__ */Math.sign || function (x) {
     48   if (x > 0) {
     49     return 1;
     50   } else if (x < 0) {
     51     return -1;
     52   } else {
     53     return 0;
     54   }
     55 };
     56 /**
     57  * Calculate the base-2 logarithm of a number
     58  * @param {number} x
     59  * @returns {number}
     60  */
     61 
     62 
     63 exports.sign = sign;
     64 
     65 var log2 = /* #__PURE__ */Math.log2 || function log2(x) {
     66   return Math.log(x) / Math.LN2;
     67 };
     68 /**
     69  * Calculate the base-10 logarithm of a number
     70  * @param {number} x
     71  * @returns {number}
     72  */
     73 
     74 
     75 exports.log2 = log2;
     76 
     77 var log10 = /* #__PURE__ */Math.log10 || function log10(x) {
     78   return Math.log(x) / Math.LN10;
     79 };
     80 /**
     81  * Calculate the natural logarithm of a number + 1
     82  * @param {number} x
     83  * @returns {number}
     84  */
     85 
     86 
     87 exports.log10 = log10;
     88 
     89 var log1p = /* #__PURE__ */Math.log1p || function (x) {
     90   return Math.log(x + 1);
     91 };
     92 /**
     93  * Calculate cubic root for a number
     94  *
     95  * Code from es6-shim.js:
     96  *   https://github.com/paulmillr/es6-shim/blob/master/es6-shim.js#L1564-L1577
     97  *
     98  * @param {number} x
     99  * @returns {number} Returns the cubic root of x
    100  */
    101 
    102 
    103 exports.log1p = log1p;
    104 
    105 var cbrt = /* #__PURE__ */Math.cbrt || function cbrt(x) {
    106   if (x === 0) {
    107     return x;
    108   }
    109 
    110   var negate = x < 0;
    111   var result;
    112 
    113   if (negate) {
    114     x = -x;
    115   }
    116 
    117   if (isFinite(x)) {
    118     result = Math.exp(Math.log(x) / 3); // from https://en.wikipedia.org/wiki/Cube_root#Numerical_methods
    119 
    120     result = (x / (result * result) + 2 * result) / 3;
    121   } else {
    122     result = x;
    123   }
    124 
    125   return negate ? -result : result;
    126 };
    127 /**
    128  * Calculates exponentiation minus 1
    129  * @param {number} x
    130  * @return {number} res
    131  */
    132 
    133 
    134 exports.cbrt = cbrt;
    135 
    136 var expm1 = /* #__PURE__ */Math.expm1 || function expm1(x) {
    137   return x >= 2e-4 || x <= -2e-4 ? Math.exp(x) - 1 : x + x * x / 2 + x * x * x / 6;
    138 };
    139 /**
    140  * Formats a number in a given base
    141  * @param {number} n
    142  * @param {number} base
    143  * @param {number} size
    144  * @returns {string}
    145  */
    146 
    147 
    148 exports.expm1 = expm1;
    149 
    150 function formatNumberToBase(n, base, size) {
    151   var prefixes = {
    152     2: '0b',
    153     8: '0o',
    154     16: '0x'
    155   };
    156   var prefix = prefixes[base];
    157   var suffix = '';
    158 
    159   if (size) {
    160     if (size < 1) {
    161       throw new Error('size must be in greater than 0');
    162     }
    163 
    164     if (!isInteger(size)) {
    165       throw new Error('size must be an integer');
    166     }
    167 
    168     if (n > Math.pow(2, size - 1) - 1 || n < -Math.pow(2, size - 1)) {
    169       throw new Error("Value must be in range [-2^".concat(size - 1, ", 2^").concat(size - 1, "-1]"));
    170     }
    171 
    172     if (!isInteger(n)) {
    173       throw new Error('Value must be an integer');
    174     }
    175 
    176     if (n < 0) {
    177       n = n + Math.pow(2, size);
    178     }
    179 
    180     suffix = "i".concat(size);
    181   }
    182 
    183   var sign = '';
    184 
    185   if (n < 0) {
    186     n = -n;
    187     sign = '-';
    188   }
    189 
    190   return "".concat(sign).concat(prefix).concat(n.toString(base)).concat(suffix);
    191 }
    192 /**
    193  * Convert a number to a formatted string representation.
    194  *
    195  * Syntax:
    196  *
    197  *    format(value)
    198  *    format(value, options)
    199  *    format(value, precision)
    200  *    format(value, fn)
    201  *
    202  * Where:
    203  *
    204  *    {number} value   The value to be formatted
    205  *    {Object} options An object with formatting options. Available options:
    206  *                     {string} notation
    207  *                         Number notation. Choose from:
    208  *                         'fixed'          Always use regular number notation.
    209  *                                          For example '123.40' and '14000000'
    210  *                         'exponential'    Always use exponential notation.
    211  *                                          For example '1.234e+2' and '1.4e+7'
    212  *                         'engineering'    Always use engineering notation.
    213  *                                          For example '123.4e+0' and '14.0e+6'
    214  *                         'auto' (default) Regular number notation for numbers
    215  *                                          having an absolute value between
    216  *                                          `lowerExp` and `upperExp` bounds, and
    217  *                                          uses exponential notation elsewhere.
    218  *                                          Lower bound is included, upper bound
    219  *                                          is excluded.
    220  *                                          For example '123.4' and '1.4e7'.
    221  *                         'bin', 'oct, or
    222  *                         'hex'            Format the number using binary, octal,
    223  *                                          or hexadecimal notation.
    224  *                                          For example '0b1101' and '0x10fe'.
    225  *                     {number} wordSize    The word size in bits to use for formatting
    226  *                                          in binary, octal, or hexadecimal notation.
    227  *                                          To be used only with 'bin', 'oct', or 'hex'
    228  *                                          values for 'notation' option. When this option
    229  *                                          is defined the value is formatted as a signed
    230  *                                          twos complement integer of the given word size
    231  *                                          and the size suffix is appended to the output.
    232  *                                          For example
    233  *                                          format(-1, {notation: 'hex', wordSize: 8}) === '0xffi8'.
    234  *                                          Default value is undefined.
    235  *                     {number} precision   A number between 0 and 16 to round
    236  *                                          the digits of the number.
    237  *                                          In case of notations 'exponential',
    238  *                                          'engineering', and 'auto',
    239  *                                          `precision` defines the total
    240  *                                          number of significant digits returned.
    241  *                                          In case of notation 'fixed',
    242  *                                          `precision` defines the number of
    243  *                                          significant digits after the decimal
    244  *                                          point.
    245  *                                          `precision` is undefined by default,
    246  *                                          not rounding any digits.
    247  *                     {number} lowerExp    Exponent determining the lower boundary
    248  *                                          for formatting a value with an exponent
    249  *                                          when `notation='auto`.
    250  *                                          Default value is `-3`.
    251  *                     {number} upperExp    Exponent determining the upper boundary
    252  *                                          for formatting a value with an exponent
    253  *                                          when `notation='auto`.
    254  *                                          Default value is `5`.
    255  *    {Function} fn    A custom formatting function. Can be used to override the
    256  *                     built-in notations. Function `fn` is called with `value` as
    257  *                     parameter and must return a string. Is useful for example to
    258  *                     format all values inside a matrix in a particular way.
    259  *
    260  * Examples:
    261  *
    262  *    format(6.4)                                        // '6.4'
    263  *    format(1240000)                                    // '1.24e6'
    264  *    format(1/3)                                        // '0.3333333333333333'
    265  *    format(1/3, 3)                                     // '0.333'
    266  *    format(21385, 2)                                   // '21000'
    267  *    format(12.071, {notation: 'fixed'})                // '12'
    268  *    format(2.3,    {notation: 'fixed', precision: 2})  // '2.30'
    269  *    format(52.8,   {notation: 'exponential'})          // '5.28e+1'
    270  *    format(12345678, {notation: 'engineering'})        // '12.345678e+6'
    271  *
    272  * @param {number} value
    273  * @param {Object | Function | number} [options]
    274  * @return {string} str The formatted value
    275  */
    276 
    277 
    278 function format(value, options) {
    279   if (typeof options === 'function') {
    280     // handle format(value, fn)
    281     return options(value);
    282   } // handle special cases
    283 
    284 
    285   if (value === Infinity) {
    286     return 'Infinity';
    287   } else if (value === -Infinity) {
    288     return '-Infinity';
    289   } else if (isNaN(value)) {
    290     return 'NaN';
    291   } // default values for options
    292 
    293 
    294   var notation = 'auto';
    295   var precision;
    296   var wordSize;
    297 
    298   if (options) {
    299     // determine notation from options
    300     if (options.notation) {
    301       notation = options.notation;
    302     } // determine precision from options
    303 
    304 
    305     if ((0, _is.isNumber)(options)) {
    306       precision = options;
    307     } else if ((0, _is.isNumber)(options.precision)) {
    308       precision = options.precision;
    309     }
    310 
    311     if (options.wordSize) {
    312       wordSize = options.wordSize;
    313 
    314       if (typeof wordSize !== 'number') {
    315         throw new Error('Option "wordSize" must be a number');
    316       }
    317     }
    318   } // handle the various notations
    319 
    320 
    321   switch (notation) {
    322     case 'fixed':
    323       return toFixed(value, precision);
    324 
    325     case 'exponential':
    326       return toExponential(value, precision);
    327 
    328     case 'engineering':
    329       return toEngineering(value, precision);
    330 
    331     case 'bin':
    332       return formatNumberToBase(value, 2, wordSize);
    333 
    334     case 'oct':
    335       return formatNumberToBase(value, 8, wordSize);
    336 
    337     case 'hex':
    338       return formatNumberToBase(value, 16, wordSize);
    339 
    340     case 'auto':
    341       // remove trailing zeros after the decimal point
    342       return toPrecision(value, precision, options && options).replace(/((\.\d*?)(0+))($|e)/, function () {
    343         var digits = arguments[2];
    344         var e = arguments[4];
    345         return digits !== '.' ? digits + e : e;
    346       });
    347 
    348     default:
    349       throw new Error('Unknown notation "' + notation + '". ' + 'Choose "auto", "exponential", "fixed", "bin", "oct", or "hex.');
    350   }
    351 }
    352 /**
    353  * Split a number into sign, coefficients, and exponent
    354  * @param {number | string} value
    355  * @return {SplitValue}
    356  *              Returns an object containing sign, coefficients, and exponent
    357  */
    358 
    359 
    360 function splitNumber(value) {
    361   // parse the input value
    362   var match = String(value).toLowerCase().match(/^(-?)(\d+\.?\d*)(e([+-]?\d+))?$/);
    363 
    364   if (!match) {
    365     throw new SyntaxError('Invalid number ' + value);
    366   }
    367 
    368   var sign = match[1];
    369   var digits = match[2];
    370   var exponent = parseFloat(match[4] || '0');
    371   var dot = digits.indexOf('.');
    372   exponent += dot !== -1 ? dot - 1 : digits.length - 1;
    373   var coefficients = digits.replace('.', '') // remove the dot (must be removed before removing leading zeros)
    374   .replace(/^0*/, function (zeros) {
    375     // remove leading zeros, add their count to the exponent
    376     exponent -= zeros.length;
    377     return '';
    378   }).replace(/0*$/, '') // remove trailing zeros
    379   .split('').map(function (d) {
    380     return parseInt(d);
    381   });
    382 
    383   if (coefficients.length === 0) {
    384     coefficients.push(0);
    385     exponent++;
    386   }
    387 
    388   return {
    389     sign: sign,
    390     coefficients: coefficients,
    391     exponent: exponent
    392   };
    393 }
    394 /**
    395  * Format a number in engineering notation. Like '1.23e+6', '2.3e+0', '3.500e-3'
    396  * @param {number | string} value
    397  * @param {number} [precision]        Optional number of significant figures to return.
    398  */
    399 
    400 
    401 function toEngineering(value, precision) {
    402   if (isNaN(value) || !isFinite(value)) {
    403     return String(value);
    404   }
    405 
    406   var split = splitNumber(value);
    407   var rounded = roundDigits(split, precision);
    408   var e = rounded.exponent;
    409   var c = rounded.coefficients; // find nearest lower multiple of 3 for exponent
    410 
    411   var newExp = e % 3 === 0 ? e : e < 0 ? e - 3 - e % 3 : e - e % 3;
    412 
    413   if ((0, _is.isNumber)(precision)) {
    414     // add zeroes to give correct sig figs
    415     while (precision > c.length || e - newExp + 1 > c.length) {
    416       c.push(0);
    417     }
    418   } else {
    419     // concatenate coefficients with necessary zeros
    420     // add zeros if necessary (for example: 1e+8 -> 100e+6)
    421     var missingZeros = Math.abs(e - newExp) - (c.length - 1);
    422 
    423     for (var i = 0; i < missingZeros; i++) {
    424       c.push(0);
    425     }
    426   } // find difference in exponents
    427 
    428 
    429   var expDiff = Math.abs(e - newExp);
    430   var decimalIdx = 1; // push decimal index over by expDiff times
    431 
    432   while (expDiff > 0) {
    433     decimalIdx++;
    434     expDiff--;
    435   } // if all coefficient values are zero after the decimal point and precision is unset, don't add a decimal value.
    436   // otherwise concat with the rest of the coefficients
    437 
    438 
    439   var decimals = c.slice(decimalIdx).join('');
    440   var decimalVal = (0, _is.isNumber)(precision) && decimals.length || decimals.match(/[1-9]/) ? '.' + decimals : '';
    441   var str = c.slice(0, decimalIdx).join('') + decimalVal + 'e' + (e >= 0 ? '+' : '') + newExp.toString();
    442   return rounded.sign + str;
    443 }
    444 /**
    445  * Format a number with fixed notation.
    446  * @param {number | string} value
    447  * @param {number} [precision=undefined]  Optional number of decimals after the
    448  *                                        decimal point. null by default.
    449  */
    450 
    451 
    452 function toFixed(value, precision) {
    453   if (isNaN(value) || !isFinite(value)) {
    454     return String(value);
    455   }
    456 
    457   var splitValue = splitNumber(value);
    458   var rounded = typeof precision === 'number' ? roundDigits(splitValue, splitValue.exponent + 1 + precision) : splitValue;
    459   var c = rounded.coefficients;
    460   var p = rounded.exponent + 1; // exponent may have changed
    461   // append zeros if needed
    462 
    463   var pp = p + (precision || 0);
    464 
    465   if (c.length < pp) {
    466     c = c.concat(zeros(pp - c.length));
    467   } // prepend zeros if needed
    468 
    469 
    470   if (p < 0) {
    471     c = zeros(-p + 1).concat(c);
    472     p = 1;
    473   } // insert a dot if needed
    474 
    475 
    476   if (p < c.length) {
    477     c.splice(p, 0, p === 0 ? '0.' : '.');
    478   }
    479 
    480   return rounded.sign + c.join('');
    481 }
    482 /**
    483  * Format a number in exponential notation. Like '1.23e+5', '2.3e+0', '3.500e-3'
    484  * @param {number | string} value
    485  * @param {number} [precision]  Number of digits in formatted output.
    486  *                              If not provided, the maximum available digits
    487  *                              is used.
    488  */
    489 
    490 
    491 function toExponential(value, precision) {
    492   if (isNaN(value) || !isFinite(value)) {
    493     return String(value);
    494   } // round if needed, else create a clone
    495 
    496 
    497   var split = splitNumber(value);
    498   var rounded = precision ? roundDigits(split, precision) : split;
    499   var c = rounded.coefficients;
    500   var e = rounded.exponent; // append zeros if needed
    501 
    502   if (c.length < precision) {
    503     c = c.concat(zeros(precision - c.length));
    504   } // format as `C.CCCe+EEE` or `C.CCCe-EEE`
    505 
    506 
    507   var first = c.shift();
    508   return rounded.sign + first + (c.length > 0 ? '.' + c.join('') : '') + 'e' + (e >= 0 ? '+' : '') + e;
    509 }
    510 /**
    511  * Format a number with a certain precision
    512  * @param {number | string} value
    513  * @param {number} [precision=undefined] Optional number of digits.
    514  * @param {{lowerExp: number | undefined, upperExp: number | undefined}} [options]
    515  *                                       By default:
    516  *                                         lowerExp = -3 (incl)
    517  *                                         upper = +5 (excl)
    518  * @return {string}
    519  */
    520 
    521 
    522 function toPrecision(value, precision, options) {
    523   if (isNaN(value) || !isFinite(value)) {
    524     return String(value);
    525   } // determine lower and upper bound for exponential notation.
    526 
    527 
    528   var lowerExp = options && options.lowerExp !== undefined ? options.lowerExp : -3;
    529   var upperExp = options && options.upperExp !== undefined ? options.upperExp : 5;
    530   var split = splitNumber(value);
    531   var rounded = precision ? roundDigits(split, precision) : split;
    532 
    533   if (rounded.exponent < lowerExp || rounded.exponent >= upperExp) {
    534     // exponential notation
    535     return toExponential(value, precision);
    536   } else {
    537     var c = rounded.coefficients;
    538     var e = rounded.exponent; // append trailing zeros
    539 
    540     if (c.length < precision) {
    541       c = c.concat(zeros(precision - c.length));
    542     } // append trailing zeros
    543     // TODO: simplify the next statement
    544 
    545 
    546     c = c.concat(zeros(e - c.length + 1 + (c.length < precision ? precision - c.length : 0))); // prepend zeros
    547 
    548     c = zeros(-e).concat(c);
    549     var dot = e > 0 ? e : 0;
    550 
    551     if (dot < c.length - 1) {
    552       c.splice(dot + 1, 0, '.');
    553     }
    554 
    555     return rounded.sign + c.join('');
    556   }
    557 }
    558 /**
    559  * Round the number of digits of a number *
    560  * @param {SplitValue} split       A value split with .splitNumber(value)
    561  * @param {number} precision  A positive integer
    562  * @return {SplitValue}
    563  *              Returns an object containing sign, coefficients, and exponent
    564  *              with rounded digits
    565  */
    566 
    567 
    568 function roundDigits(split, precision) {
    569   // create a clone
    570   var rounded = {
    571     sign: split.sign,
    572     coefficients: split.coefficients,
    573     exponent: split.exponent
    574   };
    575   var c = rounded.coefficients; // prepend zeros if needed
    576 
    577   while (precision <= 0) {
    578     c.unshift(0);
    579     rounded.exponent++;
    580     precision++;
    581   }
    582 
    583   if (c.length > precision) {
    584     var removed = c.splice(precision, c.length - precision);
    585 
    586     if (removed[0] >= 5) {
    587       var i = precision - 1;
    588       c[i]++;
    589 
    590       while (c[i] === 10) {
    591         c.pop();
    592 
    593         if (i === 0) {
    594           c.unshift(0);
    595           rounded.exponent++;
    596           i++;
    597         }
    598 
    599         i--;
    600         c[i]++;
    601       }
    602     }
    603   }
    604 
    605   return rounded;
    606 }
    607 /**
    608  * Create an array filled with zeros.
    609  * @param {number} length
    610  * @return {Array}
    611  */
    612 
    613 
    614 function zeros(length) {
    615   var arr = [];
    616 
    617   for (var i = 0; i < length; i++) {
    618     arr.push(0);
    619   }
    620 
    621   return arr;
    622 }
    623 /**
    624  * Count the number of significant digits of a number.
    625  *
    626  * For example:
    627  *   2.34 returns 3
    628  *   0.0034 returns 2
    629  *   120.5e+30 returns 4
    630  *
    631  * @param {number} value
    632  * @return {number} digits   Number of significant digits
    633  */
    634 
    635 
    636 function digits(value) {
    637   return value.toExponential().replace(/e.*$/, '') // remove exponential notation
    638   .replace(/^0\.?0*|\./, '') // remove decimal point and leading zeros
    639   .length;
    640 }
    641 /**
    642  * Minimum number added to one that makes the result different than one
    643  */
    644 
    645 
    646 var DBL_EPSILON = Number.EPSILON || 2.2204460492503130808472633361816E-16;
    647 /**
    648  * Compares two floating point numbers.
    649  * @param {number} x          First value to compare
    650  * @param {number} y          Second value to compare
    651  * @param {number} [epsilon]  The maximum relative difference between x and y
    652  *                            If epsilon is undefined or null, the function will
    653  *                            test whether x and y are exactly equal.
    654  * @return {boolean} whether the two numbers are nearly equal
    655 */
    656 
    657 exports.DBL_EPSILON = DBL_EPSILON;
    658 
    659 function nearlyEqual(x, y, epsilon) {
    660   // if epsilon is null or undefined, test whether x and y are exactly equal
    661   if (epsilon === null || epsilon === undefined) {
    662     return x === y;
    663   }
    664 
    665   if (x === y) {
    666     return true;
    667   } // NaN
    668 
    669 
    670   if (isNaN(x) || isNaN(y)) {
    671     return false;
    672   } // at this point x and y should be finite
    673 
    674 
    675   if (isFinite(x) && isFinite(y)) {
    676     // check numbers are very close, needed when comparing numbers near zero
    677     var diff = Math.abs(x - y);
    678 
    679     if (diff < DBL_EPSILON) {
    680       return true;
    681     } else {
    682       // use relative error
    683       return diff <= Math.max(Math.abs(x), Math.abs(y)) * epsilon;
    684     }
    685   } // Infinite and Number or negative Infinite and positive Infinite cases
    686 
    687 
    688   return false;
    689 }
    690 /**
    691  * Calculate the hyperbolic arccos of a number
    692  * @param {number} x
    693  * @return {number}
    694  */
    695 
    696 
    697 var acosh = Math.acosh || function (x) {
    698   return Math.log(Math.sqrt(x * x - 1) + x);
    699 };
    700 
    701 exports.acosh = acosh;
    702 
    703 var asinh = Math.asinh || function (x) {
    704   return Math.log(Math.sqrt(x * x + 1) + x);
    705 };
    706 /**
    707  * Calculate the hyperbolic arctangent of a number
    708  * @param {number} x
    709  * @return {number}
    710  */
    711 
    712 
    713 exports.asinh = asinh;
    714 
    715 var atanh = Math.atanh || function (x) {
    716   return Math.log((1 + x) / (1 - x)) / 2;
    717 };
    718 /**
    719  * Calculate the hyperbolic cosine of a number
    720  * @param {number} x
    721  * @returns {number}
    722  */
    723 
    724 
    725 exports.atanh = atanh;
    726 
    727 var cosh = Math.cosh || function (x) {
    728   return (Math.exp(x) + Math.exp(-x)) / 2;
    729 };
    730 /**
    731  * Calculate the hyperbolic sine of a number
    732  * @param {number} x
    733  * @returns {number}
    734  */
    735 
    736 
    737 exports.cosh = cosh;
    738 
    739 var sinh = Math.sinh || function (x) {
    740   return (Math.exp(x) - Math.exp(-x)) / 2;
    741 };
    742 /**
    743  * Calculate the hyperbolic tangent of a number
    744  * @param {number} x
    745  * @returns {number}
    746  */
    747 
    748 
    749 exports.sinh = sinh;
    750 
    751 var tanh = Math.tanh || function (x) {
    752   var e = Math.exp(2 * x);
    753   return (e - 1) / (e + 1);
    754 };
    755 
    756 exports.tanh = tanh;