mathsteps-experimental-fork
Version:
Step by step math solutions. Experimental Fork
874 lines (870 loc) • 19.2 kB
JavaScript
'use strict';
const index = require('../../node/index.js');
const ChangeTypes = require('../../types/changeType/ChangeTypes.js');
const nthRootLUT = {
2: {
4: [2, 0],
8: [2, 2],
9: [3, 0],
12: [2, 3],
16: [4, 0],
18: [3, 2],
20: [2, 5],
24: [2, 6],
25: [5, 0],
27: [3, 3],
28: [2, 7],
32: [4, 2],
36: [6, 0],
40: [2, 10],
44: [2, 11],
45: [3, 5],
48: [4, 3],
49: [7, 0],
50: [5, 2],
52: [2, 13],
54: [3, 6],
56: [2, 14],
60: [2, 15],
63: [3, 7],
64: [8, 0],
68: [2, 17],
72: [6, 2],
75: [5, 3],
76: [2, 19],
80: [4, 5],
81: [9, 0],
84: [2, 21],
88: [2, 22],
90: [3, 10],
92: [2, 23],
96: [4, 6],
98: [7, 2],
99: [3, 11],
100: [10, 0],
104: [2, 26],
108: [6, 3],
112: [4, 7],
116: [2, 29],
117: [3, 13],
120: [2, 30],
121: [11, 0],
124: [2, 31],
125: [5, 5],
126: [3, 14],
128: [8, 2],
132: [2, 33],
135: [3, 15],
136: [2, 34],
140: [2, 35],
144: [12, 0],
147: [7, 3],
148: [2, 37],
150: [5, 6],
152: [2, 38],
153: [3, 17],
156: [2, 39],
160: [4, 10],
162: [9, 2],
164: [2, 41],
168: [2, 42],
169: [13, 0],
171: [3, 19],
172: [2, 43],
175: [5, 7],
176: [4, 11],
180: [6, 5],
184: [2, 46],
188: [2, 47],
189: [3, 21],
192: [8, 3],
196: [14, 0],
198: [3, 22],
200: [10, 2],
204: [2, 51],
207: [3, 23],
208: [4, 13],
212: [2, 53],
216: [6, 6],
220: [2, 55],
224: [4, 14],
225: [15, 0],
228: [2, 57],
232: [2, 58],
234: [3, 26],
236: [2, 59],
240: [4, 15],
242: [11, 2],
243: [9, 3],
244: [2, 61],
245: [7, 5],
248: [2, 62],
250: [5, 10],
252: [6, 7],
256: [16, 0],
261: [3, 29],
270: [3, 30],
272: [4, 17],
275: [5, 11],
279: [3, 31],
288: [12, 2],
289: [17, 0],
294: [7, 6],
297: [3, 33],
300: [10, 3],
304: [4, 19],
306: [3, 34],
315: [3, 35],
320: [8, 5],
324: [18, 0],
325: [5, 13],
333: [3, 37],
336: [4, 21],
338: [13, 2],
342: [3, 38],
343: [7, 7],
350: [5, 14],
351: [3, 39],
352: [4, 22],
360: [6, 10],
361: [19, 0],
363: [11, 3],
368: [4, 23],
369: [3, 41],
375: [5, 15],
378: [3, 42],
384: [8, 6],
387: [3, 43],
392: [14, 2],
396: [6, 11],
400: [20, 0],
405: [9, 5],
414: [3, 46],
416: [4, 26],
423: [3, 47],
425: [5, 17],
432: [12, 3],
441: [21, 0],
448: [8, 7],
450: [15, 2],
459: [3, 51],
464: [4, 29],
468: [6, 13],
475: [5, 19],
477: [3, 53],
480: [4, 30],
484: [22, 0],
486: [9, 6],
490: [7, 10],
495: [3, 55],
496: [4, 31],
500: [10, 5],
504: [6, 14],
507: [13, 3],
512: [16, 2],
513: [3, 57],
522: [3, 58],
525: [5, 21],
528: [4, 33],
529: [23, 0],
531: [3, 59],
539: [7, 11],
540: [6, 15],
544: [4, 34],
549: [3, 61],
550: [5, 22],
558: [3, 62],
560: [4, 35],
567: [9, 7],
575: [5, 23],
576: [24, 0],
578: [17, 2],
588: [14, 3],
592: [4, 37],
600: [10, 6],
605: [11, 5],
608: [4, 38],
612: [6, 17],
624: [4, 39],
625: [25, 0],
637: [7, 13],
640: [8, 10],
648: [18, 2],
650: [5, 26],
656: [4, 41],
672: [4, 42],
675: [15, 3],
676: [26, 0],
684: [6, 19],
686: [7, 14],
688: [4, 43],
700: [10, 7],
704: [8, 11],
720: [12, 5],
722: [19, 2],
725: [5, 29],
726: [11, 6],
729: [27, 0],
735: [7, 15],
736: [4, 46],
750: [5, 30],
752: [4, 47],
756: [6, 21],
768: [16, 3],
775: [5, 31],
784: [28, 0],
792: [6, 22],
800: [20, 2],
810: [9, 10],
816: [4, 51],
825: [5, 33],
828: [6, 23],
832: [8, 13],
833: [7, 17],
841: [29, 0],
845: [13, 5],
847: [11, 7],
848: [4, 53],
850: [5, 34],
864: [12, 6],
867: [17, 3],
875: [5, 35],
880: [4, 55],
882: [21, 2],
891: [9, 11],
896: [8, 14],
900: [30, 0],
912: [4, 57],
925: [5, 37],
928: [4, 58],
931: [7, 19],
936: [6, 26],
944: [4, 59],
950: [5, 38],
960: [8, 15],
961: [31, 0],
968: [22, 2],
972: [18, 3],
975: [5, 39],
976: [4, 61],
980: [14, 5],
992: [4, 62],
1000: [10, 10],
1008: [12, 7],
1014: [13, 6],
1024: [32, 0],
1025: [5, 41],
1029: [7, 21],
1044: [6, 29],
1050: [5, 42],
1053: [9, 13],
1058: [23, 2],
1075: [5, 43],
1078: [7, 22],
1080: [6, 30],
1083: [19, 3],
1088: [8, 17],
1089: [33, 0],
1100: [10, 11],
1116: [6, 31],
1125: [15, 5],
1127: [7, 23],
1134: [9, 14],
1150: [5, 46],
1152: [24, 2],
1156: [34, 0],
1175: [5, 47],
1176: [14, 6],
1183: [13, 7],
1188: [6, 33],
1200: [20, 3],
1210: [11, 10],
1215: [9, 15],
1216: [8, 19],
1224: [6, 34],
1225: [35, 0],
1250: [25, 2],
1260: [6, 35],
1274: [7, 26],
1275: [5, 51],
1280: [16, 5],
1296: [36, 0],
1300: [10, 13],
1323: [21, 3],
1325: [5, 53],
1331: [11, 11],
1332: [6, 37],
1344: [8, 21],
1350: [15, 6],
1352: [26, 2],
1368: [6, 38],
1369: [37, 0],
1372: [14, 7],
1375: [5, 55],
1377: [9, 17],
1400: [10, 14],
1404: [6, 39],
1408: [8, 22],
1421: [7, 29],
1425: [5, 57],
1440: [12, 10],
1444: [38, 0],
1445: [17, 5],
1450: [5, 58],
1452: [22, 3],
1458: [27, 2],
1470: [7, 30],
1472: [8, 23],
1475: [5, 59],
1476: [6, 41],
1500: [10, 15],
1512: [6, 42],
1519: [7, 31],
1521: [39, 0],
1525: [5, 61],
1536: [16, 6],
1539: [9, 19],
1548: [6, 43],
1550: [5, 62],
1568: [28, 2],
1573: [11, 13],
1575: [15, 7],
1584: [12, 11],
1587: [23, 3],
1600: [40, 0],
1617: [7, 33],
1620: [18, 5],
1656: [6, 46],
1664: [8, 26],
1666: [7, 34],
1681: [41, 0],
1682: [29, 2],
1690: [13, 10],
1692: [6, 47],
1694: [11, 14],
1700: [10, 17],
1701: [9, 21],
1715: [7, 35],
1728: [24, 3],
1734: [17, 6],
1764: [42, 0],
1782: [9, 22],
1792: [16, 7],
1800: [30, 2],
1805: [19, 5],
1813: [7, 37],
1815: [11, 15],
1836: [6, 51],
1849: [43, 0],
1856: [8, 29],
1859: [13, 11],
1862: [7, 38],
1863: [9, 23],
1872: [12, 13],
1875: [25, 3],
1900: [10, 19],
1908: [6, 53],
1911: [7, 39],
1920: [8, 30],
1922: [31, 2],
1936: [44, 0],
1944: [18, 6],
1960: [14, 10],
1980: [6, 55],
1984: [8, 31],
2000: [20, 5],
2009: [7, 41],
2016: [12, 14],
2023: [17, 7],
2025: [45, 0],
2028: [26, 3],
2048: [32, 2],
2052: [6, 57],
2057: [11, 17],
2058: [7, 42],
2088: [6, 58],
2100: [10, 21],
2106: [9, 26],
2107: [7, 43],
2112: [8, 33],
2116: [46, 0],
2124: [6, 59],
2156: [14, 11],
2160: [12, 15],
2166: [19, 6],
2176: [8, 34],
2178: [33, 2],
2187: [27, 3],
2196: [6, 61],
2197: [13, 13],
2200: [10, 22],
2205: [21, 5],
2209: [47, 0],
2232: [6, 62],
2240: [8, 35],
2250: [15, 10],
2254: [7, 46],
2268: [18, 7],
2299: [11, 19],
2300: [10, 23],
2303: [7, 47],
2304: [48, 0],
2312: [34, 2],
2349: [9, 29],
2352: [28, 3],
2366: [13, 14],
2368: [8, 37],
2400: [20, 6],
2401: [49, 0],
2420: [22, 5],
2430: [9, 30],
2432: [8, 38],
2448: [12, 17],
2450: [35, 2],
2475: [15, 11],
2496: [8, 39],
2499: [7, 51],
2500: [50, 0],
2511: [9, 31],
2523: [29, 3],
2527: [19, 7],
2535: [13, 15],
2541: [11, 21],
2548: [14, 13],
2560: [16, 10],
2592: [36, 2],
2597: [7, 53],
2600: [10, 26],
2601: [51, 0],
2624: [8, 41],
2645: [23, 5],
2646: [21, 6],
2662: [11, 22],
2673: [9, 33],
2688: [8, 42],
2695: [7, 55],
2700: [30, 3],
2704: [52, 0],
2736: [12, 19],
2738: [37, 2],
2744: [14, 14],
2752: [8, 43],
2754: [9, 34],
2783: [11, 23],
2793: [7, 57],
2800: [20, 7],
2809: [53, 0],
2816: [16, 11],
2835: [9, 35],
2842: [7, 58],
2873: [13, 17],
2880: [24, 5],
2883: [31, 3],
2888: [38, 2],
2890: [17, 10],
2891: [7, 59],
2900: [10, 29],
2904: [22, 6],
2916: [54, 0],
2925: [15, 13],
2940: [14, 15],
2944: [8, 46],
2989: [7, 61],
2997: [9, 37],
3000: [10, 30],
3008: [8, 47],
3024: [12, 21],
3025: [55, 0],
3038: [7, 62],
3042: [39, 2],
3072: [32, 3],
3078: [9, 38],
3087: [21, 7],
3100: [10, 31],
3125: [25, 5],
3136: [56, 0],
3146: [11, 26],
3150: [15, 14],
3159: [9, 39],
3168: [12, 22],
3174: [23, 6],
3179: [17, 11],
3200: [40, 2],
3211: [13, 19],
3240: [18, 10],
3249: [57, 0],
3264: [8, 51],
3267: [33, 3],
3300: [10, 33],
3312: [12, 23],
3321: [9, 41],
3328: [16, 13],
3332: [14, 17],
3362: [41, 2],
3364: [58, 0],
3375: [15, 15],
3380: [26, 5],
3388: [22, 7],
3392: [8, 53],
3400: [10, 34],
3402: [9, 42],
3456: [24, 6],
3468: [34, 3],
3481: [59, 0],
3483: [9, 43],
3500: [10, 35],
3509: [11, 29],
3520: [8, 55],
3528: [42, 2],
3549: [13, 21],
3564: [18, 11],
3584: [16, 14],
3600: [60, 0],
3610: [19, 10],
3630: [11, 30],
3645: [27, 5],
3648: [8, 57],
3675: [35, 3],
3698: [43, 2],
3700: [10, 37],
3703: [23, 7],
3712: [8, 58],
3718: [13, 22],
3721: [61, 0],
3724: [14, 19],
3726: [9, 46],
3744: [12, 26],
3750: [25, 6],
3751: [11, 31],
3757: [17, 13],
3776: [8, 59],
3800: [10, 38],
3807: [9, 47],
3825: [15, 17],
3840: [16, 15],
3844: [62, 0],
3872: [44, 2],
3887: [13, 23],
3888: [36, 3],
3900: [10, 39],
3904: [8, 61],
3920: [28, 5],
3968: [8, 62],
3969: [63, 0],
3971: [19, 11],
3993: [11, 33],
4000: [20, 10],
4032: [24, 7],
4046: [17, 14],
4050: [45, 2],
4056: [26, 6],
},
3: {
8: [2, 0],
16: [2, 2],
24: [2, 3],
27: [3, 0],
32: [2, 4],
40: [2, 5],
48: [2, 6],
54: [3, 2],
56: [2, 7],
64: [4, 0],
72: [2, 9],
80: [2, 10],
81: [3, 3],
88: [2, 11],
96: [2, 12],
104: [2, 13],
108: [3, 4],
112: [2, 14],
125: [5, 0],
128: [4, 2],
135: [3, 5],
162: [3, 6],
189: [3, 7],
192: [4, 3],
216: [6, 0],
243: [3, 9],
250: [5, 2],
256: [4, 4],
270: [3, 10],
297: [3, 11],
320: [4, 5],
324: [3, 12],
343: [7, 0],
351: [3, 13],
375: [5, 3],
378: [3, 14],
384: [4, 6],
432: [6, 2],
448: [4, 7],
500: [5, 4],
512: [8, 0],
576: [4, 9],
625: [5, 5],
640: [4, 10],
648: [6, 3],
686: [7, 2],
704: [4, 11],
729: [9, 0],
750: [5, 6],
768: [4, 12],
832: [4, 13],
864: [6, 4],
875: [5, 7],
896: [4, 14],
1000: [10, 0],
1024: [8, 2],
1029: [7, 3],
1080: [6, 5],
1125: [5, 9],
1250: [5, 10],
1296: [6, 6],
1331: [11, 0],
1372: [7, 4],
1375: [5, 11],
1458: [9, 2],
1500: [5, 12],
1512: [6, 7],
1536: [8, 3],
1625: [5, 13],
1715: [7, 5],
1728: [12, 0],
1750: [5, 14],
1944: [6, 9],
2000: [10, 2],
2048: [8, 4],
2058: [7, 6],
2160: [6, 10],
2187: [9, 3],
2197: [13, 0],
2376: [6, 11],
2401: [7, 7],
2560: [8, 5],
2592: [6, 12],
2662: [11, 2],
2744: [14, 0],
2808: [6, 13],
2916: [9, 4],
3000: [10, 3],
3024: [6, 14],
3072: [8, 6],
3087: [7, 9],
3430: [7, 10],
3456: [12, 2],
3584: [8, 7],
3645: [9, 5],
3773: [7, 11],
3993: [11, 3],
4000: [10, 4],
},
4: {
16: [2, 0],
32: [2, 2],
48: [2, 3],
64: [2, 4],
80: [2, 5],
81: [3, 0],
96: [2, 6],
112: [2, 7],
162: [3, 2],
243: [3, 3],
256: [4, 0],
324: [3, 4],
405: [3, 5],
486: [3, 6],
512: [4, 2],
567: [3, 7],
625: [5, 0],
768: [4, 3],
1024: [4, 4],
1250: [5, 2],
1280: [4, 5],
1296: [6, 0],
1536: [4, 6],
1792: [4, 7],
1875: [5, 3],
2401: [7, 0],
2500: [5, 4],
2592: [6, 2],
3125: [5, 5],
3750: [5, 6],
3888: [6, 3],
},
5: {
32: [2, 0],
64: [2, 2],
96: [2, 3],
128: [2, 4],
243: [3, 0],
486: [3, 2],
729: [3, 3],
972: [3, 4],
1024: [4, 0],
2048: [4, 2],
3072: [4, 3],
},
6: { 64: [2, 0], 128: [2, 2] },
7: { 128: [2, 0], 256: [2, 2] },
};
const NODE_TWO = index.default.Creator.constant(2);
function _factorRadicandNode(radicand, exponent) {
let rv = null;
let p = null;
let q = null;
exponent = exponent || NODE_TWO;
if (index.default.Type.isConstant(radicand) && nthRootLUT[exponent.value]) {
const entryLUT = nthRootLUT[exponent.value][radicand.value];
if (entryLUT) {
// Use lookup table for common radicands e.g. sqrt(8) -> 2 sqrt(2)
p = entryLUT[0];
q = entryLUT[1];
}
else {
// General algorithm.
// Try to convert into form: p sqrt(q).
const value = radicand.value;
const nMax = Math.floor(Math.sqrt(value));
// Possible improvement: Use factorization.
for (let n = nMax; n > 1; n--) {
const nToExp = n ** exponent.value;
if ((value % nToExp) === 0) {
p = n;
q = value / nToExp;
break
}
}
}
}
if (p) {
rv = {
p,
q,
pNode: index.default.Creator.constant(p),
};
if (q && (q !== 1)) {
// p * nthRoot(q, exponent)
rv.qNode = index.default.Creator.constant(q);
rv.p2Node = index.default.Creator.operator('^', [rv.pNode, exponent]);
rv.p2qNode = index.default.Creator.operator('*', [rv.p2Node, rv.qNode], false);
rv.sqrtP2qNode = index.default.Creator.nthRoot(rv.p2qNode, exponent);
rv.sqrtQNode = index.default.Creator.nthRoot(rv.qNode, exponent);
}
}
return rv
}
function _buildResultNode(pNode, args, argIdx, qNode, expNode) {
let rv = null;
// p * sqrt(... a * b * q * d * e * ...)
// Clone original args.
const newArgs = args.slice(0);
if (qNode) {
// Replace one arg by qNode.
newArgs[argIdx] = qNode;
}
else {
// qNode is null - just remove original arg.
newArgs.splice(argIdx, 1);
}
if (newArgs.length > 0) {
// Wrap args into product: ... * a * b * q * d * e * ...
const newArgsProduct = newArgs.length > 1
// sqrt(a*b*c*d*...) -> sqrt(c) * sqrt(a*b*d*...)
? index.default.Creator.operator('*', newArgs, false)
// sqrt(x*y) -> sqrt(x) * sqrt(y)
: newArgs[0];
// Build right node: sqrt(... * a * b * q * d * e * ...)
const sqrtRightNode = index.default.Creator.nthRoot(newArgsProduct, expNode);
if (pNode) {
// Build result node: p * sqrt(... a * b * q * d * e * ...)
rv = index.default.Creator.operator('*', [pNode, sqrtRightNode], true);
}
else {
// Build result node: sqrt(... a * b * q * d * e * ...)
rv = sqrtRightNode;
}
}
else {
// Perfect match - whole sqrt gone away.
rv = pNode;
}
return rv
}
function _collectFactors(rv, node) {
if (node.op === '*') {
node.args.forEach((oneArg) => {
_collectFactors(rv, oneArg);
});
}
else {
rv.push(node);
}
return rv
}
function sqrtFromConstant(node) {
let rv = null;
if (index.default.Type.isNthRoot(node)) {
const baseNode = node.args[0];
const expNode = node.args[1] || NODE_TWO;
if (index.default.Type.isOperator(baseNode, '/')) {
// sqrt(x/y)
// Possible improvement: optimize it.
const sqrtX = index.default.Creator.nthRoot(baseNode.args[0], expNode);
const sqrtY = index.default.Creator.nthRoot(baseNode.args[1], expNode);
if (sqrtFromConstant(sqrtX) || sqrtFromConstant(sqrtY)) {
// sqrt(x/y) gives sqrt(x) / sqrt(y)
const newNode = index.default.Creator.operator('/', [sqrtX, sqrtY]);
rv = {
changeType: ChangeTypes.ChangeTypes.KEMU_ROOT_FROM_FRACTION,
rootNode: newNode,
};
}
}
else {
// sqrt(...)
const args = _collectFactors([], node.args[0]);
for (const argIdx in args) {
const oneFactor = args[argIdx];
const factorResult = _factorRadicandNode(oneFactor, expNode);
if (factorResult) {
// Breakable factor found.
// Clone original args.
const pNode = factorResult.pNode;
const qNode = factorResult.qNode;
if (qNode) {
// Non-perfect match: sqrt(x) -> p sqrt(q)
// Step I: sqrt(p^2 * q)
const stepNode1 = _buildResultNode(null, args, argIdx, factorResult.p2qNode, expNode);
// Step II: p sqrt(q)
const stepNode2 = _buildResultNode(pNode, args, argIdx, qNode, expNode);
// Prepare substeps.
const substeps = [
{ changeType: ChangeTypes.ChangeTypes.KEMU_FACTOR_EXPRESSION_UNDER_ROOT, rootNode: stepNode1 },
{ changeType: ChangeTypes.ChangeTypes.KEMU_SQRT_FROM_POW, rootNode: stepNode2 },
];
// Final result.
rv = {
changeType: ChangeTypes.ChangeTypes.KEMU_SQRT_FROM_CONST,
rootNode: stepNode2,
substeps,
};
}
else {
// Perfect match: sqrt(x) -> p
const newNode = _buildResultNode(pNode, args, argIdx, null, expNode);
rv = {
changeType: ChangeTypes.ChangeTypes.KEMU_ROOT_FROM_CONST,
rootNode: newNode,
};
}
// Move out one breakable factor at one time.
// Don't go on anymore in current call.
break
}
}
}
}
return rv
}
module.exports = sqrtFromConstant;