simplifyConstant.js (17268B)
1 "use strict"; 2 3 var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 5 Object.defineProperty(exports, "__esModule", { 6 value: true 7 }); 8 exports.createSimplifyConstant = void 0; 9 10 var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof")); 11 12 var _is = require("../../../utils/is.js"); 13 14 var _factory = require("../../../utils/factory.js"); 15 16 var _util = require("./util.js"); 17 18 var _noop = require("../../../utils/noop.js"); 19 20 function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; } 21 22 function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } 23 24 function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } 25 26 var name = 'simplifyConstant'; 27 var dependencies = ['typed', 'config', 'mathWithTransform', 'matrix', '?fraction', '?bignumber', 'AccessorNode', 'ArrayNode', 'ConstantNode', 'FunctionNode', 'IndexNode', 'ObjectNode', 'OperatorNode', 'SymbolNode']; 28 var createSimplifyConstant = /* #__PURE__ */(0, _factory.factory)(name, dependencies, function (_ref) { 29 var typed = _ref.typed, 30 config = _ref.config, 31 mathWithTransform = _ref.mathWithTransform, 32 matrix = _ref.matrix, 33 fraction = _ref.fraction, 34 bignumber = _ref.bignumber, 35 AccessorNode = _ref.AccessorNode, 36 ArrayNode = _ref.ArrayNode, 37 ConstantNode = _ref.ConstantNode, 38 FunctionNode = _ref.FunctionNode, 39 IndexNode = _ref.IndexNode, 40 ObjectNode = _ref.ObjectNode, 41 OperatorNode = _ref.OperatorNode, 42 SymbolNode = _ref.SymbolNode; 43 44 var _createUtil = (0, _util.createUtil)({ 45 FunctionNode: FunctionNode, 46 OperatorNode: OperatorNode, 47 SymbolNode: SymbolNode 48 }), 49 isCommutative = _createUtil.isCommutative, 50 isAssociative = _createUtil.isAssociative, 51 allChildren = _createUtil.allChildren, 52 createMakeNodeFunction = _createUtil.createMakeNodeFunction; 53 54 function simplifyConstant(expr, options) { 55 return _ensureNode(foldFraction(expr, options)); 56 } 57 58 function _removeFractions(thing) { 59 if ((0, _is.isFraction)(thing)) { 60 return thing.valueOf(); 61 } 62 63 if (thing instanceof Array) { 64 return thing.map(_removeFractions); 65 } 66 67 if ((0, _is.isMatrix)(thing)) { 68 return matrix(_removeFractions(thing.valueOf())); 69 } 70 71 return thing; 72 } 73 74 function _eval(fnname, args, options) { 75 try { 76 return mathWithTransform[fnname].apply(null, args); 77 } catch (ignore) { 78 // sometimes the implicit type conversion causes the evaluation to fail, so we'll try again after removing Fractions 79 args = args.map(_removeFractions); 80 return _toNumber(mathWithTransform[fnname].apply(null, args), options); 81 } 82 } 83 84 var _toNode = typed({ 85 Fraction: _fractionToNode, 86 number: function number(n) { 87 if (n < 0) { 88 return unaryMinusNode(new ConstantNode(-n)); 89 } 90 91 return new ConstantNode(n); 92 }, 93 BigNumber: function BigNumber(n) { 94 if (n < 0) { 95 return unaryMinusNode(new ConstantNode(-n)); 96 } 97 98 return new ConstantNode(n); // old parameters: (n.toString(), 'number') 99 }, 100 Complex: function Complex(s) { 101 throw new Error('Cannot convert Complex number to Node'); 102 }, 103 string: function string(s) { 104 return new ConstantNode(s); 105 }, 106 Matrix: function Matrix(m) { 107 return new ArrayNode(m.valueOf().map(function (e) { 108 return _toNode(e); 109 })); 110 } 111 }); 112 113 function _ensureNode(thing) { 114 if ((0, _is.isNode)(thing)) { 115 return thing; 116 } 117 118 return _toNode(thing); 119 } // convert a number to a fraction only if it can be expressed exactly, 120 // and when both numerator and denominator are small enough 121 122 123 function _exactFraction(n, options) { 124 var exactFractions = options && options.exactFractions !== false; 125 126 if (exactFractions && isFinite(n) && fraction) { 127 var f = fraction(n); 128 var fractionsLimit = options && typeof options.fractionsLimit === 'number' ? options.fractionsLimit : Infinity; // no limit by default 129 130 if (f.valueOf() === n && f.n < fractionsLimit && f.d < fractionsLimit) { 131 return f; 132 } 133 } 134 135 return n; 136 } // Convert numbers to a preferred number type in preference order: Fraction, number, Complex 137 // BigNumbers are left alone 138 139 140 var _toNumber = typed({ 141 'string, Object': function stringObject(s, options) { 142 if (config.number === 'BigNumber') { 143 if (bignumber === undefined) { 144 (0, _noop.noBignumber)(); 145 } 146 147 return bignumber(s); 148 } else if (config.number === 'Fraction') { 149 if (fraction === undefined) { 150 (0, _noop.noFraction)(); 151 } 152 153 return fraction(s); 154 } else { 155 var n = parseFloat(s); 156 return _exactFraction(n, options); 157 } 158 }, 159 'Fraction, Object': function FractionObject(s, options) { 160 return s; 161 }, 162 // we don't need options here 163 'BigNumber, Object': function BigNumberObject(s, options) { 164 return s; 165 }, 166 // we don't need options here 167 'number, Object': function numberObject(s, options) { 168 return _exactFraction(s, options); 169 }, 170 'Complex, Object': function ComplexObject(s, options) { 171 if (s.im !== 0) { 172 return s; 173 } 174 175 return _exactFraction(s.re, options); 176 }, 177 'Matrix, Object': function MatrixObject(s, options) { 178 return matrix(_exactFraction(s.valueOf())); 179 }, 180 'Array, Object': function ArrayObject(s, options) { 181 return s.map(_exactFraction); 182 } 183 }); 184 185 function unaryMinusNode(n) { 186 return new OperatorNode('-', 'unaryMinus', [n]); 187 } 188 189 function _fractionToNode(f) { 190 var n; 191 var vn = f.s * f.n; 192 193 if (vn < 0) { 194 n = new OperatorNode('-', 'unaryMinus', [new ConstantNode(-vn)]); 195 } else { 196 n = new ConstantNode(vn); 197 } 198 199 if (f.d === 1) { 200 return n; 201 } 202 203 return new OperatorNode('/', 'divide', [n, new ConstantNode(f.d)]); 204 } 205 /* Handles constant indexing of ArrayNodes, matrices, and ObjectNodes */ 206 207 208 function _foldAccessor(obj, index, options) { 209 if (!(0, _is.isIndexNode)(index)) { 210 // don't know what to do with that... 211 return new AccessorNode(_ensureNode(obj), _ensureNode(index)); 212 } 213 214 if ((0, _is.isArrayNode)(obj) || (0, _is.isMatrix)(obj)) { 215 var remainingDims = Array.from(index.dimensions); 216 /* We will resolve constant indices one at a time, looking 217 * just in the first or second dimensions because (a) arrays 218 * of more than two dimensions are likely rare, and (b) pulling 219 * out the third or higher dimension would be pretty intricate. 220 * The price is that we miss simplifying [..3d array][x,y,1] 221 */ 222 223 while (remainingDims.length > 0) { 224 if ((0, _is.isConstantNode)(remainingDims[0]) && typeof remainingDims[0].value !== 'string') { 225 var first = _toNumber(remainingDims.shift().value, options); 226 227 if ((0, _is.isArrayNode)(obj)) { 228 obj = obj.items[first - 1]; 229 } else { 230 // matrix 231 obj = obj.valueOf()[first - 1]; 232 233 if (obj instanceof Array) { 234 obj = matrix(obj); 235 } 236 } 237 } else if (remainingDims.length > 1 && (0, _is.isConstantNode)(remainingDims[1]) && typeof remainingDims[1].value !== 'string') { 238 var second = _toNumber(remainingDims[1].value, options); 239 240 var tryItems = []; 241 var fromItems = (0, _is.isArrayNode)(obj) ? obj.items : obj.valueOf(); 242 243 var _iterator = _createForOfIteratorHelper(fromItems), 244 _step; 245 246 try { 247 for (_iterator.s(); !(_step = _iterator.n()).done;) { 248 var item = _step.value; 249 250 if ((0, _is.isArrayNode)(item)) { 251 tryItems.push(item.items[second - 1]); 252 } else if ((0, _is.isMatrix)(obj)) { 253 tryItems.push(item[second - 1]); 254 } else { 255 break; 256 } 257 } 258 } catch (err) { 259 _iterator.e(err); 260 } finally { 261 _iterator.f(); 262 } 263 264 if (tryItems.length === fromItems.length) { 265 if ((0, _is.isArrayNode)(obj)) { 266 obj = new ArrayNode(tryItems); 267 } else { 268 // matrix 269 obj = matrix(tryItems); 270 } 271 272 remainingDims.splice(1, 1); 273 } else { 274 // extracting slice along 2nd dimension failed, give up 275 break; 276 } 277 } else { 278 // neither 1st or 2nd dimension is constant, give up 279 break; 280 } 281 } 282 283 if (remainingDims.length === index.dimensions.length) { 284 /* No successful constant indexing */ 285 return new AccessorNode(_ensureNode(obj), index); 286 } 287 288 if (remainingDims.length > 0) { 289 /* Indexed some but not all dimensions */ 290 index = new IndexNode(remainingDims); 291 return new AccessorNode(_ensureNode(obj), index); 292 } 293 /* All dimensions were constant, access completely resolved */ 294 295 296 return obj; 297 } 298 299 if ((0, _is.isObjectNode)(obj) && index.dimensions.length === 1 && (0, _is.isConstantNode)(index.dimensions[0])) { 300 var key = index.dimensions[0].value; 301 302 if (key in obj.properties) { 303 return obj.properties[key]; 304 } 305 306 return new ConstantNode(); // undefined 307 } 308 /* Don't know how to index this sort of obj, at least not with this index */ 309 310 311 return new AccessorNode(_ensureNode(obj), index); 312 } 313 /* 314 * Create a binary tree from a list of Fractions and Nodes. 315 * Tries to fold Fractions by evaluating them until the first Node in the list is hit, so 316 * `args` should be sorted to have the Fractions at the start (if the operator is commutative). 317 * @param args - list of Fractions and Nodes 318 * @param fn - evaluator for the binary operation evaluator that accepts two Fractions 319 * @param makeNode - creates a binary OperatorNode/FunctionNode from a list of child Nodes 320 * if args.length is 1, returns args[0] 321 * @return - Either a Node representing a binary expression or Fraction 322 */ 323 324 325 function foldOp(fn, args, makeNode, options) { 326 return args.reduce(function (a, b) { 327 if (!(0, _is.isNode)(a) && !(0, _is.isNode)(b)) { 328 try { 329 return _eval(fn, [a, b], options); 330 } catch (ignoreandcontinue) {} 331 332 a = _toNode(a); 333 b = _toNode(b); 334 } else if (!(0, _is.isNode)(a)) { 335 a = _toNode(a); 336 } else if (!(0, _is.isNode)(b)) { 337 b = _toNode(b); 338 } 339 340 return makeNode([a, b]); 341 }); 342 } // destroys the original node and returns a folded one 343 344 345 function foldFraction(node, options) { 346 switch (node.type) { 347 case 'SymbolNode': 348 return node; 349 350 case 'ConstantNode': 351 switch ((0, _typeof2.default)(node.value)) { 352 case 'number': 353 return _toNumber(node.value, options); 354 355 case 'string': 356 return node.value; 357 358 default: 359 if (!isNaN(node.value)) return _toNumber(node.value, options); 360 } 361 362 return node; 363 364 case 'FunctionNode': 365 if (mathWithTransform[node.name] && mathWithTransform[node.name].rawArgs) { 366 return node; 367 } 368 369 { 370 // Process operators as OperatorNode 371 var operatorFunctions = ['add', 'multiply']; 372 373 if (operatorFunctions.indexOf(node.name) === -1) { 374 var args = node.args.map(function (arg) { 375 return foldFraction(arg, options); 376 }); // If all args are numbers 377 378 if (!args.some(_is.isNode)) { 379 try { 380 return _eval(node.name, args, options); 381 } catch (ignoreandcontinue) {} 382 } // Size of a matrix does not depend on entries 383 384 385 if (node.name === 'size' && args.length === 1 && (0, _is.isArrayNode)(args[0])) { 386 var sz = []; 387 var section = args[0]; 388 389 while ((0, _is.isArrayNode)(section)) { 390 sz.push(section.items.length); 391 section = section.items[0]; 392 } 393 394 return matrix(sz); 395 } // Convert all args to nodes and construct a symbolic function call 396 397 398 return new FunctionNode(node.name, args.map(_ensureNode)); 399 } else {// treat as operator 400 } 401 } 402 403 /* falls through */ 404 405 case 'OperatorNode': 406 { 407 var fn = node.fn.toString(); 408 409 var _args; 410 411 var res; 412 var makeNode = createMakeNodeFunction(node); 413 414 if ((0, _is.isOperatorNode)(node) && node.isUnary()) { 415 _args = [foldFraction(node.args[0], options)]; 416 417 if (!(0, _is.isNode)(_args[0])) { 418 res = _eval(fn, _args, options); 419 } else { 420 res = makeNode(_args); 421 } 422 } else if (isAssociative(node, options.context)) { 423 _args = allChildren(node, options.context); 424 _args = _args.map(function (arg) { 425 return foldFraction(arg, options); 426 }); 427 428 if (isCommutative(fn, options.context)) { 429 // commutative binary operator 430 var consts = []; 431 var vars = []; 432 433 for (var i = 0; i < _args.length; i++) { 434 if (!(0, _is.isNode)(_args[i])) { 435 consts.push(_args[i]); 436 } else { 437 vars.push(_args[i]); 438 } 439 } 440 441 if (consts.length > 1) { 442 res = foldOp(fn, consts, makeNode, options); 443 vars.unshift(res); 444 res = foldOp(fn, vars, makeNode, options); 445 } else { 446 // we won't change the children order since it's not neccessary 447 res = foldOp(fn, _args, makeNode, options); 448 } 449 } else { 450 // non-commutative binary operator 451 res = foldOp(fn, _args, makeNode, options); 452 } 453 } else { 454 // non-associative binary operator 455 _args = node.args.map(function (arg) { 456 return foldFraction(arg, options); 457 }); 458 res = foldOp(fn, _args, makeNode, options); 459 } 460 461 return res; 462 } 463 464 case 'ParenthesisNode': 465 // remove the uneccessary parenthesis 466 return foldFraction(node.content, options); 467 468 case 'AccessorNode': 469 return _foldAccessor(foldFraction(node.object, options), foldFraction(node.index, options), options); 470 471 case 'ArrayNode': 472 { 473 var foldItems = node.items.map(function (item) { 474 return foldFraction(item, options); 475 }); 476 477 if (foldItems.some(_is.isNode)) { 478 return new ArrayNode(foldItems.map(_ensureNode)); 479 } 480 /* All literals -- return a Matrix so we can operate on it */ 481 482 483 return matrix(foldItems); 484 } 485 486 case 'IndexNode': 487 { 488 return new IndexNode(node.dimensions.map(function (n) { 489 return simplifyConstant(n, options); 490 })); 491 } 492 493 case 'ObjectNode': 494 { 495 var foldProps = {}; 496 497 for (var prop in node.properties) { 498 foldProps[prop] = simplifyConstant(node.properties[prop], options); 499 } 500 501 return new ObjectNode(foldProps); 502 } 503 504 case 'AssignmentNode': 505 /* falls through */ 506 507 case 'BlockNode': 508 /* falls through */ 509 510 case 'FunctionAssignmentNode': 511 /* falls through */ 512 513 case 'RangeNode': 514 /* falls through */ 515 516 case 'ConditionalNode': 517 /* falls through */ 518 519 default: 520 throw new Error("Unimplemented node type in simplifyConstant: ".concat(node.type)); 521 } 522 } 523 524 return simplifyConstant; 525 }); 526 exports.createSimplifyConstant = createSimplifyConstant;