simplify.js 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265
  1. "use strict";
  2. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  3. Object.defineProperty(exports, "__esModule", {
  4. value: true
  5. });
  6. exports.createSimplify = void 0;
  7. var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
  8. var _is = require("../../utils/is.js");
  9. var _wildcards = require("./simplify/wildcards.js");
  10. var _factory = require("../../utils/factory.js");
  11. var _util = require("./simplify/util.js");
  12. var _object = require("../../utils/object.js");
  13. var _map = require("../../utils/map.js");
  14. var name = 'simplify';
  15. var dependencies = ['config', 'typed', 'parse', 'add', 'subtract', 'multiply', 'divide', 'pow', 'isZero', 'equal', 'resolve', 'simplifyConstant', 'simplifyCore', '?fraction', '?bignumber', 'mathWithTransform', 'matrix', 'AccessorNode', 'ArrayNode', 'ConstantNode', 'FunctionNode', 'IndexNode', 'ObjectNode', 'OperatorNode', 'ParenthesisNode', 'SymbolNode'];
  16. var createSimplify = /* #__PURE__ */(0, _factory.factory)(name, dependencies, function (_ref) {
  17. var config = _ref.config,
  18. typed = _ref.typed,
  19. parse = _ref.parse,
  20. add = _ref.add,
  21. subtract = _ref.subtract,
  22. multiply = _ref.multiply,
  23. divide = _ref.divide,
  24. pow = _ref.pow,
  25. isZero = _ref.isZero,
  26. equal = _ref.equal,
  27. resolve = _ref.resolve,
  28. simplifyConstant = _ref.simplifyConstant,
  29. simplifyCore = _ref.simplifyCore,
  30. fraction = _ref.fraction,
  31. bignumber = _ref.bignumber,
  32. mathWithTransform = _ref.mathWithTransform,
  33. matrix = _ref.matrix,
  34. AccessorNode = _ref.AccessorNode,
  35. ArrayNode = _ref.ArrayNode,
  36. ConstantNode = _ref.ConstantNode,
  37. FunctionNode = _ref.FunctionNode,
  38. IndexNode = _ref.IndexNode,
  39. ObjectNode = _ref.ObjectNode,
  40. OperatorNode = _ref.OperatorNode,
  41. ParenthesisNode = _ref.ParenthesisNode,
  42. SymbolNode = _ref.SymbolNode;
  43. var _createUtil = (0, _util.createUtil)({
  44. FunctionNode: FunctionNode,
  45. OperatorNode: OperatorNode,
  46. SymbolNode: SymbolNode
  47. }),
  48. hasProperty = _createUtil.hasProperty,
  49. isCommutative = _createUtil.isCommutative,
  50. isAssociative = _createUtil.isAssociative,
  51. mergeContext = _createUtil.mergeContext,
  52. flatten = _createUtil.flatten,
  53. unflattenr = _createUtil.unflattenr,
  54. unflattenl = _createUtil.unflattenl,
  55. createMakeNodeFunction = _createUtil.createMakeNodeFunction,
  56. defaultContext = _createUtil.defaultContext,
  57. realContext = _createUtil.realContext,
  58. positiveContext = _createUtil.positiveContext;
  59. /**
  60. * Simplify an expression tree.
  61. *
  62. * A list of rules are applied to an expression, repeating over the list until
  63. * no further changes are made.
  64. * It's possible to pass a custom set of rules to the function as second
  65. * argument. A rule can be specified as an object, string, or function:
  66. *
  67. * const rules = [
  68. * { l: 'n1*n3 + n2*n3', r: '(n1+n2)*n3' },
  69. * 'n1*n3 + n2*n3 -> (n1+n2)*n3',
  70. * function (node) {
  71. * // ... return a new node or return the node unchanged
  72. * return node
  73. * }
  74. * ]
  75. *
  76. * String and object rules consist of a left and right pattern. The left is
  77. * used to match against the expression and the right determines what matches
  78. * are replaced with. The main difference between a pattern and a normal
  79. * expression is that variables starting with the following characters are
  80. * interpreted as wildcards:
  81. *
  82. * - 'n' - Matches any node [Node]
  83. * - 'c' - Matches a constant literal (5 or 3.2) [ConstantNode]
  84. * - 'cl' - Matches a constant literal; same as c [ConstantNode]
  85. * - 'cd' - Matches a decimal literal (5 or -3.2) [ConstantNode or unaryMinus wrapping a ConstantNode]
  86. * - 'ce' - Matches a constant expression (-5 or √3) [Expressions consisting of only ConstantNodes, functions, and operators]
  87. * - 'v' - Matches a variable; anything not matched by c (-5 or x) [Node that is not a ConstantNode]
  88. * - 'vl' - Matches a variable literal (x or y) [SymbolNode]
  89. * - 'vd' - Matches a non-decimal expression; anything not matched by cd (x or √3) [Node that is not a ConstantNode or unaryMinus that is wrapping a ConstantNode]
  90. * - 've' - Matches a variable expression; anything not matched by ce (x or 2x) [Expressions that contain a SymbolNode or other non-constant term]
  91. *
  92. * The default list of rules is exposed on the function as `simplify.rules`
  93. * and can be used as a basis to built a set of custom rules. Note that since
  94. * the `simplifyCore` function is in the default list of rules, by default
  95. * simplify will convert any function calls in the expression that have
  96. * operator equivalents to their operator forms.
  97. *
  98. * To specify a rule as a string, separate the left and right pattern by '->'
  99. * When specifying a rule as an object, the following keys are meaningful:
  100. * - l - the left pattern
  101. * - r - the right pattern
  102. * - s - in lieu of l and r, the string form that is broken at -> to give them
  103. * - repeat - whether to repeat this rule until the expression stabilizes
  104. * - assuming - gives a context object, as in the 'context' option to
  105. * simplify. Every property in the context object must match the current
  106. * context in order, or else the rule will not be applied.
  107. * - imposeContext - gives a context object, as in the 'context' option to
  108. * simplify. Any settings specified will override the incoming context
  109. * for all matches of this rule.
  110. *
  111. * For more details on the theory, see:
  112. *
  113. * - [Strategies for simplifying math expressions (Stackoverflow)](https://stackoverflow.com/questions/7540227/strategies-for-simplifying-math-expressions)
  114. * - [Symbolic computation - Simplification (Wikipedia)](https://en.wikipedia.org/wiki/Symbolic_computation#Simplification)
  115. *
  116. * An optional `options` argument can be passed as last argument of `simplify`.
  117. * Currently available options (defaults in parentheses):
  118. * - `consoleDebug` (false): whether to write the expression being simplified
  119. * and any changes to it, along with the rule responsible, to console
  120. * - `context` (simplify.defaultContext): an object giving properties of
  121. * each operator, which determine what simplifications are allowed. The
  122. * currently meaningful properties are commutative, associative,
  123. * total (whether the operation is defined for all arguments), and
  124. * trivial (whether the operation applied to a single argument leaves
  125. * that argument unchanged). The default context is very permissive and
  126. * allows almost all simplifications. Only properties differing from
  127. * the default need to be specified; the default context is used as a
  128. * fallback. Additional contexts `simplify.realContext` and
  129. * `simplify.positiveContext` are supplied to cause simplify to perform
  130. * just simplifications guaranteed to preserve all values of the expression
  131. * assuming all variables and subexpressions are real numbers or
  132. * positive real numbers, respectively. (Note that these are in some cases
  133. * more restrictive than the default context; for example, the default
  134. * context will allow `x/x` to simplify to 1, whereas
  135. * `simplify.realContext` will not, as `0/0` is not equal to 1.)
  136. * - `exactFractions` (true): whether to try to convert all constants to
  137. * exact rational numbers.
  138. * - `fractionsLimit` (10000): when `exactFractions` is true, constants will
  139. * be expressed as fractions only when both numerator and denominator
  140. * are smaller than `fractionsLimit`.
  141. *
  142. * Syntax:
  143. *
  144. * simplify(expr)
  145. * simplify(expr, rules)
  146. * simplify(expr, rules)
  147. * simplify(expr, rules, scope)
  148. * simplify(expr, rules, scope, options)
  149. * simplify(expr, scope)
  150. * simplify(expr, scope, options)
  151. *
  152. * Examples:
  153. *
  154. * math.simplify('2 * 1 * x ^ (2 - 1)') // Node "2 * x"
  155. * math.simplify('2 * 3 * x', {x: 4}) // Node "24"
  156. * const f = math.parse('2 * 1 * x ^ (2 - 1)')
  157. * math.simplify(f) // Node "2 * x"
  158. * math.simplify('0.4 * x', {}, {exactFractions: true}) // Node "x * 2 / 5"
  159. * math.simplify('0.4 * x', {}, {exactFractions: false}) // Node "0.4 * x"
  160. *
  161. * See also:
  162. *
  163. * simplifyCore, derivative, evaluate, parse, rationalize, resolve
  164. *
  165. * @param {Node | string} expr
  166. * The expression to be simplified
  167. * @param {SimplifyRule[]} [rules]
  168. * Optional list with custom rules
  169. * @param {Object} [scope] Optional scope with variables
  170. * @param {SimplifyOptions} [options] Optional configuration settings
  171. * @return {Node} Returns the simplified form of `expr`
  172. */
  173. typed.addConversion({
  174. from: 'Object',
  175. to: 'Map',
  176. convert: _map.createMap
  177. });
  178. var simplify = typed('simplify', {
  179. Node: _simplify,
  180. 'Node, Map': function NodeMap(expr, scope) {
  181. return _simplify(expr, false, scope);
  182. },
  183. 'Node, Map, Object': function NodeMapObject(expr, scope, options) {
  184. return _simplify(expr, false, scope, options);
  185. },
  186. 'Node, Array': _simplify,
  187. 'Node, Array, Map': _simplify,
  188. 'Node, Array, Map, Object': _simplify
  189. });
  190. typed.removeConversion({
  191. from: 'Object',
  192. to: 'Map',
  193. convert: _map.createMap
  194. });
  195. simplify.defaultContext = defaultContext;
  196. simplify.realContext = realContext;
  197. simplify.positiveContext = positiveContext;
  198. function removeParens(node) {
  199. return node.transform(function (node, path, parent) {
  200. return (0, _is.isParenthesisNode)(node) ? removeParens(node.content) : node;
  201. });
  202. }
  203. // All constants that are allowed in rules
  204. var SUPPORTED_CONSTANTS = {
  205. "true": true,
  206. "false": true,
  207. e: true,
  208. i: true,
  209. Infinity: true,
  210. LN2: true,
  211. LN10: true,
  212. LOG2E: true,
  213. LOG10E: true,
  214. NaN: true,
  215. phi: true,
  216. pi: true,
  217. SQRT1_2: true,
  218. SQRT2: true,
  219. tau: true
  220. // null: false,
  221. // undefined: false,
  222. // version: false,
  223. };
  224. // Array of strings, used to build the ruleSet.
  225. // Each l (left side) and r (right side) are parsed by
  226. // the expression parser into a node tree.
  227. // Left hand sides are matched to subtrees within the
  228. // expression to be parsed and replaced with the right
  229. // hand side.
  230. // TODO: Add support for constraints on constants (either in the form of a '=' expression or a callback [callback allows things like comparing symbols alphabetically])
  231. // To evaluate lhs constants for rhs constants, use: { l: 'c1+c2', r: 'c3', evaluate: 'c3 = c1 + c2' }. Multiple assignments are separated by ';' in block format.
  232. // It is possible to get into an infinite loop with conflicting rules
  233. simplify.rules = [simplifyCore,
  234. // { l: 'n+0', r: 'n' }, // simplifyCore
  235. // { l: 'n^0', r: '1' }, // simplifyCore
  236. // { l: '0*n', r: '0' }, // simplifyCore
  237. // { l: 'n/n', r: '1'}, // simplifyCore
  238. // { l: 'n^1', r: 'n' }, // simplifyCore
  239. // { l: '+n1', r:'n1' }, // simplifyCore
  240. // { l: 'n--n1', r:'n+n1' }, // simplifyCore
  241. {
  242. l: 'log(e)',
  243. r: '1'
  244. },
  245. // temporary rules
  246. // Note initially we tend constants to the right because like-term
  247. // collection prefers the left, and we would rather collect nonconstants
  248. {
  249. s: 'n-n1 -> n+-n1',
  250. // temporarily replace 'subtract' so we can further flatten the 'add' operator
  251. assuming: {
  252. subtract: {
  253. total: true
  254. }
  255. }
  256. }, {
  257. s: 'n-n -> 0',
  258. // partial alternative when we can't always subtract
  259. assuming: {
  260. subtract: {
  261. total: false
  262. }
  263. }
  264. }, {
  265. s: '-(cl*v) -> v * (-cl)',
  266. // make non-constant terms positive
  267. assuming: {
  268. multiply: {
  269. commutative: true
  270. },
  271. subtract: {
  272. total: true
  273. }
  274. }
  275. }, {
  276. s: '-(cl*v) -> (-cl) * v',
  277. // non-commutative version, part 1
  278. assuming: {
  279. multiply: {
  280. commutative: false
  281. },
  282. subtract: {
  283. total: true
  284. }
  285. }
  286. }, {
  287. s: '-(v*cl) -> v * (-cl)',
  288. // non-commutative version, part 2
  289. assuming: {
  290. multiply: {
  291. commutative: false
  292. },
  293. subtract: {
  294. total: true
  295. }
  296. }
  297. }, {
  298. l: '-(n1/n2)',
  299. r: '-n1/n2'
  300. }, {
  301. l: '-v',
  302. r: 'v * (-1)'
  303. },
  304. // finish making non-constant terms positive
  305. {
  306. l: '(n1 + n2)*(-1)',
  307. r: 'n1*(-1) + n2*(-1)',
  308. repeat: true
  309. },
  310. // expand negations to achieve as much sign cancellation as possible
  311. {
  312. l: 'n/n1^n2',
  313. r: 'n*n1^-n2'
  314. },
  315. // temporarily replace 'divide' so we can further flatten the 'multiply' operator
  316. {
  317. l: 'n/n1',
  318. r: 'n*n1^-1'
  319. }, {
  320. s: '(n1*n2)^n3 -> n1^n3 * n2^n3',
  321. assuming: {
  322. multiply: {
  323. commutative: true
  324. }
  325. }
  326. }, {
  327. s: '(n1*n2)^(-1) -> n2^(-1) * n1^(-1)',
  328. assuming: {
  329. multiply: {
  330. commutative: false
  331. }
  332. }
  333. },
  334. // expand nested exponentiation
  335. {
  336. s: '(n ^ n1) ^ n2 -> n ^ (n1 * n2)',
  337. assuming: {
  338. divide: {
  339. total: true
  340. }
  341. } // 1/(1/n) = n needs 1/n to exist
  342. },
  343. // collect like factors; into a sum, only do this for nonconstants
  344. {
  345. l: ' vd * ( vd * n1 + n2)',
  346. r: 'vd^2 * n1 + vd * n2'
  347. }, {
  348. s: ' vd * (vd^n4 * n1 + n2) -> vd^(1+n4) * n1 + vd * n2',
  349. assuming: {
  350. divide: {
  351. total: true
  352. }
  353. } // v*1/v = v^(1+-1) needs 1/v
  354. }, {
  355. s: 'vd^n3 * ( vd * n1 + n2) -> vd^(n3+1) * n1 + vd^n3 * n2',
  356. assuming: {
  357. divide: {
  358. total: true
  359. }
  360. }
  361. }, {
  362. s: 'vd^n3 * (vd^n4 * n1 + n2) -> vd^(n3+n4) * n1 + vd^n3 * n2',
  363. assuming: {
  364. divide: {
  365. total: true
  366. }
  367. }
  368. }, {
  369. l: 'n*n',
  370. r: 'n^2'
  371. }, {
  372. s: 'n * n^n1 -> n^(n1+1)',
  373. assuming: {
  374. divide: {
  375. total: true
  376. }
  377. } // n*1/n = n^(-1+1) needs 1/n
  378. }, {
  379. s: 'n^n1 * n^n2 -> n^(n1+n2)',
  380. assuming: {
  381. divide: {
  382. total: true
  383. }
  384. } // ditto for n^2*1/n^2
  385. },
  386. // Unfortunately, to deal with more complicated cancellations, it
  387. // becomes necessary to simplify constants twice per pass. It's not
  388. // terribly expensive compared to matching rules, so this should not
  389. // pose a performance problem.
  390. simplifyConstant,
  391. // First: before collecting like terms
  392. // collect like terms
  393. {
  394. s: 'n+n -> 2*n',
  395. assuming: {
  396. add: {
  397. total: true
  398. }
  399. } // 2 = 1 + 1 needs to exist
  400. }, {
  401. l: 'n+-n',
  402. r: '0'
  403. }, {
  404. l: 'vd*n + vd',
  405. r: 'vd*(n+1)'
  406. },
  407. // NOTE: leftmost position is special:
  408. {
  409. l: 'n3*n1 + n3*n2',
  410. r: 'n3*(n1+n2)'
  411. },
  412. // All sub-monomials tried there.
  413. {
  414. l: 'n3^(-n4)*n1 + n3 * n2',
  415. r: 'n3^(-n4)*(n1 + n3^(n4+1) *n2)'
  416. }, {
  417. l: 'n3^(-n4)*n1 + n3^n5 * n2',
  418. r: 'n3^(-n4)*(n1 + n3^(n4+n5)*n2)'
  419. },
  420. // noncommutative additional cases (term collection & factoring)
  421. {
  422. s: 'n*vd + vd -> (n+1)*vd',
  423. assuming: {
  424. multiply: {
  425. commutative: false
  426. }
  427. }
  428. }, {
  429. s: 'vd + n*vd -> (1+n)*vd',
  430. assuming: {
  431. multiply: {
  432. commutative: false
  433. }
  434. }
  435. }, {
  436. s: 'n1*n3 + n2*n3 -> (n1+n2)*n3',
  437. assuming: {
  438. multiply: {
  439. commutative: false
  440. }
  441. }
  442. }, {
  443. s: 'n^n1 * n -> n^(n1+1)',
  444. assuming: {
  445. divide: {
  446. total: true
  447. },
  448. multiply: {
  449. commutative: false
  450. }
  451. }
  452. }, {
  453. s: 'n1*n3^(-n4) + n2 * n3 -> (n1 + n2*n3^(n4 + 1))*n3^(-n4)',
  454. assuming: {
  455. multiply: {
  456. commutative: false
  457. }
  458. }
  459. }, {
  460. s: 'n1*n3^(-n4) + n2 * n3^n5 -> (n1 + n2*n3^(n4 + n5))*n3^(-n4)',
  461. assuming: {
  462. multiply: {
  463. commutative: false
  464. }
  465. }
  466. }, {
  467. l: 'n*cd + cd',
  468. r: '(n+1)*cd'
  469. }, {
  470. s: 'cd*n + cd -> cd*(n+1)',
  471. assuming: {
  472. multiply: {
  473. commutative: false
  474. }
  475. }
  476. }, {
  477. s: 'cd + cd*n -> cd*(1+n)',
  478. assuming: {
  479. multiply: {
  480. commutative: false
  481. }
  482. }
  483. }, simplifyConstant,
  484. // Second: before returning expressions to "standard form"
  485. // make factors positive (and undo 'make non-constant terms positive')
  486. {
  487. s: '(-n)*n1 -> -(n*n1)',
  488. assuming: {
  489. subtract: {
  490. total: true
  491. }
  492. }
  493. }, {
  494. s: 'n1*(-n) -> -(n1*n)',
  495. // in case * non-commutative
  496. assuming: {
  497. subtract: {
  498. total: true
  499. },
  500. multiply: {
  501. commutative: false
  502. }
  503. }
  504. },
  505. // final ordering of constants
  506. {
  507. s: 'ce+ve -> ve+ce',
  508. assuming: {
  509. add: {
  510. commutative: true
  511. }
  512. },
  513. imposeContext: {
  514. add: {
  515. commutative: false
  516. }
  517. }
  518. }, {
  519. s: 'vd*cd -> cd*vd',
  520. assuming: {
  521. multiply: {
  522. commutative: true
  523. }
  524. },
  525. imposeContext: {
  526. multiply: {
  527. commutative: false
  528. }
  529. }
  530. },
  531. // undo temporary rules
  532. // { l: '(-1) * n', r: '-n' }, // #811 added test which proved this is redundant
  533. {
  534. l: 'n+-n1',
  535. r: 'n-n1'
  536. },
  537. // undo replace 'subtract'
  538. {
  539. s: 'n*(n1^-1) -> n/n1',
  540. // undo replace 'divide'; for * commutative
  541. assuming: {
  542. multiply: {
  543. commutative: true
  544. }
  545. } // o.w. / not conventional
  546. }, {
  547. s: 'n*n1^-n2 -> n/n1^n2',
  548. assuming: {
  549. multiply: {
  550. commutative: true
  551. }
  552. } // o.w. / not conventional
  553. }, {
  554. s: 'n^-1 -> 1/n',
  555. assuming: {
  556. multiply: {
  557. commutative: true
  558. }
  559. } // o.w. / not conventional
  560. }, {
  561. l: 'n^1',
  562. r: 'n'
  563. },
  564. // can be produced by power cancellation
  565. {
  566. s: 'n*(n1/n2) -> (n*n1)/n2',
  567. // '*' before '/'
  568. assuming: {
  569. multiply: {
  570. associative: true
  571. }
  572. }
  573. }, {
  574. s: 'n-(n1+n2) -> n-n1-n2',
  575. // '-' before '+'
  576. assuming: {
  577. addition: {
  578. associative: true,
  579. commutative: true
  580. }
  581. }
  582. },
  583. // { l: '(n1/n2)/n3', r: 'n1/(n2*n3)' },
  584. // { l: '(n*n1)/(n*n2)', r: 'n1/n2' },
  585. // simplifyConstant can leave an extra factor of 1, which can always
  586. // be eliminated, since the identity always commutes
  587. {
  588. l: '1*n',
  589. r: 'n',
  590. imposeContext: {
  591. multiply: {
  592. commutative: true
  593. }
  594. }
  595. }, {
  596. s: 'n1/(n2/n3) -> (n1*n3)/n2',
  597. assuming: {
  598. multiply: {
  599. associative: true
  600. }
  601. }
  602. }, {
  603. l: 'n1/(-n2)',
  604. r: '-n1/n2'
  605. }];
  606. /**
  607. * Takes any rule object as allowed by the specification in simplify
  608. * and puts it in a standard form used by applyRule
  609. */
  610. function _canonicalizeRule(ruleObject, context) {
  611. var newRule = {};
  612. if (ruleObject.s) {
  613. var lr = ruleObject.s.split('->');
  614. if (lr.length === 2) {
  615. newRule.l = lr[0];
  616. newRule.r = lr[1];
  617. } else {
  618. throw SyntaxError('Could not parse rule: ' + ruleObject.s);
  619. }
  620. } else {
  621. newRule.l = ruleObject.l;
  622. newRule.r = ruleObject.r;
  623. }
  624. newRule.l = removeParens(parse(newRule.l));
  625. newRule.r = removeParens(parse(newRule.r));
  626. for (var _i = 0, _arr = ['imposeContext', 'repeat', 'assuming']; _i < _arr.length; _i++) {
  627. var prop = _arr[_i];
  628. if (prop in ruleObject) {
  629. newRule[prop] = ruleObject[prop];
  630. }
  631. }
  632. if (ruleObject.evaluate) {
  633. newRule.evaluate = parse(ruleObject.evaluate);
  634. }
  635. if (isAssociative(newRule.l, context)) {
  636. var nonCommutative = !isCommutative(newRule.l, context);
  637. var leftExpandsym;
  638. // Gen. the LHS placeholder used in this NC-context specific expansion rules
  639. if (nonCommutative) leftExpandsym = _getExpandPlaceholderSymbol();
  640. var makeNode = createMakeNodeFunction(newRule.l);
  641. var expandsym = _getExpandPlaceholderSymbol();
  642. newRule.expanded = {};
  643. newRule.expanded.l = makeNode([newRule.l, expandsym]);
  644. // Push the expandsym into the deepest possible branch.
  645. // This helps to match the newRule against nodes returned from getSplits() later on.
  646. flatten(newRule.expanded.l, context);
  647. unflattenr(newRule.expanded.l, context);
  648. newRule.expanded.r = makeNode([newRule.r, expandsym]);
  649. // In and for a non-commutative context, attempting with yet additional expansion rules makes
  650. // way for more matches cases of multi-arg expressions; such that associative rules (such as
  651. // 'n*n -> n^2') can be applied to exprs. such as 'a * b * b' and 'a * b * b * a'.
  652. if (nonCommutative) {
  653. // 'Non-commutative' 1: LHS (placeholder) only
  654. newRule.expandedNC1 = {};
  655. newRule.expandedNC1.l = makeNode([leftExpandsym, newRule.l]);
  656. newRule.expandedNC1.r = makeNode([leftExpandsym, newRule.r]);
  657. // 'Non-commutative' 2: farmost LHS and RHS placeholders
  658. newRule.expandedNC2 = {};
  659. newRule.expandedNC2.l = makeNode([leftExpandsym, newRule.expanded.l]);
  660. newRule.expandedNC2.r = makeNode([leftExpandsym, newRule.expanded.r]);
  661. }
  662. }
  663. return newRule;
  664. }
  665. /**
  666. * Parse the string array of rules into nodes
  667. *
  668. * Example syntax for rules:
  669. *
  670. * Position constants to the left in a product:
  671. * { l: 'n1 * c1', r: 'c1 * n1' }
  672. * n1 is any Node, and c1 is a ConstantNode.
  673. *
  674. * Apply difference of squares formula:
  675. * { l: '(n1 - n2) * (n1 + n2)', r: 'n1^2 - n2^2' }
  676. * n1, n2 mean any Node.
  677. *
  678. * Short hand notation:
  679. * 'n1 * c1 -> c1 * n1'
  680. */
  681. function _buildRules(rules, context) {
  682. // Array of rules to be used to simplify expressions
  683. var ruleSet = [];
  684. for (var i = 0; i < rules.length; i++) {
  685. var rule = rules[i];
  686. var newRule = void 0;
  687. var ruleType = (0, _typeof2["default"])(rule);
  688. switch (ruleType) {
  689. case 'string':
  690. rule = {
  691. s: rule
  692. };
  693. /* falls through */
  694. case 'object':
  695. newRule = _canonicalizeRule(rule, context);
  696. break;
  697. case 'function':
  698. newRule = rule;
  699. break;
  700. default:
  701. throw TypeError('Unsupported type of rule: ' + ruleType);
  702. }
  703. // console.log('Adding rule: ' + rules[i])
  704. // console.log(newRule)
  705. ruleSet.push(newRule);
  706. }
  707. return ruleSet;
  708. }
  709. var _lastsym = 0;
  710. function _getExpandPlaceholderSymbol() {
  711. return new SymbolNode('_p' + _lastsym++);
  712. }
  713. function _simplify(expr, rules) {
  714. var scope = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : (0, _map.createEmptyMap)();
  715. var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
  716. var debug = options.consoleDebug;
  717. rules = _buildRules(rules || simplify.rules, options.context);
  718. var res = resolve(expr, scope);
  719. res = removeParens(res);
  720. var visited = {};
  721. var str = res.toString({
  722. parenthesis: 'all'
  723. });
  724. while (!visited[str]) {
  725. visited[str] = true;
  726. _lastsym = 0; // counter for placeholder symbols
  727. var laststr = str;
  728. if (debug) console.log('Working on: ', str);
  729. for (var i = 0; i < rules.length; i++) {
  730. var rulestr = '';
  731. if (typeof rules[i] === 'function') {
  732. res = rules[i](res, options);
  733. if (debug) rulestr = rules[i].name;
  734. } else {
  735. flatten(res, options.context);
  736. res = applyRule(res, rules[i], options.context);
  737. if (debug) {
  738. rulestr = "".concat(rules[i].l.toString(), " -> ").concat(rules[i].r.toString());
  739. }
  740. }
  741. if (debug) {
  742. var newstr = res.toString({
  743. parenthesis: 'all'
  744. });
  745. if (newstr !== laststr) {
  746. console.log('Applying', rulestr, 'produced', newstr);
  747. laststr = newstr;
  748. }
  749. }
  750. /* Use left-heavy binary tree internally,
  751. * since custom rule functions may expect it
  752. */
  753. unflattenl(res, options.context);
  754. }
  755. str = res.toString({
  756. parenthesis: 'all'
  757. });
  758. }
  759. return res;
  760. }
  761. function mapRule(nodes, rule, context) {
  762. var resNodes = nodes;
  763. if (nodes) {
  764. for (var i = 0; i < nodes.length; ++i) {
  765. var newNode = applyRule(nodes[i], rule, context);
  766. if (newNode !== nodes[i]) {
  767. if (resNodes === nodes) {
  768. resNodes = nodes.slice();
  769. }
  770. resNodes[i] = newNode;
  771. }
  772. }
  773. }
  774. return resNodes;
  775. }
  776. /**
  777. * Returns a simplfied form of node, or the original node if no simplification was possible.
  778. *
  779. * @param {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode} node
  780. * @param {Object | Function} rule
  781. * @param {Object} context -- information about assumed properties of operators
  782. * @return {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode} The simplified form of `expr`, or the original node if no simplification was possible.
  783. */
  784. function applyRule(node, rule, context) {
  785. // console.log('Entering applyRule("', rule.l.toString({parenthesis:'all'}), '->', rule.r.toString({parenthesis:'all'}), '",', node.toString({parenthesis:'all'}),')')
  786. // check that the assumptions for this rule are satisfied by the current
  787. // context:
  788. if (rule.assuming) {
  789. for (var symbol in rule.assuming) {
  790. for (var property in rule.assuming[symbol]) {
  791. if (hasProperty(symbol, property, context) !== rule.assuming[symbol][property]) {
  792. return node;
  793. }
  794. }
  795. }
  796. }
  797. var mergedContext = mergeContext(rule.imposeContext, context);
  798. // Do not clone node unless we find a match
  799. var res = node;
  800. // First replace our child nodes with their simplified versions
  801. // If a child could not be simplified, applying the rule to it
  802. // will have no effect since the node is returned unchanged
  803. if (res instanceof OperatorNode || res instanceof FunctionNode) {
  804. var newArgs = mapRule(res.args, rule, context);
  805. if (newArgs !== res.args) {
  806. res = res.clone();
  807. res.args = newArgs;
  808. }
  809. } else if (res instanceof ParenthesisNode) {
  810. if (res.content) {
  811. var newContent = applyRule(res.content, rule, context);
  812. if (newContent !== res.content) {
  813. res = new ParenthesisNode(newContent);
  814. }
  815. }
  816. } else if (res instanceof ArrayNode) {
  817. var newItems = mapRule(res.items, rule, context);
  818. if (newItems !== res.items) {
  819. res = new ArrayNode(newItems);
  820. }
  821. } else if (res instanceof AccessorNode) {
  822. var newObj = res.object;
  823. if (res.object) {
  824. newObj = applyRule(res.object, rule, context);
  825. }
  826. var newIndex = res.index;
  827. if (res.index) {
  828. newIndex = applyRule(res.index, rule, context);
  829. }
  830. if (newObj !== res.object || newIndex !== res.index) {
  831. res = new AccessorNode(newObj, newIndex);
  832. }
  833. } else if (res instanceof IndexNode) {
  834. var newDims = mapRule(res.dimensions, rule, context);
  835. if (newDims !== res.dimensions) {
  836. res = new IndexNode(newDims);
  837. }
  838. } else if (res instanceof ObjectNode) {
  839. var changed = false;
  840. var newProps = {};
  841. for (var prop in res.properties) {
  842. newProps[prop] = applyRule(res.properties[prop], rule, context);
  843. if (newProps[prop] !== res.properties[prop]) {
  844. changed = true;
  845. }
  846. }
  847. if (changed) {
  848. res = new ObjectNode(newProps);
  849. }
  850. }
  851. // Try to match a rule against this node
  852. var repl = rule.r;
  853. var matches = _ruleMatch(rule.l, res, mergedContext)[0];
  854. // If the rule is associative operator, we can try matching it while allowing additional terms.
  855. // This allows us to match rules like 'n+n' to the expression '(1+x)+x' or even 'x+1+x' if the operator is commutative.
  856. if (!matches && rule.expanded) {
  857. repl = rule.expanded.r;
  858. matches = _ruleMatch(rule.expanded.l, res, mergedContext)[0];
  859. }
  860. // Additional, non-commutative context expansion-rules
  861. if (!matches && rule.expandedNC1) {
  862. repl = rule.expandedNC1.r;
  863. matches = _ruleMatch(rule.expandedNC1.l, res, mergedContext)[0];
  864. if (!matches) {
  865. // Existence of NC1 implies NC2
  866. repl = rule.expandedNC2.r;
  867. matches = _ruleMatch(rule.expandedNC2.l, res, mergedContext)[0];
  868. }
  869. }
  870. if (matches) {
  871. // const before = res.toString({parenthesis: 'all'})
  872. // Create a new node by cloning the rhs of the matched rule
  873. // we keep any implicit multiplication state if relevant
  874. var implicit = res.implicit;
  875. res = repl.clone();
  876. if (implicit && 'implicit' in repl) {
  877. res.implicit = true;
  878. }
  879. // Replace placeholders with their respective nodes without traversing deeper into the replaced nodes
  880. res = res.transform(function (node) {
  881. if (node.isSymbolNode && (0, _object.hasOwnProperty)(matches.placeholders, node.name)) {
  882. return matches.placeholders[node.name].clone();
  883. } else {
  884. return node;
  885. }
  886. });
  887. // const after = res.toString({parenthesis: 'all'})
  888. // console.log('Simplified ' + before + ' to ' + after)
  889. }
  890. if (rule.repeat && res !== node) {
  891. res = applyRule(res, rule, context);
  892. }
  893. return res;
  894. }
  895. /**
  896. * Get (binary) combinations of a flattened binary node
  897. * e.g. +(node1, node2, node3) -> [
  898. * +(node1, +(node2, node3)),
  899. * +(node2, +(node1, node3)),
  900. * +(node3, +(node1, node2))]
  901. *
  902. */
  903. function getSplits(node, context) {
  904. var res = [];
  905. var right, rightArgs;
  906. var makeNode = createMakeNodeFunction(node);
  907. if (isCommutative(node, context)) {
  908. for (var i = 0; i < node.args.length; i++) {
  909. rightArgs = node.args.slice(0);
  910. rightArgs.splice(i, 1);
  911. right = rightArgs.length === 1 ? rightArgs[0] : makeNode(rightArgs);
  912. res.push(makeNode([node.args[i], right]));
  913. }
  914. } else {
  915. // Keep order, but try all parenthesizations
  916. for (var _i2 = 1; _i2 < node.args.length; _i2++) {
  917. var left = node.args[0];
  918. if (_i2 > 1) {
  919. left = makeNode(node.args.slice(0, _i2));
  920. }
  921. rightArgs = node.args.slice(_i2);
  922. right = rightArgs.length === 1 ? rightArgs[0] : makeNode(rightArgs);
  923. res.push(makeNode([left, right]));
  924. }
  925. }
  926. return res;
  927. }
  928. /**
  929. * Returns the set union of two match-placeholders or null if there is a conflict.
  930. */
  931. function mergeMatch(match1, match2) {
  932. var res = {
  933. placeholders: {}
  934. };
  935. // Some matches may not have placeholders; this is OK
  936. if (!match1.placeholders && !match2.placeholders) {
  937. return res;
  938. } else if (!match1.placeholders) {
  939. return match2;
  940. } else if (!match2.placeholders) {
  941. return match1;
  942. }
  943. // Placeholders with the same key must match exactly
  944. for (var key in match1.placeholders) {
  945. if ((0, _object.hasOwnProperty)(match1.placeholders, key)) {
  946. res.placeholders[key] = match1.placeholders[key];
  947. if ((0, _object.hasOwnProperty)(match2.placeholders, key)) {
  948. if (!_exactMatch(match1.placeholders[key], match2.placeholders[key])) {
  949. return null;
  950. }
  951. }
  952. }
  953. }
  954. for (var _key in match2.placeholders) {
  955. if ((0, _object.hasOwnProperty)(match2.placeholders, _key)) {
  956. res.placeholders[_key] = match2.placeholders[_key];
  957. }
  958. }
  959. return res;
  960. }
  961. /**
  962. * Combine two lists of matches by applying mergeMatch to the cartesian product of two lists of matches.
  963. * Each list represents matches found in one child of a node.
  964. */
  965. function combineChildMatches(list1, list2) {
  966. var res = [];
  967. if (list1.length === 0 || list2.length === 0) {
  968. return res;
  969. }
  970. var merged;
  971. for (var i1 = 0; i1 < list1.length; i1++) {
  972. for (var i2 = 0; i2 < list2.length; i2++) {
  973. merged = mergeMatch(list1[i1], list2[i2]);
  974. if (merged) {
  975. res.push(merged);
  976. }
  977. }
  978. }
  979. return res;
  980. }
  981. /**
  982. * Combine multiple lists of matches by applying mergeMatch to the cartesian product of two lists of matches.
  983. * Each list represents matches found in one child of a node.
  984. * Returns a list of unique matches.
  985. */
  986. function mergeChildMatches(childMatches) {
  987. if (childMatches.length === 0) {
  988. return childMatches;
  989. }
  990. var sets = childMatches.reduce(combineChildMatches);
  991. var uniqueSets = [];
  992. var unique = {};
  993. for (var i = 0; i < sets.length; i++) {
  994. var s = JSON.stringify(sets[i]);
  995. if (!unique[s]) {
  996. unique[s] = true;
  997. uniqueSets.push(sets[i]);
  998. }
  999. }
  1000. return uniqueSets;
  1001. }
  1002. /**
  1003. * Determines whether node matches rule.
  1004. *
  1005. * @param {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode} rule
  1006. * @param {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode} node
  1007. * @param {Object} context -- provides assumed properties of operators
  1008. * @param {Boolean} isSplit -- whether we are in process of splitting an
  1009. * n-ary operator node into possible binary combinations.
  1010. * Defaults to false.
  1011. * @return {Object} Information about the match, if it exists.
  1012. */
  1013. function _ruleMatch(rule, node, context, isSplit) {
  1014. // console.log('Entering _ruleMatch(' + JSON.stringify(rule) + ', ' + JSON.stringify(node) + ')')
  1015. // console.log('rule = ' + rule)
  1016. // console.log('node = ' + node)
  1017. // console.log('Entering _ruleMatch(', rule.toString({parenthesis:'all'}), ', ', node.toString({parenthesis:'all'}), ', ', context, ')')
  1018. var res = [{
  1019. placeholders: {}
  1020. }];
  1021. if (rule instanceof OperatorNode && node instanceof OperatorNode || rule instanceof FunctionNode && node instanceof FunctionNode) {
  1022. // If the rule is an OperatorNode or a FunctionNode, then node must match exactly
  1023. if (rule instanceof OperatorNode) {
  1024. if (rule.op !== node.op || rule.fn !== node.fn) {
  1025. return [];
  1026. }
  1027. } else if (rule instanceof FunctionNode) {
  1028. if (rule.name !== node.name) {
  1029. return [];
  1030. }
  1031. }
  1032. // rule and node match. Search the children of rule and node.
  1033. if (node.args.length === 1 && rule.args.length === 1 || !isAssociative(node, context) && node.args.length === rule.args.length || isSplit) {
  1034. // Expect non-associative operators to match exactly,
  1035. // except in any order if operator is commutative
  1036. var childMatches = [];
  1037. for (var i = 0; i < rule.args.length; i++) {
  1038. var childMatch = _ruleMatch(rule.args[i], node.args[i], context);
  1039. if (childMatch.length === 0) {
  1040. // Child did not match, so stop searching immediately
  1041. break;
  1042. }
  1043. // The child matched, so add the information returned from the child to our result
  1044. childMatches.push(childMatch);
  1045. }
  1046. if (childMatches.length !== rule.args.length) {
  1047. if (!isCommutative(node, context) ||
  1048. // exact match in order needed
  1049. rule.args.length === 1) {
  1050. // nothing to commute
  1051. return [];
  1052. }
  1053. if (rule.args.length > 2) {
  1054. /* Need to generate all permutations and try them.
  1055. * It's a bit complicated, and unlikely to come up since there
  1056. * are very few ternary or higher operators. So punt for now.
  1057. */
  1058. throw new Error('permuting >2 commutative non-associative rule arguments not yet implemented');
  1059. }
  1060. /* Exactly two arguments, try them reversed */
  1061. var leftMatch = _ruleMatch(rule.args[0], node.args[1], context);
  1062. if (leftMatch.length === 0) {
  1063. return [];
  1064. }
  1065. var rightMatch = _ruleMatch(rule.args[1], node.args[0], context);
  1066. if (rightMatch.length === 0) {
  1067. return [];
  1068. }
  1069. childMatches = [leftMatch, rightMatch];
  1070. }
  1071. res = mergeChildMatches(childMatches);
  1072. } else if (node.args.length >= 2 && rule.args.length === 2) {
  1073. // node is flattened, rule is not
  1074. // Associative operators/functions can be split in different ways so we check if the rule
  1075. // matches for each of them and return their union.
  1076. var splits = getSplits(node, context);
  1077. var splitMatches = [];
  1078. for (var _i3 = 0; _i3 < splits.length; _i3++) {
  1079. var matchSet = _ruleMatch(rule, splits[_i3], context, true); // recursing at the same tree depth here
  1080. splitMatches = splitMatches.concat(matchSet);
  1081. }
  1082. return splitMatches;
  1083. } else if (rule.args.length > 2) {
  1084. throw Error('Unexpected non-binary associative function: ' + rule.toString());
  1085. } else {
  1086. // Incorrect number of arguments in rule and node, so no match
  1087. return [];
  1088. }
  1089. } else if (rule instanceof SymbolNode) {
  1090. // If the rule is a SymbolNode, then it carries a special meaning
  1091. // according to the first one or two characters of the symbol node name.
  1092. // These meanings are expalined in the documentation for simplify()
  1093. if (rule.name.length === 0) {
  1094. throw new Error('Symbol in rule has 0 length...!?');
  1095. }
  1096. if (SUPPORTED_CONSTANTS[rule.name]) {
  1097. // built-in constant must match exactly
  1098. if (rule.name !== node.name) {
  1099. return [];
  1100. }
  1101. } else {
  1102. // wildcards are composed of up to two alphabetic or underscore characters
  1103. switch (rule.name[1] >= 'a' && rule.name[1] <= 'z' ? rule.name.substring(0, 2) : rule.name[0]) {
  1104. case 'n':
  1105. case '_p':
  1106. // rule matches _anything_, so assign this node to the rule.name placeholder
  1107. // Assign node to the rule.name placeholder.
  1108. // Our parent will check for matches among placeholders.
  1109. res[0].placeholders[rule.name] = node;
  1110. break;
  1111. case 'c':
  1112. case 'cl':
  1113. // rule matches a ConstantNode
  1114. if ((0, _wildcards.isConstantNode)(node)) {
  1115. res[0].placeholders[rule.name] = node;
  1116. } else {
  1117. // mis-match: rule does not encompass current node
  1118. return [];
  1119. }
  1120. break;
  1121. case 'v':
  1122. // rule matches anything other than a ConstantNode
  1123. if (!(0, _wildcards.isConstantNode)(node)) {
  1124. res[0].placeholders[rule.name] = node;
  1125. } else {
  1126. // mis-match: rule does not encompass current node
  1127. return [];
  1128. }
  1129. break;
  1130. case 'vl':
  1131. // rule matches VariableNode
  1132. if ((0, _wildcards.isVariableNode)(node)) {
  1133. res[0].placeholders[rule.name] = node;
  1134. } else {
  1135. // mis-match: rule does not encompass current node
  1136. return [];
  1137. }
  1138. break;
  1139. case 'cd':
  1140. // rule matches a ConstantNode or unaryMinus-wrapped ConstantNode
  1141. if ((0, _wildcards.isNumericNode)(node)) {
  1142. res[0].placeholders[rule.name] = node;
  1143. } else {
  1144. // mis-match: rule does not encompass current node
  1145. return [];
  1146. }
  1147. break;
  1148. case 'vd':
  1149. // rule matches anything other than a ConstantNode or unaryMinus-wrapped ConstantNode
  1150. if (!(0, _wildcards.isNumericNode)(node)) {
  1151. res[0].placeholders[rule.name] = node;
  1152. } else {
  1153. // mis-match: rule does not encompass current node
  1154. return [];
  1155. }
  1156. break;
  1157. case 'ce':
  1158. // rule matches expressions that have a constant value
  1159. if ((0, _wildcards.isConstantExpression)(node)) {
  1160. res[0].placeholders[rule.name] = node;
  1161. } else {
  1162. // mis-match: rule does not encompass current node
  1163. return [];
  1164. }
  1165. break;
  1166. case 've':
  1167. // rule matches expressions that do not have a constant value
  1168. if (!(0, _wildcards.isConstantExpression)(node)) {
  1169. res[0].placeholders[rule.name] = node;
  1170. } else {
  1171. // mis-match: rule does not encompass current node
  1172. return [];
  1173. }
  1174. break;
  1175. default:
  1176. throw new Error('Invalid symbol in rule: ' + rule.name);
  1177. }
  1178. }
  1179. } else if (rule instanceof ConstantNode) {
  1180. // Literal constant must match exactly
  1181. if (!equal(rule.value, node.value)) {
  1182. return [];
  1183. }
  1184. } else {
  1185. // Some other node was encountered which we aren't prepared for, so no match
  1186. return [];
  1187. }
  1188. // It's a match!
  1189. // console.log('_ruleMatch(' + rule.toString() + ', ' + node.toString() + ') found a match')
  1190. return res;
  1191. }
  1192. /**
  1193. * Determines whether p and q (and all their children nodes) are identical.
  1194. *
  1195. * @param {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode} p
  1196. * @param {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode} q
  1197. * @return {Object} Information about the match, if it exists.
  1198. */
  1199. function _exactMatch(p, q) {
  1200. if (p instanceof ConstantNode && q instanceof ConstantNode) {
  1201. if (!equal(p.value, q.value)) {
  1202. return false;
  1203. }
  1204. } else if (p instanceof SymbolNode && q instanceof SymbolNode) {
  1205. if (p.name !== q.name) {
  1206. return false;
  1207. }
  1208. } else if (p instanceof OperatorNode && q instanceof OperatorNode || p instanceof FunctionNode && q instanceof FunctionNode) {
  1209. if (p instanceof OperatorNode) {
  1210. if (p.op !== q.op || p.fn !== q.fn) {
  1211. return false;
  1212. }
  1213. } else if (p instanceof FunctionNode) {
  1214. if (p.name !== q.name) {
  1215. return false;
  1216. }
  1217. }
  1218. if (p.args.length !== q.args.length) {
  1219. return false;
  1220. }
  1221. for (var i = 0; i < p.args.length; i++) {
  1222. if (!_exactMatch(p.args[i], q.args[i])) {
  1223. return false;
  1224. }
  1225. }
  1226. } else {
  1227. return false;
  1228. }
  1229. return true;
  1230. }
  1231. return simplify;
  1232. });
  1233. exports.createSimplify = createSimplify;