(a: Expression, b: Expression)
| 296 | if (isString(expr)) return 'string'; |
| 297 | |
| 298 | // Objects rank beside the other opaque values, after strings. They are |
| 299 | // unordered as a matter of semantics (`cmp()` answers `undefined` for |
| 300 | // them), so the rank exists only to give a sort a deterministic, total |
| 301 | // comparator — see the `'object'` arm of `order()`. |
| 302 | if (isObject(expr)) return 'object'; |
| 303 | |
| 304 | return 'other'; |
| 305 | } |
| 306 | |
| 307 | /** |
| 308 | * Given two expressions `a` and `b`, return: |
| 309 | * - `-1` if `a` should be ordered before `b` |
| 310 | * - `+1` if `b` should be ordered before `a` |
| 311 | * - `0` if they have the same order (they are structurally equal) |
| 312 | * |
| 313 | * The default order is as follow: |
| 314 | * |
| 315 | * 1/ Literal numeric values (rational, machine numbers and Decimal numbers), |
| 316 | * ordered by their numeric value (smaller numbers before larger numbers) |
| 317 | * |
| 318 | * 2/ Literal complex numbers, ordered by their imaginary parts. In case of a |
| 319 | * tie, ordered by their real parts. (An arbitrary but established total |
| 320 | * order — canonical operand order in existing expressions and snapshots |
| 321 | * depends on it, so it is documented as implemented rather than changed; |
| 322 | * CORRECTNESS P3-7.) |
| 323 | * |
| 324 | * 3/ Symbols, ordered by their name as strings |
| 325 | * |
| 326 | * 4/ Addition, ordered as a polynom, with higher degree terms first |
| 327 | * |
| 328 | * 5/ Other functions, ordered by their `complexity` property. In case |
| 329 | * of a tie, ordered by the operator of the expression as a string. In case of a |
| 330 | * tie, by the leaf count of each expression. In case of a tie, by the order |
| 331 | * of each argument, left to right. |
| 332 | * |
| 333 | * 6/ Strings, ordered by comparing their Unicode code point values. While this |
| 334 | * sort order is quick to calculate, it can produce unexpected results, for |
| 335 | * example "E" < "e" < "È" and "11" < "2". This ordering is not suitable to |
| 336 | * collate natural language strings. |
| 337 | * |
| 338 | * See https://reference.wolfram.com/language/ref/Sort.html for a |
| 339 | * description of the ordering of expressions in Mathematica. |
| 340 | * |
| 341 | */ |
| 342 | export function order(a: Expression, b: Expression): number { |
| 343 | if (a === b) return 0; |
| 344 | |
| 345 | const rankA = rank(a); |
| 346 | const rankB = rank(b); |
| 347 | if (rankA !== rankB) return RANKS.indexOf(rankA) - RANKS.indexOf(rankB); |
| 348 | |
| 349 | // All NaN operands are equivalent for ordering purposes. Returning a fixed |
| 350 | // value (0) keeps the comparator total and deterministic — an `af - bf` |
| 351 | // subtraction would yield `NaN`, corrupting `Array.sort`. |
| 352 | if (rankA === 'nan') return 0; |
| 353 | |
| 354 | if (rankA === 'complex') { |
| 355 | // If the rank is complex, the numericValues can't be a number |
no test coverage detected