Skip to content

Commit e7f0da0

Browse files
author
Mahir Shah
committed
Basic equation solver working, but non-commutative operands do not work
1 parent 88edb0e commit e7f0da0

21 files changed

+480
-39
lines changed

dist/scripts/BinaryNode.js

+12
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,18 @@ define(['exports', 'ExpressionNode', 'Operators'], function (exports, _Expressio
8686
value: function evaluate(context) {
8787
return _Operators.OPERATOR_FUNCTION_MAP.get(this.operator)(this.left.evaluate(context), this.right.evaluate(context));
8888
}
89+
}, {
90+
key: 'iterate',
91+
value: function iterate(callback) {
92+
var thisArg = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1];
93+
this.left.iterate(callback, thisArg);
94+
this.right.iterate(callback, thisArg);
95+
}
96+
}, {
97+
key: 'toInfix',
98+
value: function toInfix() {
99+
return '(' + this.left.toInfix() + this.operator + this.right.toInfix() + ')';
100+
}
89101
}]);
90102

91103
return BinaryNode;

dist/scripts/ConstantNode.js

+11
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,17 @@ define(['exports', 'ExpressionNode'], function (exports, _ExpressionNode2) {
8080
value: function evaluate(context) {
8181
return +context[this.constant] || this.constant;
8282
}
83+
}, {
84+
key: 'iterate',
85+
value: function iterate(callback) {
86+
var thisArg = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1];
87+
callback.call(thisArg, this.constant);
88+
}
89+
}, {
90+
key: 'toInfix',
91+
value: function toInfix() {
92+
return this.constant;
93+
}
8394
}]);
8495

8596
return ConstantNode;

dist/scripts/EquationInput.js

+28-8
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
'use strict';
22

3-
define(['exports', 'helperFunctions', 'ExpressionTree'], function (exports, _helperFunctions, _ExpressionTree) {
3+
define(['exports', 'helperFunctions', 'ExpressionTree', 'EquationSolver'], function (exports, _helperFunctions, _ExpressionTree, _EquationSolver) {
44
Object.defineProperty(exports, "__esModule", {
55
value: true
66
});
77

88
var _ExpressionTree2 = _interopRequireDefault(_ExpressionTree);
99

10+
var _EquationSolver2 = _interopRequireDefault(_EquationSolver);
11+
1012
function _interopRequireDefault(obj) {
1113
return obj && obj.__esModule ? obj : {
1214
default: obj
@@ -44,7 +46,8 @@ define(['exports', 'helperFunctions', 'ExpressionTree'], function (exports, _hel
4446
this.inputElement = inputElement;
4547
this.variableTable = variableTable;
4648
this.answerOutput = answerOutput;
47-
this.equationTree = new _ExpressionTree2.default(this.inputElement.value);
49+
this.expressionTree1 = new _ExpressionTree2.default();
50+
this.expressionTree2 = new _ExpressionTree2.default();
4851
this.value = null;
4952

5053
this._attachEvents();
@@ -55,14 +58,31 @@ define(['exports', 'helperFunctions', 'ExpressionTree'], function (exports, _hel
5558
value: function _attachEvents() {
5659
var _this = this;
5760

58-
this.inputElement.addEventListener('input', (0, _helperFunctions.debounce)(function (evt) {
59-
var fixedInput = _this._fixInputValue(_this.inputElement.value);
60-
61-
_this.equationTree.updateEquation(fixedInput);
61+
this.inputElement.addEventListener('input', (0, _helperFunctions.debounce)(function () {
62+
var inputValue = _this.inputElement.value;
63+
var numEquals = (inputValue.match(/=/g) || []).length;
6264

6365
try {
64-
_this.value = _this.equationTree.evaluate(_this.variableTable.getContext());
65-
_this.answerOutput.innerText = !isNaN(_this.value) ? _this.value : '';
66+
if (numEquals > 1) {
67+
throw new Error('Too many equal symbols');
68+
} else if (numEquals === 1) {
69+
var expressions = inputValue.split('=');
70+
var equationSolver = null;
71+
72+
_this.expressionTree1.updateEquation(expressions[0]);
73+
74+
_this.expressionTree2.updateEquation(expressions[1]);
75+
76+
equationSolver = new _EquationSolver2.default(_this.expressionTree1, _this.expressionTree2, _this.variableTable.getContext());
77+
_this.answerOutput.innerText = equationSolver.evaluate();
78+
} else {
79+
var fixedInput = _this._fixInputValue(_this.inputElement.value);
80+
81+
_this.expressionTree1.updateEquation(fixedInput);
82+
83+
_this.value = _this.expressionTree1.evaluate(_this.variableTable.getContext());
84+
_this.answerOutput.innerText = !isNaN(_this.value) ? _this.value : '';
85+
}
6686
} catch (exception) {
6787
_this.answerOutput.innerText = '';
6888
}

dist/scripts/EquationSolver.js

+110-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,111 @@
1-
/**
2-
* Created by mahirshah on 12/11/15.
3-
*/
4-
"use strict";
1+
'use strict';
52

6-
define([], function () {});
3+
define(['exports', 'ConstantNode', 'UnaryNode', 'Operators'], function (exports, _ConstantNode, _UnaryNode, _Operators) {
4+
Object.defineProperty(exports, "__esModule", {
5+
value: true
6+
});
7+
8+
var _ConstantNode2 = _interopRequireDefault(_ConstantNode);
9+
10+
var _UnaryNode2 = _interopRequireDefault(_UnaryNode);
11+
12+
function _interopRequireDefault(obj) {
13+
return obj && obj.__esModule ? obj : {
14+
default: obj
15+
};
16+
}
17+
18+
function _classCallCheck(instance, Constructor) {
19+
if (!(instance instanceof Constructor)) {
20+
throw new TypeError("Cannot call a class as a function");
21+
}
22+
}
23+
24+
var _createClass = (function () {
25+
function defineProperties(target, props) {
26+
for (var i = 0; i < props.length; i++) {
27+
var descriptor = props[i];
28+
descriptor.enumerable = descriptor.enumerable || false;
29+
descriptor.configurable = true;
30+
if ("value" in descriptor) descriptor.writable = true;
31+
Object.defineProperty(target, descriptor.key, descriptor);
32+
}
33+
}
34+
35+
return function (Constructor, protoProps, staticProps) {
36+
if (protoProps) defineProperties(Constructor.prototype, protoProps);
37+
if (staticProps) defineProperties(Constructor, staticProps);
38+
return Constructor;
39+
};
40+
})();
41+
42+
var EquationSolver = (function () {
43+
function EquationSolver(expressionTree1, expressionTree2) {
44+
var _this = this;
45+
46+
_classCallCheck(this, EquationSolver);
47+
48+
var context = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
49+
this.expressionTree1 = expressionTree1;
50+
this.expressionTree2 = expressionTree2;
51+
this.context = context;
52+
this.numUnknowns = 0;
53+
this.unknownTree = expressionTree1.getExpressionTree();
54+
this.knownTree = expressionTree2.getExpressionTree();
55+
this.knownTreeStack = [];
56+
this.unknownTreeStack = [];
57+
expressionTree1.getExpressionTree().iterate(function (constant) {
58+
if (isNaN(constant) && !context[constant]) {
59+
_this.numUnknowns += 1;
60+
}
61+
});
62+
expressionTree2.getExpressionTree().iterate(function (constant) {
63+
if (isNaN(constant) && !context[constant]) {
64+
_this.numUnknowns += 1;
65+
_this.unknownTree = _this.expressionTree2.getExpressionTree();
66+
_this.knownTree = _this.expressionTree2.getExpressionTree();
67+
}
68+
});
69+
}
70+
71+
_createClass(EquationSolver, [{
72+
key: 'evaluate',
73+
value: function evaluate() {
74+
if (this.numUnknowns === 0) {
75+
return this.expressionTree1.evaluate(this.context) === this.expressionTree2.evaluate(this.context);
76+
} else if (this.numUnknowns > 1) {
77+
throw new Error('Too many unknowns');
78+
}
79+
80+
while (!this._doStepOfAlegbra()) {
81+
this.unknownTreeStack.push(this.unknownTree.toInfix());
82+
this.knownTreeStack.push(this.knownTree.toInfix());
83+
}
84+
85+
return this.unknownTree.toInfix() + ' = ' + this.knownTree.toInfix();
86+
}
87+
}, {
88+
key: '_doStepOfAlegbra',
89+
value: function _doStepOfAlegbra() {
90+
if (this.unknownTree instanceof _ConstantNode2.default) {
91+
return true;
92+
} else if (this.unknownTree instanceof _UnaryNode2.default) {
93+
this.knownTree = _Operators.OPERATOR_INVERSE_MAP.get(this.unknownTree.operator)(this.knownTree);
94+
this.unknownTree = this.unknownTree.operand;
95+
} else if (this.unknownTree.left.isUnknown(this.context)) {
96+
this.knownTree = _Operators.OPERATOR_INVERSE_MAP.get(this.unknownTree.operator)(this.knownTree, this.unknownTree.right);
97+
this.unknownTree = this.unknownTree.left;
98+
} else {
99+
this.knownTree = _Operators.OPERATOR_INVERSE_MAP.get(this.unknownTree.operator)(this.knownTree, this.unknownTree.left);
100+
this.unknownTree = this.unknownTree.right;
101+
}
102+
103+
return false;
104+
}
105+
}]);
106+
107+
return EquationSolver;
108+
})();
109+
110+
exports.default = EquationSolver;
111+
});

dist/scripts/ExpressionNode.js

+38-3
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,44 @@ define(["exports"], function (exports) {
1111
}
1212
}
1313

14-
var ExpressionNode = function ExpressionNode() {
15-
_classCallCheck(this, ExpressionNode);
16-
};
14+
var _createClass = (function () {
15+
function defineProperties(target, props) {
16+
for (var i = 0; i < props.length; i++) {
17+
var descriptor = props[i];
18+
descriptor.enumerable = descriptor.enumerable || false;
19+
descriptor.configurable = true;
20+
if ("value" in descriptor) descriptor.writable = true;
21+
Object.defineProperty(target, descriptor.key, descriptor);
22+
}
23+
}
24+
25+
return function (Constructor, protoProps, staticProps) {
26+
if (protoProps) defineProperties(Constructor.prototype, protoProps);
27+
if (staticProps) defineProperties(Constructor, staticProps);
28+
return Constructor;
29+
};
30+
})();
31+
32+
var ExpressionNode = (function () {
33+
function ExpressionNode() {
34+
_classCallCheck(this, ExpressionNode);
35+
}
36+
37+
_createClass(ExpressionNode, [{
38+
key: "isUnknown",
39+
value: function isUnknown(context) {
40+
var isUnknown = false;
41+
this.iterate(function (constant) {
42+
if (isNaN(constant) && !context[constant]) {
43+
isUnknown = true;
44+
}
45+
});
46+
return isUnknown;
47+
}
48+
}]);
49+
50+
return ExpressionNode;
51+
})();
1752

1853
exports.default = ExpressionNode;
1954
});

dist/scripts/ExpressionTree.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,10 @@ define(['exports', 'PostfixConverter', 'BinaryNode', 'UnaryNode', 'ConstantNode'
5454
}
5555

5656
var ExpressionTree = (function () {
57-
function ExpressionTree(expressionString) {
57+
function ExpressionTree() {
5858
_classCallCheck(this, ExpressionTree);
5959

60+
var expressionString = arguments.length <= 0 || arguments[0] === undefined ? '' : arguments[0];
6061
this.postfixConverter = new _PostfixConverter2.default();
6162
var postfixStringArray = this.postfixConverter.toPostfix(expressionString);
6263
this.expressionTree = this._postfixToTree(postfixStringArray);

dist/scripts/Operators.js

+33-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,27 @@
11
'use strict';
22

3-
define(['exports'], function (exports) {
3+
define(['exports', 'BinaryNode', 'UnaryNode', 'ConstantNode'], function (exports, _BinaryNode, _UnaryNode, _ConstantNode) {
44
Object.defineProperty(exports, "__esModule", {
55
value: true
66
});
7+
exports.OPERATOR_INVERSE_MAP = exports.OPERATOR_FUNCTION_MAP = exports.PRECEDENCE = exports.PARENS = exports.UNARY_OPS = exports.BINARY_OPS = exports.UNARY_MINUS_ALT = exports.UNARY_MINUS = undefined;
8+
9+
var _BinaryNode2 = _interopRequireDefault(_BinaryNode);
10+
11+
var _UnaryNode2 = _interopRequireDefault(_UnaryNode);
12+
13+
var _ConstantNode2 = _interopRequireDefault(_ConstantNode);
14+
15+
function _interopRequireDefault(obj) {
16+
return obj && obj.__esModule ? obj : {
17+
default: obj
18+
};
19+
}
20+
721
var UNARY_MINUS = exports.UNARY_MINUS = '-';
822
var UNARY_MINUS_ALT = exports.UNARY_MINUS_ALT = '#';
923
var BINARY_OPS = exports.BINARY_OPS = new Set(['^', '*', '/', '-', '+']);
10-
var UNARY_OPS = exports.UNARY_OPS = new Set([UNARY_MINUS_ALT, 'ln']);
24+
var UNARY_OPS = exports.UNARY_OPS = new Set([UNARY_MINUS_ALT, 'ln', 'sqrt']);
1125
var PARENS = exports.PARENS = new Set(['(', ')']);
1226
var PRECEDENCE = exports.PRECEDENCE = new Map([[UNARY_MINUS_ALT, 4], ['ln', 4], ['sqrt', 4], ['^', 3], ['*', 2], ['/', 2], ['+', 1], ['-', 1]]);
1327
var OPERATOR_FUNCTION_MAP = exports.OPERATOR_FUNCTION_MAP = new Map([['^', function (a, b) {
@@ -23,4 +37,21 @@ define(['exports'], function (exports) {
2337
}], [UNARY_MINUS_ALT, function (a) {
2438
return -a;
2539
}], ['ln', Math.log], ['sqrt', Math.sqrt]]);
40+
var OPERATOR_INVERSE_MAP = exports.OPERATOR_INVERSE_MAP = new Map([['^', function (known, unknown) {
41+
return new _BinaryNode2.default('^', known, new _BinaryNode2.default('/', new _ConstantNode2.default(1), unknown));
42+
}], ['*', function (known, unknown) {
43+
return new _BinaryNode2.default('/', known, unknown);
44+
}], ['/', function (known, unknown) {
45+
return new _BinaryNode2.default('*', known, unknown);
46+
}], ['-', function (known, unknown) {
47+
return new _BinaryNode2.default('+', known, unknown);
48+
}], ['+', function (known, unknown) {
49+
return new _BinaryNode2.default('-', known, unknown);
50+
}], [UNARY_MINUS_ALT, function (known) {
51+
return new _UnaryNode2.default(UNARY_MINUS_ALT, known);
52+
}], ['ln', function (known) {
53+
return new _BinaryNode2.default('^', new _ConstantNode2.default(Math.E), known);
54+
}], ['sqrt', function (known) {
55+
return new _BinaryNode2.default('^', known, new _ConstantNode2.default(2));
56+
}]]);
2657
});

dist/scripts/UnaryNode.js

+15
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,21 @@ define(['exports', 'ExpressionNode', 'Operators'], function (exports, _Expressio
8181
value: function evaluate(context) {
8282
return _Operators.OPERATOR_FUNCTION_MAP.get(this.operator)(this.operand.evaluate(context));
8383
}
84+
}, {
85+
key: 'iterate',
86+
value: function iterate(callback) {
87+
var thisArg = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1];
88+
this.operand.iterate(callback, thisArg);
89+
}
90+
}, {
91+
key: 'toInfix',
92+
value: function toInfix() {
93+
if (this.operator === _Operators.UNARY_MINUS_ALT) {
94+
return '-' + this.operand;
95+
}
96+
97+
return this.operator + '(' + this.operand + ')';
98+
}
8499
}]);
85100

86101
return UnaryNode;

dist/scripts/index.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
'use strict';
22

3-
define(['VariableTable', 'EquationTree', 'EquationInput'], function (_VariableTable, _EquationTree, _EquationInput) {
3+
define(['VariableTable', 'EquationInput'], function (_VariableTable, _EquationInput) {
44
var _VariableTable2 = _interopRequireDefault(_VariableTable);
55

6-
var _EquationTree2 = _interopRequireDefault(_EquationTree);
7-
86
var _EquationInput2 = _interopRequireDefault(_EquationInput);
97

108
function _interopRequireDefault(obj) {

gulpfile.js

+8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ const babel = require('gulp-babel');
33
const uglify = require('gulp-uglify');
44
const concat = require('gulp-concat');
55
const sass = require('gulp-sass');
6+
const mocha = require('gulp-mocha');
7+
const babelRegister = require('babel/register');
68

79
gulp.task('default', ['js', 'sass']);
810

@@ -25,3 +27,9 @@ gulp.task('watch', () => {
2527
gulp.watch('styles/*.scss', ['sass']);
2628
gulp.watch('scripts/*.js', ['js']);
2729
});
30+
31+
gulp.task('test', () => {
32+
return gulp.src('tests/*.js')
33+
.pipe(mocha({}));
34+
});
35+

0 commit comments

Comments
 (0)