simple-squiggle

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

compareNatural.js (8814B)


      1 "use strict";
      2 
      3 var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
      4 
      5 Object.defineProperty(exports, "__esModule", {
      6   value: true
      7 });
      8 exports.createCompareNatural = void 0;
      9 
     10 var _javascriptNaturalSort = _interopRequireDefault(require("javascript-natural-sort"));
     11 
     12 var _is = require("../../utils/is.js");
     13 
     14 var _factory = require("../../utils/factory.js");
     15 
     16 var name = 'compareNatural';
     17 var dependencies = ['typed', 'compare'];
     18 var createCompareNatural = /* #__PURE__ */(0, _factory.factory)(name, dependencies, function (_ref) {
     19   var typed = _ref.typed,
     20       compare = _ref.compare;
     21   var compareBooleans = compare.signatures['boolean,boolean'];
     22   /**
     23    * Compare two values of any type in a deterministic, natural way.
     24    *
     25    * For numeric values, the function works the same as `math.compare`.
     26    * For types of values that can't be compared mathematically,
     27    * the function compares in a natural way.
     28    *
     29    * For numeric values, x and y are considered equal when the relative
     30    * difference between x and y is smaller than the configured epsilon.
     31    * The function cannot be used to compare values smaller than
     32    * approximately 2.22e-16.
     33    *
     34    * For Complex numbers, first the real parts are compared. If equal,
     35    * the imaginary parts are compared.
     36    *
     37    * Strings are compared with a natural sorting algorithm, which
     38    * orders strings in a "logic" way following some heuristics.
     39    * This differs from the function `compare`, which converts the string
     40    * into a numeric value and compares that. The function `compareText`
     41    * on the other hand compares text lexically.
     42    *
     43    * Arrays and Matrices are compared value by value until there is an
     44    * unequal pair of values encountered. Objects are compared by sorted
     45    * keys until the keys or their values are unequal.
     46    *
     47    * Syntax:
     48    *
     49    *    math.compareNatural(x, y)
     50    *
     51    * Examples:
     52    *
     53    *    math.compareNatural(6, 1)              // returns 1
     54    *    math.compareNatural(2, 3)              // returns -1
     55    *    math.compareNatural(7, 7)              // returns 0
     56    *
     57    *    math.compareNatural('10', '2')         // returns 1
     58    *    math.compareText('10', '2')            // returns -1
     59    *    math.compare('10', '2')                // returns 1
     60    *
     61    *    math.compareNatural('Answer: 10', 'Answer: 2') // returns 1
     62    *    math.compareText('Answer: 10', 'Answer: 2')    // returns -1
     63    *    math.compare('Answer: 10', 'Answer: 2')
     64    *        // Error: Cannot convert "Answer: 10" to a number
     65    *
     66    *    const a = math.unit('5 cm')
     67    *    const b = math.unit('40 mm')
     68    *    math.compareNatural(a, b)              // returns 1
     69    *
     70    *    const c = math.complex('2 + 3i')
     71    *    const d = math.complex('2 + 4i')
     72    *    math.compareNatural(c, d)              // returns -1
     73    *
     74    *    math.compareNatural([1, 2, 4], [1, 2, 3]) // returns 1
     75    *    math.compareNatural([1, 2, 3], [1, 2])    // returns 1
     76    *    math.compareNatural([1, 5], [1, 2, 3])    // returns 1
     77    *    math.compareNatural([1, 2], [1, 2])       // returns 0
     78    *
     79    *    math.compareNatural({a: 2}, {a: 4})       // returns -1
     80    *
     81    * See also:
     82    *
     83    *    compare, compareText
     84    *
     85    * @param  {*} x First value to compare
     86    * @param  {*} y Second value to compare
     87    * @return {number} Returns the result of the comparison:
     88    *                  1 when x > y, -1 when x < y, and 0 when x == y.
     89    */
     90 
     91   return typed(name, {
     92     'any, any': function anyAny(x, y) {
     93       var typeX = (0, _is.typeOf)(x);
     94       var typeY = (0, _is.typeOf)(y);
     95       var c; // numeric types
     96 
     97       if ((typeX === 'number' || typeX === 'BigNumber' || typeX === 'Fraction') && (typeY === 'number' || typeY === 'BigNumber' || typeY === 'Fraction')) {
     98         c = compare(x, y);
     99 
    100         if (c.toString() !== '0') {
    101           // c can be number, BigNumber, or Fraction
    102           return c > 0 ? 1 : -1; // return a number
    103         } else {
    104           return (0, _javascriptNaturalSort.default)(typeX, typeY);
    105         }
    106       } // matrix types
    107 
    108 
    109       if (typeX === 'Array' || typeX === 'Matrix' || typeY === 'Array' || typeY === 'Matrix') {
    110         c = compareMatricesAndArrays(this, x, y);
    111 
    112         if (c !== 0) {
    113           return c;
    114         } else {
    115           return (0, _javascriptNaturalSort.default)(typeX, typeY);
    116         }
    117       } // in case of different types, order by name of type, i.e. 'BigNumber' < 'Complex'
    118 
    119 
    120       if (typeX !== typeY) {
    121         return (0, _javascriptNaturalSort.default)(typeX, typeY);
    122       }
    123 
    124       if (typeX === 'Complex') {
    125         return compareComplexNumbers(x, y);
    126       }
    127 
    128       if (typeX === 'Unit') {
    129         if (x.equalBase(y)) {
    130           return this(x.value, y.value);
    131         } // compare by units
    132 
    133 
    134         return compareArrays(this, x.formatUnits(), y.formatUnits());
    135       }
    136 
    137       if (typeX === 'boolean') {
    138         return compareBooleans(x, y);
    139       }
    140 
    141       if (typeX === 'string') {
    142         return (0, _javascriptNaturalSort.default)(x, y);
    143       }
    144 
    145       if (typeX === 'Object') {
    146         return compareObjects(this, x, y);
    147       }
    148 
    149       if (typeX === 'null') {
    150         return 0;
    151       }
    152 
    153       if (typeX === 'undefined') {
    154         return 0;
    155       } // this should not occur...
    156 
    157 
    158       throw new TypeError('Unsupported type of value "' + typeX + '"');
    159     }
    160   });
    161   /**
    162    * Compare mixed matrix/array types, by converting to same-shaped array.
    163    * This comparator is non-deterministic regarding input types.
    164    * @param {Array | SparseMatrix | DenseMatrix | *} x
    165    * @param {Array | SparseMatrix | DenseMatrix | *} y
    166    * @returns {number} Returns the comparison result: -1, 0, or 1
    167    */
    168 
    169   function compareMatricesAndArrays(compareNatural, x, y) {
    170     if ((0, _is.isSparseMatrix)(x) && (0, _is.isSparseMatrix)(y)) {
    171       return compareArrays(compareNatural, x.toJSON().values, y.toJSON().values);
    172     }
    173 
    174     if ((0, _is.isSparseMatrix)(x)) {
    175       // note: convert to array is expensive
    176       return compareMatricesAndArrays(compareNatural, x.toArray(), y);
    177     }
    178 
    179     if ((0, _is.isSparseMatrix)(y)) {
    180       // note: convert to array is expensive
    181       return compareMatricesAndArrays(compareNatural, x, y.toArray());
    182     } // convert DenseArray into Array
    183 
    184 
    185     if ((0, _is.isDenseMatrix)(x)) {
    186       return compareMatricesAndArrays(compareNatural, x.toJSON().data, y);
    187     }
    188 
    189     if ((0, _is.isDenseMatrix)(y)) {
    190       return compareMatricesAndArrays(compareNatural, x, y.toJSON().data);
    191     } // convert scalars to array
    192 
    193 
    194     if (!Array.isArray(x)) {
    195       return compareMatricesAndArrays(compareNatural, [x], y);
    196     }
    197 
    198     if (!Array.isArray(y)) {
    199       return compareMatricesAndArrays(compareNatural, x, [y]);
    200     }
    201 
    202     return compareArrays(compareNatural, x, y);
    203   }
    204   /**
    205    * Compare two Arrays
    206    *
    207    * - First, compares value by value
    208    * - Next, if all corresponding values are equal,
    209    *   look at the length: longest array will be considered largest
    210    *
    211    * @param {Array} x
    212    * @param {Array} y
    213    * @returns {number} Returns the comparison result: -1, 0, or 1
    214    */
    215 
    216 
    217   function compareArrays(compareNatural, x, y) {
    218     // compare each value
    219     for (var i = 0, ii = Math.min(x.length, y.length); i < ii; i++) {
    220       var v = compareNatural(x[i], y[i]);
    221 
    222       if (v !== 0) {
    223         return v;
    224       }
    225     } // compare the size of the arrays
    226 
    227 
    228     if (x.length > y.length) {
    229       return 1;
    230     }
    231 
    232     if (x.length < y.length) {
    233       return -1;
    234     } // both Arrays have equal size and content
    235 
    236 
    237     return 0;
    238   }
    239   /**
    240    * Compare two objects
    241    *
    242    * - First, compare sorted property names
    243    * - Next, compare the property values
    244    *
    245    * @param {Object} x
    246    * @param {Object} y
    247    * @returns {number} Returns the comparison result: -1, 0, or 1
    248    */
    249 
    250 
    251   function compareObjects(compareNatural, x, y) {
    252     var keysX = Object.keys(x);
    253     var keysY = Object.keys(y); // compare keys
    254 
    255     keysX.sort(_javascriptNaturalSort.default);
    256     keysY.sort(_javascriptNaturalSort.default);
    257     var c = compareArrays(compareNatural, keysX, keysY);
    258 
    259     if (c !== 0) {
    260       return c;
    261     } // compare values
    262 
    263 
    264     for (var i = 0; i < keysX.length; i++) {
    265       var v = compareNatural(x[keysX[i]], y[keysY[i]]);
    266 
    267       if (v !== 0) {
    268         return v;
    269       }
    270     }
    271 
    272     return 0;
    273   }
    274 });
    275 /**
    276  * Compare two complex numbers, `x` and `y`:
    277  *
    278  * - First, compare the real values of `x` and `y`
    279  * - If equal, compare the imaginary values of `x` and `y`
    280  *
    281  * @params {Complex} x
    282  * @params {Complex} y
    283  * @returns {number} Returns the comparison result: -1, 0, or 1
    284  */
    285 
    286 exports.createCompareNatural = createCompareNatural;
    287 
    288 function compareComplexNumbers(x, y) {
    289   if (x.re > y.re) {
    290     return 1;
    291   }
    292 
    293   if (x.re < y.re) {
    294     return -1;
    295   }
    296 
    297   if (x.im > y.im) {
    298     return 1;
    299   }
    300 
    301   if (x.im < y.im) {
    302     return -1;
    303   }
    304 
    305   return 0;
    306 }