@khanacademy/kas 0.2.7 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +1 -0
- package/CHANGELOG.md +6 -0
- package/dist/es/index.js +387 -933
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +385 -931
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/{checking-form_test.js → checking-form.test.ts} +17 -4
- package/src/__tests__/{comparing_test.js → comparing.test.ts} +21 -10
- package/src/__tests__/{compilation_test.js → compilation.test.ts} +18 -5
- package/src/__tests__/{evaluating_test.js → evaluating.test.ts} +22 -6
- package/src/__tests__/{index_test.js → index.test.ts} +28 -16
- package/src/__tests__/{parsing_test.js → parsing.test.ts} +27 -9
- package/src/__tests__/{rendering_test.js → rendering.test.ts} +21 -8
- package/src/__tests__/{transforming_test.js → transforming.test.ts} +37 -18
- package/src/__tests__/{units_test.js → units.test.ts} +37 -16
- package/src/index.js +2 -2
- package/src/nodes.js +1182 -763
- package/src/parser-generator.js +166 -142
- package/tsconfig.json +9 -0
- package/dist/index.d.ts +0 -2
- package/dist/index.js.flow +0 -2
package/src/nodes.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/* eslint-disable prettier/prettier */
|
|
2
2
|
/* eslint-disable import/order */
|
|
3
3
|
/* TODO(charlie): fix these lint errors (http://eslint.org/docs/rules): */
|
|
4
|
-
/* eslint-disable indent, no-undef, no-var, one-var, no-dupe-keys, no-new-func, no-redeclare, no-unused-vars, comma-dangle, max-len, prefer-spread, space-infix-ops, space-unary-ops */
|
|
4
|
+
/* eslint-disable indent, no-undef, no-var, one-var, no-dupe-keys, no-new-func, no-redeclare, @typescript-eslint/no-unused-vars, comma-dangle, max-len, prefer-spread, space-infix-ops, space-unary-ops */
|
|
5
5
|
import _ from "underscore";
|
|
6
6
|
|
|
7
|
-
import {unitParser} from "./__genfiles__/unitparser
|
|
8
|
-
import {parser} from "./__genfiles__/parser
|
|
7
|
+
import {unitParser} from "./__genfiles__/unitparser";
|
|
8
|
+
import {parser} from "./__genfiles__/parser";
|
|
9
9
|
|
|
10
10
|
/* The node hierarcy is as follows:
|
|
11
11
|
|
|
@@ -39,22 +39,28 @@ import {parser} from "./__genfiles__/parser.js";
|
|
|
39
39
|
/* non user-facing functions */
|
|
40
40
|
|
|
41
41
|
// assert that all abstract methods have been overridden
|
|
42
|
-
var abstract = function() {
|
|
42
|
+
var abstract = function () {
|
|
43
43
|
// Try to give people a bit of information when this happens
|
|
44
|
-
throw new Error(
|
|
44
|
+
throw new Error(
|
|
45
|
+
"Abstract method - must override for expr: " +
|
|
45
46
|
// eslint-disable-next-line @babel/no-invalid-this
|
|
46
|
-
this.print()
|
|
47
|
+
this.print(),
|
|
48
|
+
);
|
|
47
49
|
};
|
|
48
50
|
|
|
49
51
|
// throw an error that is meant to be caught by the test suite (not user facing)
|
|
50
|
-
var error = function(message) {
|
|
52
|
+
var error = function (message) {
|
|
53
|
+
throw new Error(message);
|
|
54
|
+
};
|
|
51
55
|
|
|
52
56
|
// reliably detect NaN
|
|
53
|
-
var isNaN = function(object) {
|
|
57
|
+
var isNaN = function (object) {
|
|
58
|
+
return object !== object;
|
|
59
|
+
};
|
|
54
60
|
|
|
55
61
|
// return a random float between min (inclusive) and max (exclusive),
|
|
56
62
|
// not that inclusivity means much, probabilistically, on floats
|
|
57
|
-
var randomFloat = function(min, max) {
|
|
63
|
+
var randomFloat = function (min, max) {
|
|
58
64
|
var extent = max - min;
|
|
59
65
|
return Math.random() * extent + min;
|
|
60
66
|
};
|
|
@@ -63,12 +69,10 @@ var randomFloat = function(min, max) {
|
|
|
63
69
|
var ITERATIONS = 12;
|
|
64
70
|
var TOLERANCE = 9; // decimal places
|
|
65
71
|
|
|
66
|
-
|
|
67
72
|
/* abstract base expression node */
|
|
68
73
|
function Expr() {}
|
|
69
74
|
|
|
70
75
|
_.extend(Expr.prototype, {
|
|
71
|
-
|
|
72
76
|
// this node's immediate constructor
|
|
73
77
|
func: abstract,
|
|
74
78
|
|
|
@@ -76,16 +80,16 @@ _.extend(Expr.prototype, {
|
|
|
76
80
|
args: abstract,
|
|
77
81
|
|
|
78
82
|
// make a new node with the given arguments
|
|
79
|
-
construct: function(args) {
|
|
83
|
+
construct: function (args) {
|
|
80
84
|
var instance = new this.func();
|
|
81
85
|
this.func.apply(instance, args);
|
|
82
86
|
return instance;
|
|
83
87
|
},
|
|
84
88
|
|
|
85
89
|
// an abstraction for chainable, bottom-up recursion
|
|
86
|
-
recurse: function(method) {
|
|
90
|
+
recurse: function (method) {
|
|
87
91
|
var passed = Array.prototype.slice.call(arguments, 1);
|
|
88
|
-
var args = _.map(this.args(), function(arg) {
|
|
92
|
+
var args = _.map(this.args(), function (arg) {
|
|
89
93
|
return _.isString(arg) ? arg : arg[method].apply(arg, passed);
|
|
90
94
|
});
|
|
91
95
|
return this.construct(args);
|
|
@@ -96,7 +100,7 @@ _.extend(Expr.prototype, {
|
|
|
96
100
|
|
|
97
101
|
codegen: abstract,
|
|
98
102
|
|
|
99
|
-
compile: function() {
|
|
103
|
+
compile: function () {
|
|
100
104
|
var code = this.codegen();
|
|
101
105
|
try {
|
|
102
106
|
return new Function("vars", "return " + code + ";");
|
|
@@ -114,13 +118,12 @@ _.extend(Expr.prototype, {
|
|
|
114
118
|
tex: abstract,
|
|
115
119
|
|
|
116
120
|
// returns a TeX string, modified by the given options
|
|
117
|
-
asTex: function(options) {
|
|
118
|
-
|
|
121
|
+
asTex: function (options) {
|
|
119
122
|
options = options || {};
|
|
120
123
|
_.defaults(options, {
|
|
121
124
|
display: true,
|
|
122
125
|
dynamic: true,
|
|
123
|
-
times: false
|
|
126
|
+
times: false,
|
|
124
127
|
});
|
|
125
128
|
|
|
126
129
|
var tex = this.tex();
|
|
@@ -141,7 +144,7 @@ _.extend(Expr.prototype, {
|
|
|
141
144
|
|
|
142
145
|
// returns the name of this expression's constructor as a string
|
|
143
146
|
// only used for testing and debugging (the ugly regex is for IE8)
|
|
144
|
-
name: function() {
|
|
147
|
+
name: function () {
|
|
145
148
|
if (this.func.name) {
|
|
146
149
|
return this.func.name;
|
|
147
150
|
} else {
|
|
@@ -150,37 +153,55 @@ _.extend(Expr.prototype, {
|
|
|
150
153
|
},
|
|
151
154
|
|
|
152
155
|
// returns a string representing current node structure
|
|
153
|
-
repr: function() {
|
|
154
|
-
return
|
|
155
|
-
|
|
156
|
-
|
|
156
|
+
repr: function () {
|
|
157
|
+
return (
|
|
158
|
+
this.name() +
|
|
159
|
+
"(" +
|
|
160
|
+
_.map(this.args(), function (arg) {
|
|
161
|
+
return _.isString(arg) ? arg : arg.repr();
|
|
162
|
+
}).join(",") +
|
|
163
|
+
")"
|
|
164
|
+
);
|
|
157
165
|
},
|
|
158
166
|
|
|
159
167
|
// removes all negative signs
|
|
160
|
-
strip: function() {
|
|
168
|
+
strip: function () {
|
|
169
|
+
return this.recurse("strip");
|
|
170
|
+
},
|
|
161
171
|
|
|
162
172
|
// canonically reorders all commutative elements
|
|
163
|
-
normalize: function() {
|
|
173
|
+
normalize: function () {
|
|
174
|
+
return this.recurse("normalize");
|
|
175
|
+
},
|
|
164
176
|
|
|
165
177
|
// expands the expression
|
|
166
|
-
expand: function() {
|
|
178
|
+
expand: function () {
|
|
179
|
+
return this.recurse("expand");
|
|
180
|
+
},
|
|
167
181
|
|
|
168
182
|
// naively factors out like terms
|
|
169
|
-
factor: function(options) {
|
|
183
|
+
factor: function (options) {
|
|
184
|
+
return this.recurse("factor", options);
|
|
185
|
+
},
|
|
170
186
|
|
|
171
187
|
// collect all like terms
|
|
172
|
-
collect: function(options) {
|
|
188
|
+
collect: function (options) {
|
|
189
|
+
return this.recurse("collect", options);
|
|
190
|
+
},
|
|
173
191
|
|
|
174
192
|
// strict syntactic equality check
|
|
175
|
-
equals: function(other) {
|
|
193
|
+
equals: function (other) {
|
|
176
194
|
return this.normalize().print() === other.normalize().print();
|
|
177
195
|
},
|
|
178
196
|
|
|
179
197
|
// expand and collect until the expression no longer changes
|
|
180
|
-
simplify: function(options) {
|
|
181
|
-
options = _.extend(
|
|
182
|
-
|
|
183
|
-
|
|
198
|
+
simplify: function (options) {
|
|
199
|
+
options = _.extend(
|
|
200
|
+
{
|
|
201
|
+
once: false,
|
|
202
|
+
},
|
|
203
|
+
options,
|
|
204
|
+
);
|
|
184
205
|
|
|
185
206
|
// Attempt to factor and collect
|
|
186
207
|
var step1 = this.factor(options);
|
|
@@ -211,72 +232,82 @@ _.extend(Expr.prototype, {
|
|
|
211
232
|
},
|
|
212
233
|
|
|
213
234
|
// check whether this expression is simplified
|
|
214
|
-
isSimplified: function() {
|
|
235
|
+
isSimplified: function () {
|
|
215
236
|
return this.equals(this.simplify());
|
|
216
237
|
},
|
|
217
238
|
|
|
218
239
|
// return the child nodes of this node
|
|
219
|
-
exprArgs: function() {
|
|
220
|
-
return _.filter(this.args(), function(arg) {
|
|
240
|
+
exprArgs: function () {
|
|
241
|
+
return _.filter(this.args(), function (arg) {
|
|
221
242
|
return arg instanceof Expr;
|
|
222
243
|
});
|
|
223
244
|
},
|
|
224
245
|
|
|
225
246
|
// return the variables (function and non) within the expression
|
|
226
|
-
getVars: function(excludeFunc) {
|
|
227
|
-
return _.uniq(
|
|
247
|
+
getVars: function (excludeFunc) {
|
|
248
|
+
return _.uniq(
|
|
249
|
+
_.flatten(_.invoke(this.exprArgs(), "getVars", excludeFunc)),
|
|
250
|
+
).sort();
|
|
228
251
|
},
|
|
229
252
|
|
|
230
|
-
getConsts: function() {
|
|
253
|
+
getConsts: function () {
|
|
231
254
|
return _.uniq(_.flatten(_.invoke(this.exprArgs(), "getConsts"))).sort();
|
|
232
255
|
},
|
|
233
256
|
|
|
234
|
-
getUnits: function() {
|
|
257
|
+
getUnits: function () {
|
|
235
258
|
return _.flatten(_.invoke(this.exprArgs(), "getUnits"));
|
|
236
259
|
},
|
|
237
260
|
|
|
238
261
|
// check whether this expression node is of a particular type
|
|
239
|
-
is: function(func) {
|
|
262
|
+
is: function (func) {
|
|
240
263
|
return this instanceof func;
|
|
241
264
|
},
|
|
242
265
|
|
|
243
266
|
// check whether this expression has a particular node type
|
|
244
|
-
has: function(func) {
|
|
267
|
+
has: function (func) {
|
|
245
268
|
if (this instanceof func) {
|
|
246
269
|
return true;
|
|
247
270
|
}
|
|
248
|
-
return _.any(this.exprArgs(), function(arg) {
|
|
271
|
+
return _.any(this.exprArgs(), function (arg) {
|
|
272
|
+
return arg.has(func);
|
|
273
|
+
});
|
|
249
274
|
},
|
|
250
275
|
|
|
251
276
|
// raise this expression to a given exponent
|
|
252
277
|
// most useful for eventually implementing i^3 = -i, etc.
|
|
253
|
-
raiseToThe: function(exp) {
|
|
278
|
+
raiseToThe: function (exp) {
|
|
254
279
|
return new Pow(this, exp);
|
|
255
280
|
},
|
|
256
281
|
|
|
257
282
|
// does this expression have a specific rendering hint?
|
|
258
283
|
// rendering hints are picked up while parsing, but are lost during transformations
|
|
259
|
-
isSubtract: function() {
|
|
260
|
-
|
|
261
|
-
|
|
284
|
+
isSubtract: function () {
|
|
285
|
+
return false;
|
|
286
|
+
},
|
|
287
|
+
isDivide: function () {
|
|
288
|
+
return false;
|
|
289
|
+
},
|
|
290
|
+
isRoot: function () {
|
|
291
|
+
return false;
|
|
292
|
+
},
|
|
262
293
|
|
|
263
294
|
// whether this node needs an explicit multiplication sign if following a Num
|
|
264
|
-
needsExplicitMul: function() {
|
|
295
|
+
needsExplicitMul: function () {
|
|
265
296
|
return this.args()[0].needsExplicitMul();
|
|
266
297
|
},
|
|
267
298
|
|
|
268
299
|
// check that the variables in both expressions are the same
|
|
269
|
-
sameVars: function(other) {
|
|
300
|
+
sameVars: function (other) {
|
|
270
301
|
var vars1 = this.getVars();
|
|
271
302
|
var vars2 = other.getVars();
|
|
272
303
|
|
|
273
304
|
// the other Expr can have more variables than this one
|
|
274
305
|
// this lets you multiply equations by other variables
|
|
275
|
-
var same = function(array1, array2) {
|
|
306
|
+
var same = function (array1, array2) {
|
|
276
307
|
return !_.difference(array1, array2).length;
|
|
277
308
|
};
|
|
278
309
|
|
|
279
|
-
var lower = function(array) {
|
|
310
|
+
var lower = function (array) {
|
|
280
311
|
return _.uniq(_.invoke(array, "toLowerCase")).sort();
|
|
281
312
|
};
|
|
282
313
|
|
|
@@ -289,7 +320,7 @@ _.extend(Expr.prototype, {
|
|
|
289
320
|
// semantic equality check, call after sameVars() to avoid potential false positives
|
|
290
321
|
// plug in random numbers for the variables in both expressions
|
|
291
322
|
// if they both consistently evaluate the same, then they're the same
|
|
292
|
-
compare: function(other) {
|
|
323
|
+
compare: function (other) {
|
|
293
324
|
// equation comparisons are handled by Eq.compare()
|
|
294
325
|
if (other instanceof Eq) {
|
|
295
326
|
return false;
|
|
@@ -297,12 +328,13 @@ _.extend(Expr.prototype, {
|
|
|
297
328
|
|
|
298
329
|
var varList = _.union(
|
|
299
330
|
this.getVars(/* excludeFunc */ true),
|
|
300
|
-
other.getVars(/* excludeFunc */ true)
|
|
331
|
+
other.getVars(/* excludeFunc */ true),
|
|
332
|
+
);
|
|
301
333
|
|
|
302
334
|
// If the numbers are large we would like to do a relative comparison
|
|
303
335
|
// rather than an absolute one, but if they're small enough then an
|
|
304
336
|
// absolute comparison makes more sense
|
|
305
|
-
var getDelta = function(num1, num2) {
|
|
337
|
+
var getDelta = function (num1, num2) {
|
|
306
338
|
if (Math.abs(num1) < 1 || Math.abs(num2) < 1) {
|
|
307
339
|
return Math.abs(num1 - num2);
|
|
308
340
|
} else {
|
|
@@ -310,11 +342,13 @@ _.extend(Expr.prototype, {
|
|
|
310
342
|
}
|
|
311
343
|
};
|
|
312
344
|
|
|
313
|
-
var equalNumbers = function(num1, num2) {
|
|
345
|
+
var equalNumbers = function (num1, num2) {
|
|
314
346
|
var delta = getDelta(num1, num2);
|
|
315
|
-
return (
|
|
316
|
-
|
|
317
|
-
|
|
347
|
+
return (
|
|
348
|
+
num1 === num2 /* needed if either is +/- Infinity */ ||
|
|
349
|
+
(isNaN(num1) && isNaN(num2)) ||
|
|
350
|
+
delta < Math.pow(10, -TOLERANCE)
|
|
351
|
+
);
|
|
318
352
|
};
|
|
319
353
|
|
|
320
354
|
// if no variables, only need to evaluate once
|
|
@@ -345,11 +379,10 @@ _.extend(Expr.prototype, {
|
|
|
345
379
|
// Note that because there are 12 iterations and three ranges, each
|
|
346
380
|
// range is checked four times.
|
|
347
381
|
for (var i = 0; i < ITERATIONS; i++) {
|
|
348
|
-
|
|
349
382
|
var vars = {};
|
|
350
383
|
|
|
351
384
|
// One third total iterations each with range 10, 100, and 1000
|
|
352
|
-
var range = Math.pow(10, 1 + Math.floor(3 * i / ITERATIONS));
|
|
385
|
+
var range = Math.pow(10, 1 + Math.floor((3 * i) / ITERATIONS));
|
|
353
386
|
|
|
354
387
|
// Half of the iterations should only use integer values.
|
|
355
388
|
// This is because expressions like (-2)^x are common but result
|
|
@@ -367,15 +400,19 @@ _.extend(Expr.prototype, {
|
|
|
367
400
|
// TODO(alex): Add support for complex numbers, then remove this.
|
|
368
401
|
var useFloats = i % 2 === 0;
|
|
369
402
|
|
|
370
|
-
_.each(varList, function(v) {
|
|
371
|
-
vars[v] = useFloats
|
|
372
|
-
|
|
403
|
+
_.each(varList, function (v) {
|
|
404
|
+
vars[v] = useFloats
|
|
405
|
+
? randomFloat(-range, range)
|
|
406
|
+
: _.random(-range, range);
|
|
373
407
|
});
|
|
374
408
|
|
|
375
409
|
var equal;
|
|
376
|
-
if (
|
|
377
|
-
|
|
378
|
-
|
|
410
|
+
if (
|
|
411
|
+
expr1.has(Func) ||
|
|
412
|
+
expr2.has(Func) ||
|
|
413
|
+
expr1.has(Unit) ||
|
|
414
|
+
expr2.has(Unit)
|
|
415
|
+
) {
|
|
379
416
|
var result1 = expr1.partialEval(vars);
|
|
380
417
|
var result2 = expr2.partialEval(vars);
|
|
381
418
|
|
|
@@ -396,7 +433,7 @@ _.extend(Expr.prototype, {
|
|
|
396
433
|
},
|
|
397
434
|
|
|
398
435
|
// evaluate as much of the expression as possible
|
|
399
|
-
partialEval: function(vars) {
|
|
436
|
+
partialEval: function (vars) {
|
|
400
437
|
if (this instanceof Unit) {
|
|
401
438
|
return this;
|
|
402
439
|
} else if (!this.has(Func)) {
|
|
@@ -412,22 +449,22 @@ _.extend(Expr.prototype, {
|
|
|
412
449
|
// all negative signs are stripped and the expressions are converted to
|
|
413
450
|
// a canonical commutative form
|
|
414
451
|
// should only be done after compare() returns true to avoid false positives
|
|
415
|
-
sameForm: function(other) {
|
|
452
|
+
sameForm: function (other) {
|
|
416
453
|
return this.strip().equals(other.strip());
|
|
417
454
|
},
|
|
418
455
|
|
|
419
456
|
// returns the GCD of this expression and the given factor
|
|
420
|
-
findGCD: function(factor) {
|
|
457
|
+
findGCD: function (factor) {
|
|
421
458
|
return this.equals(factor) ? factor : Num.One;
|
|
422
459
|
},
|
|
423
460
|
|
|
424
461
|
// return this expression's denominator
|
|
425
|
-
getDenominator: function() {
|
|
462
|
+
getDenominator: function () {
|
|
426
463
|
return Num.One;
|
|
427
464
|
},
|
|
428
465
|
|
|
429
466
|
// return this expression as a Mul
|
|
430
|
-
asMul: function() {
|
|
467
|
+
asMul: function () {
|
|
431
468
|
return new Mul(Num.One, this);
|
|
432
469
|
},
|
|
433
470
|
|
|
@@ -437,15 +474,17 @@ _.extend(Expr.prototype, {
|
|
|
437
474
|
|
|
438
475
|
// TODO(alex): rename to hasNegativeSign or similar?
|
|
439
476
|
// return whether this expression has a negative sign
|
|
440
|
-
isNegative: function() {
|
|
477
|
+
isNegative: function () {
|
|
478
|
+
return false;
|
|
479
|
+
},
|
|
441
480
|
|
|
442
481
|
// return a factor of this expression that is 100% positive
|
|
443
|
-
asPositiveFactor: function() {
|
|
482
|
+
asPositiveFactor: function () {
|
|
444
483
|
return this.isPositive() ? this : Num.One;
|
|
445
484
|
},
|
|
446
485
|
|
|
447
486
|
// return a copy of the expression with a new hint set (preserves hints)
|
|
448
|
-
addHint: function(hint) {
|
|
487
|
+
addHint: function (hint) {
|
|
449
488
|
if (!hint) {
|
|
450
489
|
return this;
|
|
451
490
|
}
|
|
@@ -457,46 +496,54 @@ _.extend(Expr.prototype, {
|
|
|
457
496
|
},
|
|
458
497
|
|
|
459
498
|
hints: {
|
|
460
|
-
parens: false
|
|
499
|
+
parens: false,
|
|
461
500
|
},
|
|
462
501
|
|
|
463
502
|
// currently unused!
|
|
464
|
-
asExpr: function() {
|
|
503
|
+
asExpr: function () {
|
|
504
|
+
return this;
|
|
505
|
+
},
|
|
465
506
|
|
|
466
507
|
// complete parse by performing a few necessary transformations
|
|
467
|
-
completeParse: function() {
|
|
508
|
+
completeParse: function () {
|
|
509
|
+
return this.recurse("completeParse");
|
|
510
|
+
},
|
|
468
511
|
|
|
469
512
|
abs: abstract,
|
|
470
513
|
|
|
471
|
-
negate: function() {
|
|
514
|
+
negate: function () {
|
|
472
515
|
return new Mul(Num.Neg, this);
|
|
473
|
-
}
|
|
516
|
+
},
|
|
474
517
|
});
|
|
475
518
|
|
|
476
|
-
|
|
477
519
|
/* abstract sequence node */
|
|
478
520
|
function Seq() {}
|
|
479
521
|
Seq.prototype = new Expr();
|
|
480
522
|
|
|
481
523
|
_.extend(Seq.prototype, {
|
|
482
|
-
args: function() {
|
|
524
|
+
args: function () {
|
|
525
|
+
return this.terms;
|
|
526
|
+
},
|
|
483
527
|
|
|
484
|
-
normalize: function() {
|
|
485
|
-
var terms = _.sortBy(
|
|
486
|
-
|
|
487
|
-
|
|
528
|
+
normalize: function () {
|
|
529
|
+
var terms = _.sortBy(
|
|
530
|
+
_.invoke(this.terms, "normalize"),
|
|
531
|
+
function (term) {
|
|
532
|
+
return term.print();
|
|
533
|
+
},
|
|
534
|
+
);
|
|
488
535
|
|
|
489
536
|
return new this.func(terms);
|
|
490
537
|
},
|
|
491
538
|
|
|
492
|
-
expand: function() {
|
|
539
|
+
expand: function () {
|
|
493
540
|
return this.recurse("expand").flatten();
|
|
494
541
|
},
|
|
495
542
|
|
|
496
543
|
// partition the sequence into its numeric and non-numeric parts
|
|
497
544
|
// makes no guarantees about the validity of either part!
|
|
498
|
-
partition: function() {
|
|
499
|
-
var terms = _.groupBy(this.terms, function(term) {
|
|
545
|
+
partition: function () {
|
|
546
|
+
var terms = _.groupBy(this.terms, function (term) {
|
|
500
547
|
return term instanceof Num;
|
|
501
548
|
});
|
|
502
549
|
|
|
@@ -511,9 +558,9 @@ _.extend(Seq.prototype, {
|
|
|
511
558
|
|
|
512
559
|
// ensure that sequences have 2+ terms and no nested sequences of the same type
|
|
513
560
|
// this is a shallow flattening and will return a non-Seq if terms.length <= 1
|
|
514
|
-
flatten: function() {
|
|
561
|
+
flatten: function () {
|
|
515
562
|
var type = this;
|
|
516
|
-
var terms = _.reject(this.terms, function(term) {
|
|
563
|
+
var terms = _.reject(this.terms, function (term) {
|
|
517
564
|
return term.equals(type.identity);
|
|
518
565
|
});
|
|
519
566
|
|
|
@@ -524,7 +571,7 @@ _.extend(Seq.prototype, {
|
|
|
524
571
|
return terms[0];
|
|
525
572
|
}
|
|
526
573
|
|
|
527
|
-
var grouped = _.groupBy(terms, function(term) {
|
|
574
|
+
var grouped = _.groupBy(terms, function (term) {
|
|
528
575
|
return term instanceof type.func;
|
|
529
576
|
});
|
|
530
577
|
|
|
@@ -532,7 +579,9 @@ _.extend(Seq.prototype, {
|
|
|
532
579
|
var same = grouped[true] || [];
|
|
533
580
|
var others = grouped[false] || [];
|
|
534
581
|
|
|
535
|
-
var flattened = others.concat(
|
|
582
|
+
var flattened = others.concat(
|
|
583
|
+
_.flatten(_.pluck(same, "terms"), /* shallow: */ true),
|
|
584
|
+
);
|
|
536
585
|
return new type.func(flattened);
|
|
537
586
|
},
|
|
538
587
|
|
|
@@ -542,7 +591,7 @@ _.extend(Seq.prototype, {
|
|
|
542
591
|
// reduce a numeric sequence to a Num
|
|
543
592
|
reduce: abstract,
|
|
544
593
|
|
|
545
|
-
isPositive: function() {
|
|
594
|
+
isPositive: function () {
|
|
546
595
|
var terms = _.invoke(this.terms, "collect");
|
|
547
596
|
return _.all(_.invoke(terms, "isPositive"));
|
|
548
597
|
},
|
|
@@ -550,7 +599,7 @@ _.extend(Seq.prototype, {
|
|
|
550
599
|
// return a new Seq with a given term replaced by a different term
|
|
551
600
|
// (or array of terms). given term can be passed directly, or by index
|
|
552
601
|
// if no new term is provided, the old one is simply removed
|
|
553
|
-
replace: function(oldTerm, newTerm) {
|
|
602
|
+
replace: function (oldTerm, newTerm) {
|
|
554
603
|
var index;
|
|
555
604
|
|
|
556
605
|
if (oldTerm instanceof Expr) {
|
|
@@ -566,25 +615,25 @@ _.extend(Seq.prototype, {
|
|
|
566
615
|
newTerms = [newTerm];
|
|
567
616
|
}
|
|
568
617
|
|
|
569
|
-
var terms = this.terms
|
|
570
|
-
|
|
571
|
-
|
|
618
|
+
var terms = this.terms
|
|
619
|
+
.slice(0, index)
|
|
620
|
+
.concat(newTerms)
|
|
621
|
+
.concat(this.terms.slice(index + 1));
|
|
572
622
|
|
|
573
623
|
return new this.func(terms);
|
|
574
624
|
},
|
|
575
625
|
|
|
576
626
|
// syntactic sugar for replace()
|
|
577
|
-
remove: function(term) {
|
|
627
|
+
remove: function (term) {
|
|
578
628
|
return this.replace(term);
|
|
579
629
|
},
|
|
580
630
|
|
|
581
|
-
getDenominator: function() {
|
|
631
|
+
getDenominator: function () {
|
|
582
632
|
// TODO(alex): find and return LCM
|
|
583
633
|
return new Mul(_.invoke(this.terms, "getDenominator")).flatten();
|
|
584
|
-
}
|
|
634
|
+
},
|
|
585
635
|
});
|
|
586
636
|
|
|
587
|
-
|
|
588
637
|
/* sequence of additive terms */
|
|
589
638
|
export function Add() {
|
|
590
639
|
if (arguments.length === 1) {
|
|
@@ -598,24 +647,32 @@ Add.prototype = new Seq();
|
|
|
598
647
|
_.extend(Add.prototype, {
|
|
599
648
|
func: Add,
|
|
600
649
|
|
|
601
|
-
eval: function(vars, options) {
|
|
602
|
-
return _.reduce(
|
|
650
|
+
eval: function (vars, options) {
|
|
651
|
+
return _.reduce(
|
|
652
|
+
this.terms,
|
|
653
|
+
function (memo, term) {
|
|
654
|
+
return memo + term.eval(vars, options);
|
|
655
|
+
},
|
|
656
|
+
0,
|
|
657
|
+
);
|
|
603
658
|
},
|
|
604
659
|
|
|
605
|
-
codegen: function() {
|
|
606
|
-
return
|
|
607
|
-
|
|
608
|
-
|
|
660
|
+
codegen: function () {
|
|
661
|
+
return (
|
|
662
|
+
_.map(this.terms, function (term) {
|
|
663
|
+
return "(" + term.codegen() + ")";
|
|
664
|
+
}).join(" + ") || "0"
|
|
665
|
+
);
|
|
609
666
|
},
|
|
610
667
|
|
|
611
|
-
print: function() {
|
|
668
|
+
print: function () {
|
|
612
669
|
return _.invoke(this.terms, "print").join("+");
|
|
613
670
|
},
|
|
614
671
|
|
|
615
|
-
tex: function() {
|
|
672
|
+
tex: function () {
|
|
616
673
|
var tex = "";
|
|
617
674
|
|
|
618
|
-
_.each(this.terms, function(term) {
|
|
675
|
+
_.each(this.terms, function (term) {
|
|
619
676
|
if (!tex || term.isSubtract()) {
|
|
620
677
|
tex += term.tex();
|
|
621
678
|
} else {
|
|
@@ -626,13 +683,13 @@ _.extend(Add.prototype, {
|
|
|
626
683
|
return tex;
|
|
627
684
|
},
|
|
628
685
|
|
|
629
|
-
collect: function(options) {
|
|
686
|
+
collect: function (options) {
|
|
630
687
|
var terms = _.invoke(this.terms, "collect", options);
|
|
631
688
|
|
|
632
689
|
// [Expr expr, Num coefficient]
|
|
633
690
|
var pairs = [];
|
|
634
691
|
|
|
635
|
-
_.each(terms, function(term) {
|
|
692
|
+
_.each(terms, function (term) {
|
|
636
693
|
if (term instanceof Mul) {
|
|
637
694
|
var muls = term.partition();
|
|
638
695
|
pairs.push([muls[1].flatten(), muls[0].reduce(options)]);
|
|
@@ -644,16 +701,18 @@ _.extend(Add.prototype, {
|
|
|
644
701
|
});
|
|
645
702
|
|
|
646
703
|
// { (Expr expr).print(): [[Expr expr, Num coefficient]] }
|
|
647
|
-
var grouped = _.groupBy(pairs, function(pair) {
|
|
704
|
+
var grouped = _.groupBy(pairs, function (pair) {
|
|
648
705
|
return pair[0].normalize().print();
|
|
649
706
|
});
|
|
650
707
|
|
|
651
|
-
var collected = _.compact(
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
708
|
+
var collected = _.compact(
|
|
709
|
+
_.map(grouped, function (pairs) {
|
|
710
|
+
var expr = pairs[0][0];
|
|
711
|
+
var sum = new Add(_.zip.apply(_, pairs)[1]);
|
|
712
|
+
var coefficient = sum.reduce(options);
|
|
713
|
+
return new Mul(coefficient, expr).collect(options);
|
|
714
|
+
}),
|
|
715
|
+
);
|
|
657
716
|
|
|
658
717
|
// TODO(alex): use the Pythagorean identity here
|
|
659
718
|
// e.g. x*sin^2(y) + x*cos^2(y) -> x
|
|
@@ -663,10 +722,13 @@ _.extend(Add.prototype, {
|
|
|
663
722
|
|
|
664
723
|
// naively factor out anything that is common to all terms
|
|
665
724
|
// if options.keepNegative is specified, won't factor out a common -1
|
|
666
|
-
factor: function(options) {
|
|
667
|
-
options = _.extend(
|
|
668
|
-
|
|
669
|
-
|
|
725
|
+
factor: function (options) {
|
|
726
|
+
options = _.extend(
|
|
727
|
+
{
|
|
728
|
+
keepNegative: false,
|
|
729
|
+
},
|
|
730
|
+
options,
|
|
731
|
+
);
|
|
670
732
|
|
|
671
733
|
var terms = _.invoke(this.terms, "collect");
|
|
672
734
|
var factors;
|
|
@@ -677,8 +739,8 @@ _.extend(Add.prototype, {
|
|
|
677
739
|
factors = [terms[0]];
|
|
678
740
|
}
|
|
679
741
|
|
|
680
|
-
_.each(_.rest(this.terms), function(term) {
|
|
681
|
-
factors = _.map(factors, function(factor) {
|
|
742
|
+
_.each(_.rest(this.terms), function (term) {
|
|
743
|
+
factors = _.map(factors, function (factor) {
|
|
682
744
|
return term.findGCD(factor);
|
|
683
745
|
});
|
|
684
746
|
});
|
|
@@ -689,7 +751,7 @@ _.extend(Add.prototype, {
|
|
|
689
751
|
|
|
690
752
|
factors = new Mul(factors).flatten().collect();
|
|
691
753
|
|
|
692
|
-
var remainder = _.map(terms, function(term) {
|
|
754
|
+
var remainder = _.map(terms, function (term) {
|
|
693
755
|
return Mul.handleDivide(term, factors).simplify();
|
|
694
756
|
});
|
|
695
757
|
remainder = new Add(remainder).flatten();
|
|
@@ -697,25 +759,30 @@ _.extend(Add.prototype, {
|
|
|
697
759
|
return Mul.createOrAppend(factors, remainder).flatten();
|
|
698
760
|
},
|
|
699
761
|
|
|
700
|
-
reduce: function(options) {
|
|
701
|
-
return _.reduce(
|
|
702
|
-
|
|
703
|
-
|
|
762
|
+
reduce: function (options) {
|
|
763
|
+
return _.reduce(
|
|
764
|
+
this.terms,
|
|
765
|
+
function (memo, term) {
|
|
766
|
+
return memo.add(term, options);
|
|
767
|
+
},
|
|
768
|
+
this.identity,
|
|
769
|
+
);
|
|
704
770
|
},
|
|
705
771
|
|
|
706
|
-
needsExplicitMul: function() {
|
|
772
|
+
needsExplicitMul: function () {
|
|
773
|
+
return false;
|
|
774
|
+
},
|
|
707
775
|
|
|
708
|
-
isNegative: function() {
|
|
776
|
+
isNegative: function () {
|
|
709
777
|
var terms = _.invoke(this.terms, "collect");
|
|
710
778
|
return _.all(_.invoke(terms, "isNegative"));
|
|
711
779
|
},
|
|
712
780
|
|
|
713
|
-
negate: function() {
|
|
781
|
+
negate: function () {
|
|
714
782
|
return new Add(_.invoke(this.terms, "negate"));
|
|
715
|
-
}
|
|
783
|
+
},
|
|
716
784
|
});
|
|
717
785
|
|
|
718
|
-
|
|
719
786
|
/* sequence of multiplicative terms */
|
|
720
787
|
export function Mul() {
|
|
721
788
|
if (arguments.length === 1) {
|
|
@@ -729,26 +796,36 @@ Mul.prototype = new Seq();
|
|
|
729
796
|
_.extend(Mul.prototype, {
|
|
730
797
|
func: Mul,
|
|
731
798
|
|
|
732
|
-
eval: function(vars, options) {
|
|
733
|
-
return _.reduce(
|
|
799
|
+
eval: function (vars, options) {
|
|
800
|
+
return _.reduce(
|
|
801
|
+
this.terms,
|
|
802
|
+
function (memo, term) {
|
|
803
|
+
return memo * term.eval(vars, options);
|
|
804
|
+
},
|
|
805
|
+
1,
|
|
806
|
+
);
|
|
734
807
|
},
|
|
735
808
|
|
|
736
|
-
codegen: function() {
|
|
737
|
-
return
|
|
738
|
-
|
|
739
|
-
|
|
809
|
+
codegen: function () {
|
|
810
|
+
return (
|
|
811
|
+
_.map(this.terms, function (term) {
|
|
812
|
+
return "(" + term.codegen() + ")";
|
|
813
|
+
}).join(" * ") || "0"
|
|
814
|
+
);
|
|
740
815
|
},
|
|
741
816
|
|
|
742
|
-
print: function() {
|
|
743
|
-
return _.map(this.terms, function(term) {
|
|
744
|
-
return
|
|
817
|
+
print: function () {
|
|
818
|
+
return _.map(this.terms, function (term) {
|
|
819
|
+
return term instanceof Add
|
|
820
|
+
? "(" + term.print() + ")"
|
|
821
|
+
: term.print();
|
|
745
822
|
}).join("*");
|
|
746
823
|
},
|
|
747
824
|
|
|
748
|
-
getUnits: function() {
|
|
825
|
+
getUnits: function () {
|
|
749
826
|
var tmUnits = _(this.terms)
|
|
750
827
|
.chain()
|
|
751
|
-
.map(function(term) {
|
|
828
|
+
.map(function (term) {
|
|
752
829
|
return term.getUnits();
|
|
753
830
|
})
|
|
754
831
|
.flatten()
|
|
@@ -761,10 +838,10 @@ _.extend(Mul.prototype, {
|
|
|
761
838
|
|
|
762
839
|
// since we don't care about commutativity, we can render a Mul any way we choose
|
|
763
840
|
// so we follow convention: first any negatives, then any numbers, then everything else
|
|
764
|
-
tex: function() {
|
|
841
|
+
tex: function () {
|
|
765
842
|
var cdot = " \\cdot ";
|
|
766
843
|
|
|
767
|
-
var terms = _.groupBy(this.terms, function(term) {
|
|
844
|
+
var terms = _.groupBy(this.terms, function (term) {
|
|
768
845
|
if (term.isDivide()) {
|
|
769
846
|
return "inverse";
|
|
770
847
|
} else if (term instanceof Num) {
|
|
@@ -785,8 +862,8 @@ _.extend(Mul.prototype, {
|
|
|
785
862
|
// since we would like 1/2x/y to come out as \frac{1}{2}\frac{x}{y},
|
|
786
863
|
// and not \frac{1x}{2y}.
|
|
787
864
|
for (var i = 0; i < numbers.length; i++) {
|
|
788
|
-
var isRational =
|
|
789
|
-
|
|
865
|
+
var isRational =
|
|
866
|
+
numbers[i] instanceof Rational && !(numbers[i] instanceof Int);
|
|
790
867
|
if (isRational && others.length > 0 && inverses.length > 0) {
|
|
791
868
|
var withThisRemoved = numbers.slice();
|
|
792
869
|
withThisRemoved.splice(i, 1);
|
|
@@ -795,19 +872,23 @@ _.extend(Mul.prototype, {
|
|
|
795
872
|
}
|
|
796
873
|
}
|
|
797
874
|
|
|
798
|
-
numbers = _.compact(
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
875
|
+
numbers = _.compact(
|
|
876
|
+
_.map(numbers, function (term) {
|
|
877
|
+
var hasDenom =
|
|
878
|
+
term instanceof Rational && !(term instanceof Int);
|
|
879
|
+
var shouldPushDown =
|
|
880
|
+
!term.hints.fraction || inverses.length > 0;
|
|
881
|
+
if (hasDenom && shouldPushDown) {
|
|
882
|
+
// e.g. 3x/4 -> 3/4*x (internally) -> 3x/4 (rendered)
|
|
883
|
+
inverses.push(new Pow(new Int(term.d), Num.Div));
|
|
884
|
+
var number = new Int(term.n);
|
|
885
|
+
number.hints = term.hints;
|
|
886
|
+
return _.any(term.hints) ? number : null;
|
|
887
|
+
} else {
|
|
888
|
+
return term;
|
|
889
|
+
}
|
|
890
|
+
}),
|
|
891
|
+
);
|
|
811
892
|
|
|
812
893
|
if (numbers.length === 0 && others.length === 1) {
|
|
813
894
|
// e.g. (x+y)/z -> \frac{x+y}{z}
|
|
@@ -815,12 +896,15 @@ _.extend(Mul.prototype, {
|
|
|
815
896
|
} else {
|
|
816
897
|
var tex = "";
|
|
817
898
|
|
|
818
|
-
_.each(numbers, function(term) {
|
|
899
|
+
_.each(numbers, function (term) {
|
|
819
900
|
if (term.hints.subtract && term.hints.entered) {
|
|
820
901
|
negatives += "-";
|
|
821
902
|
tex += (tex ? cdot : "") + term.abs().tex();
|
|
822
|
-
} else if (
|
|
823
|
-
|
|
903
|
+
} else if (
|
|
904
|
+
term instanceof Int &&
|
|
905
|
+
term.n === -1 &&
|
|
906
|
+
(term.hints.negate || term.hints.subtract)
|
|
907
|
+
) {
|
|
824
908
|
// e.g. -1*-1 -> --1
|
|
825
909
|
// e.g. -1*x -> -x
|
|
826
910
|
negatives += "-";
|
|
@@ -830,7 +914,7 @@ _.extend(Mul.prototype, {
|
|
|
830
914
|
}
|
|
831
915
|
});
|
|
832
916
|
|
|
833
|
-
_.each(others, function(term) {
|
|
917
|
+
_.each(others, function (term) {
|
|
834
918
|
if (term.needsExplicitMul()) {
|
|
835
919
|
// e.g. 2*2^3 -> 2(dot)2^3
|
|
836
920
|
tex += (tex ? cdot : "") + term.tex();
|
|
@@ -849,30 +933,31 @@ _.extend(Mul.prototype, {
|
|
|
849
933
|
if (!inverses.length) {
|
|
850
934
|
return negatives + numerator;
|
|
851
935
|
} else {
|
|
852
|
-
var denominator = new Mul(_.invoke(inverses, "asDivide"))
|
|
936
|
+
var denominator = new Mul(_.invoke(inverses, "asDivide"))
|
|
937
|
+
.flatten()
|
|
938
|
+
.tex();
|
|
853
939
|
return negatives + "\\frac{" + numerator + "}{" + denominator + "}";
|
|
854
940
|
}
|
|
855
941
|
},
|
|
856
942
|
|
|
857
|
-
strip: function() {
|
|
858
|
-
var terms = _.map(this.terms, function(term) {
|
|
943
|
+
strip: function () {
|
|
944
|
+
var terms = _.map(this.terms, function (term) {
|
|
859
945
|
return term instanceof Num ? term.abs() : term.strip();
|
|
860
946
|
});
|
|
861
947
|
return new Mul(terms).flatten();
|
|
862
948
|
},
|
|
863
949
|
|
|
864
950
|
// expand numerator and denominator separately
|
|
865
|
-
expand: function() {
|
|
866
|
-
|
|
867
|
-
var isAdd = function(term) {
|
|
951
|
+
expand: function () {
|
|
952
|
+
var isAdd = function (term) {
|
|
868
953
|
return term instanceof Add;
|
|
869
954
|
};
|
|
870
955
|
|
|
871
|
-
var isInverse = function(term) {
|
|
956
|
+
var isInverse = function (term) {
|
|
872
957
|
return term instanceof Pow && term.exp.isNegative();
|
|
873
958
|
};
|
|
874
959
|
|
|
875
|
-
var isInverseAdd = function(term) {
|
|
960
|
+
var isInverseAdd = function (term) {
|
|
876
961
|
return isInverse(term) && isAdd(term.base);
|
|
877
962
|
};
|
|
878
963
|
|
|
@@ -895,18 +980,28 @@ _.extend(Mul.prototype, {
|
|
|
895
980
|
var others = grouped[false] || [];
|
|
896
981
|
|
|
897
982
|
// loop over each additive sequence
|
|
898
|
-
var expanded = _.reduce(
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
// loop over each
|
|
902
|
-
return
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
983
|
+
var expanded = _.reduce(
|
|
984
|
+
adds,
|
|
985
|
+
function (expanded, add) {
|
|
986
|
+
// loop over each expanded array of terms
|
|
987
|
+
return _.reduce(
|
|
988
|
+
expanded,
|
|
989
|
+
function (temp, array) {
|
|
990
|
+
// loop over each additive sequence's terms
|
|
991
|
+
return temp.concat(
|
|
992
|
+
_.map(add.terms, function (term) {
|
|
993
|
+
return array.concat(term);
|
|
994
|
+
}),
|
|
995
|
+
);
|
|
996
|
+
},
|
|
997
|
+
[],
|
|
998
|
+
);
|
|
999
|
+
},
|
|
1000
|
+
[[]],
|
|
1001
|
+
);
|
|
907
1002
|
|
|
908
1003
|
// join each fully expanded array of factors with remaining multiplicative factors
|
|
909
|
-
var muls = _.map(expanded, function(array) {
|
|
1004
|
+
var muls = _.map(expanded, function (array) {
|
|
910
1005
|
return new Mul(others.concat(array)).flatten();
|
|
911
1006
|
});
|
|
912
1007
|
|
|
@@ -914,30 +1009,36 @@ _.extend(Mul.prototype, {
|
|
|
914
1009
|
}
|
|
915
1010
|
|
|
916
1011
|
if (hasInverseAdd) {
|
|
917
|
-
var denominator = new Mul(
|
|
1012
|
+
var denominator = new Mul(
|
|
1013
|
+
_.invoke(inverses, "getDenominator"),
|
|
1014
|
+
).flatten();
|
|
918
1015
|
inverses = [new Pow(denominator.expand(), Num.Div)];
|
|
919
1016
|
}
|
|
920
1017
|
|
|
921
1018
|
return new Mul(normals.concat(inverses)).flatten();
|
|
922
1019
|
},
|
|
923
1020
|
|
|
924
|
-
factor: function(options) {
|
|
1021
|
+
factor: function (options) {
|
|
925
1022
|
var factored = this.recurse("factor", options).flatten();
|
|
926
|
-
if (!
|
|
1023
|
+
if (!(factored instanceof Mul)) {
|
|
927
1024
|
return factored;
|
|
928
1025
|
}
|
|
929
1026
|
|
|
930
1027
|
// Combine any factored out Rationals into one, but don't collect
|
|
931
|
-
var grouped = _.groupBy(factored.terms, function(term) {
|
|
1028
|
+
var grouped = _.groupBy(factored.terms, function (term) {
|
|
932
1029
|
return term instanceof Rational;
|
|
933
1030
|
});
|
|
934
1031
|
|
|
935
1032
|
// Could also accomplish this by passing a new option
|
|
936
1033
|
// e.g. return memo.mul(term, {autocollect: false});
|
|
937
1034
|
// TODO(alex): Decide whether this is a good use of options or not
|
|
938
|
-
var rational = _.reduce(
|
|
939
|
-
|
|
940
|
-
|
|
1035
|
+
var rational = _.reduce(
|
|
1036
|
+
grouped[true],
|
|
1037
|
+
function (memo, term) {
|
|
1038
|
+
return {n: memo.n * term.n, d: memo.d * term.d};
|
|
1039
|
+
},
|
|
1040
|
+
{n: 1, d: 1},
|
|
1041
|
+
);
|
|
941
1042
|
|
|
942
1043
|
if (rational.d === 1) {
|
|
943
1044
|
rational = new Int(rational.n);
|
|
@@ -948,7 +1049,7 @@ _.extend(Mul.prototype, {
|
|
|
948
1049
|
return new Mul((grouped[false] || []).concat(rational)).flatten();
|
|
949
1050
|
},
|
|
950
1051
|
|
|
951
|
-
collect: function(options) {
|
|
1052
|
+
collect: function (options) {
|
|
952
1053
|
var partitioned = this.recurse("collect", options).partition();
|
|
953
1054
|
var number = partitioned[0].reduce(options);
|
|
954
1055
|
|
|
@@ -970,7 +1071,7 @@ _.extend(Mul.prototype, {
|
|
|
970
1071
|
// [Expr base, Expr exp]
|
|
971
1072
|
var pairs = [];
|
|
972
1073
|
|
|
973
|
-
_.each(others, function(term) {
|
|
1074
|
+
_.each(others, function (term) {
|
|
974
1075
|
if (term instanceof Pow) {
|
|
975
1076
|
pairs.push([term.base, term.exp]);
|
|
976
1077
|
} else {
|
|
@@ -979,25 +1080,27 @@ _.extend(Mul.prototype, {
|
|
|
979
1080
|
});
|
|
980
1081
|
|
|
981
1082
|
// {(Expr base).print(): [[Expr base, Expr exp]]}
|
|
982
|
-
var grouped = _.groupBy(pairs, function(pair) {
|
|
1083
|
+
var grouped = _.groupBy(pairs, function (pair) {
|
|
983
1084
|
return pair[0].normalize().print();
|
|
984
1085
|
});
|
|
985
1086
|
|
|
986
1087
|
// [[Expr base, Expr exp]]
|
|
987
|
-
var summed = _.compact(
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
1088
|
+
var summed = _.compact(
|
|
1089
|
+
_.map(grouped, function (pairs) {
|
|
1090
|
+
var base = pairs[0][0];
|
|
1091
|
+
var sum = new Add(_.zip.apply(_, pairs)[1]);
|
|
1092
|
+
var exp = sum.collect(options);
|
|
1093
|
+
|
|
1094
|
+
if (exp instanceof Num && exp.eval() === 0) {
|
|
1095
|
+
return null;
|
|
1096
|
+
} else {
|
|
1097
|
+
return [base, exp];
|
|
1098
|
+
}
|
|
1099
|
+
}),
|
|
1100
|
+
);
|
|
998
1101
|
|
|
999
1102
|
// XXX `pairs` is shadowed four or five times in this function
|
|
1000
|
-
var pairs = _.groupBy(summed, function(pair) {
|
|
1103
|
+
var pairs = _.groupBy(summed, function (pair) {
|
|
1001
1104
|
if (pair[0] instanceof Trig && pair[0].isBasic()) {
|
|
1002
1105
|
return "trig";
|
|
1003
1106
|
} else if (pair[0] instanceof Log) {
|
|
@@ -1014,21 +1117,25 @@ _.extend(Mul.prototype, {
|
|
|
1014
1117
|
// combine sines and cosines into other trig functions
|
|
1015
1118
|
|
|
1016
1119
|
// {Trig.arg.print(): [[Trig base, Expr exp]]}
|
|
1017
|
-
var byArg = _.groupBy(trigs, function(pair) {
|
|
1120
|
+
var byArg = _.groupBy(trigs, function (pair) {
|
|
1018
1121
|
return pair[0].arg.normalize().print();
|
|
1019
1122
|
});
|
|
1020
1123
|
|
|
1021
1124
|
trigs = [];
|
|
1022
|
-
_.each(byArg, function(pairs) {
|
|
1125
|
+
_.each(byArg, function (pairs) {
|
|
1023
1126
|
var arg = pairs[0][0].arg;
|
|
1024
1127
|
|
|
1025
1128
|
// {Trig.type: Expr exp}
|
|
1026
1129
|
var funcs = {sin: Num.Zero, cos: Num.Zero};
|
|
1027
|
-
_.each(pairs, function(pair) {
|
|
1130
|
+
_.each(pairs, function (pair) {
|
|
1028
1131
|
funcs[pair[0].type] = pair[1];
|
|
1029
1132
|
});
|
|
1030
1133
|
|
|
1031
|
-
if (
|
|
1134
|
+
if (
|
|
1135
|
+
Mul.handleNegative(funcs.sin)
|
|
1136
|
+
.collect(options)
|
|
1137
|
+
.equals(funcs.cos)
|
|
1138
|
+
) {
|
|
1032
1139
|
// e.g. sin^x(y)/cos^x(y) -> tan^x(y)
|
|
1033
1140
|
if (funcs.cos.isNegative()) {
|
|
1034
1141
|
funcs = {tan: funcs.sin};
|
|
@@ -1040,7 +1147,7 @@ _.extend(Mul.prototype, {
|
|
|
1040
1147
|
// TODO(alex): combine even if exponents not a perfect match
|
|
1041
1148
|
// TODO(alex): transform 1/sin and 1/cos into csc and sec
|
|
1042
1149
|
|
|
1043
|
-
_.each(funcs, function(exp, type) {
|
|
1150
|
+
_.each(funcs, function (exp, type) {
|
|
1044
1151
|
trigs.push([new Trig(type, arg), exp]);
|
|
1045
1152
|
});
|
|
1046
1153
|
});
|
|
@@ -1050,24 +1157,34 @@ _.extend(Mul.prototype, {
|
|
|
1050
1157
|
// combine logs with the same base
|
|
1051
1158
|
|
|
1052
1159
|
// {Log.base.print(): [[Log base, Expr exp]]}
|
|
1053
|
-
var byBase = _.groupBy(logs, function(pair) {
|
|
1160
|
+
var byBase = _.groupBy(logs, function (pair) {
|
|
1054
1161
|
return pair[0].base.normalize().print();
|
|
1055
1162
|
});
|
|
1056
1163
|
|
|
1057
1164
|
logs = [];
|
|
1058
1165
|
|
|
1059
|
-
_.each(byBase, function(pairs) {
|
|
1166
|
+
_.each(byBase, function (pairs) {
|
|
1060
1167
|
// only combine two logs of the same base, otherwise commutative
|
|
1061
1168
|
// differences result in different equally valid output
|
|
1062
1169
|
// e.g. ln(x)/ln(z)*ln(y) -> log_z(x)*ln(y)
|
|
1063
1170
|
// e.g. ln(x)*ln(y)/ln(z) -> ln(x)*log_z(y)
|
|
1064
|
-
if (
|
|
1065
|
-
|
|
1171
|
+
if (
|
|
1172
|
+
pairs.length === 2 &&
|
|
1173
|
+
Mul.handleNegative(pairs[0][1])
|
|
1174
|
+
.collect(options)
|
|
1175
|
+
.equals(pairs[1][1])
|
|
1176
|
+
) {
|
|
1066
1177
|
// e.g. ln(x)^y/ln(b)^y -> log_b(x)^y
|
|
1067
1178
|
if (pairs[0][1].isNegative()) {
|
|
1068
|
-
logs.push([
|
|
1179
|
+
logs.push([
|
|
1180
|
+
new Log(pairs[0][0].power, pairs[1][0].power),
|
|
1181
|
+
pairs[1][1],
|
|
1182
|
+
]);
|
|
1069
1183
|
} else {
|
|
1070
|
-
logs.push([
|
|
1184
|
+
logs.push([
|
|
1185
|
+
new Log(pairs[1][0].power, pairs[0][0].power),
|
|
1186
|
+
pairs[0][1],
|
|
1187
|
+
]);
|
|
1071
1188
|
}
|
|
1072
1189
|
} else {
|
|
1073
1190
|
logs = logs.concat(pairs);
|
|
@@ -1079,27 +1196,29 @@ _.extend(Mul.prototype, {
|
|
|
1079
1196
|
|
|
1080
1197
|
pairs = trigs.concat(logs).concat(exprs);
|
|
1081
1198
|
|
|
1082
|
-
var collected = _.map(pairs, function(pair) {
|
|
1199
|
+
var collected = _.map(pairs, function (pair) {
|
|
1083
1200
|
return new Pow(pair[0], pair[1]).collect(options);
|
|
1084
1201
|
});
|
|
1085
1202
|
|
|
1086
1203
|
return new Mul([number].concat(collected)).flatten();
|
|
1087
1204
|
},
|
|
1088
1205
|
|
|
1089
|
-
isSubtract: function() {
|
|
1090
|
-
return _.any(this.terms, function(term) {
|
|
1206
|
+
isSubtract: function () {
|
|
1207
|
+
return _.any(this.terms, function (term) {
|
|
1091
1208
|
return term instanceof Num && term.hints.subtract;
|
|
1092
1209
|
});
|
|
1093
1210
|
},
|
|
1094
1211
|
|
|
1095
1212
|
// factor a single -1 in to the Mul
|
|
1096
1213
|
// combine with a Num if all Nums are positive, else add as a term
|
|
1097
|
-
factorIn: function(hint) {
|
|
1214
|
+
factorIn: function (hint) {
|
|
1098
1215
|
var partitioned = this.partition();
|
|
1099
1216
|
var numbers = partitioned[0].terms;
|
|
1100
|
-
var fold =
|
|
1101
|
-
|
|
1102
|
-
|
|
1217
|
+
var fold =
|
|
1218
|
+
numbers.length &&
|
|
1219
|
+
_.all(numbers, function (num) {
|
|
1220
|
+
return num.n > 0;
|
|
1221
|
+
});
|
|
1103
1222
|
|
|
1104
1223
|
if (fold) {
|
|
1105
1224
|
// e.g. - x*2*3 -> x*-2*3
|
|
@@ -1115,16 +1234,18 @@ _.extend(Mul.prototype, {
|
|
|
1115
1234
|
|
|
1116
1235
|
// factor out a single hinted -1 (assume it is the division hint)
|
|
1117
1236
|
// TODO(alex): make more general or rename to be more specific
|
|
1118
|
-
factorOut: function() {
|
|
1237
|
+
factorOut: function () {
|
|
1119
1238
|
var factored = false;
|
|
1120
|
-
var terms = _.compact(
|
|
1121
|
-
|
|
1122
|
-
factored
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1239
|
+
var terms = _.compact(
|
|
1240
|
+
_.map(this.terms, function (term, i, list) {
|
|
1241
|
+
if (!factored && term instanceof Num && term.hints.divide) {
|
|
1242
|
+
factored = true;
|
|
1243
|
+
return term.n !== -1 ? term.negate() : null;
|
|
1244
|
+
} else {
|
|
1245
|
+
return term;
|
|
1246
|
+
}
|
|
1247
|
+
}),
|
|
1248
|
+
);
|
|
1128
1249
|
|
|
1129
1250
|
if (terms.length === 1) {
|
|
1130
1251
|
return terms[0];
|
|
@@ -1133,21 +1254,25 @@ _.extend(Mul.prototype, {
|
|
|
1133
1254
|
}
|
|
1134
1255
|
},
|
|
1135
1256
|
|
|
1136
|
-
reduce: function(options) {
|
|
1137
|
-
return _.reduce(
|
|
1138
|
-
|
|
1139
|
-
|
|
1257
|
+
reduce: function (options) {
|
|
1258
|
+
return _.reduce(
|
|
1259
|
+
this.terms,
|
|
1260
|
+
function (memo, term) {
|
|
1261
|
+
return memo.mul(term, options);
|
|
1262
|
+
},
|
|
1263
|
+
this.identity,
|
|
1264
|
+
);
|
|
1140
1265
|
},
|
|
1141
1266
|
|
|
1142
|
-
findGCD: function(factor) {
|
|
1267
|
+
findGCD: function (factor) {
|
|
1143
1268
|
return new Mul(_.invoke(this.terms, "findGCD", factor)).flatten();
|
|
1144
1269
|
},
|
|
1145
1270
|
|
|
1146
|
-
asMul: function() {
|
|
1271
|
+
asMul: function () {
|
|
1147
1272
|
return this;
|
|
1148
1273
|
},
|
|
1149
1274
|
|
|
1150
|
-
asPositiveFactor: function() {
|
|
1275
|
+
asPositiveFactor: function () {
|
|
1151
1276
|
if (this.isPositive()) {
|
|
1152
1277
|
return this;
|
|
1153
1278
|
} else {
|
|
@@ -1156,36 +1281,38 @@ _.extend(Mul.prototype, {
|
|
|
1156
1281
|
}
|
|
1157
1282
|
},
|
|
1158
1283
|
|
|
1159
|
-
isNegative: function() {
|
|
1284
|
+
isNegative: function () {
|
|
1160
1285
|
return _.any(_.invoke(this.collect().terms, "isNegative"));
|
|
1161
1286
|
},
|
|
1162
1287
|
|
|
1163
|
-
fold: function() {
|
|
1288
|
+
fold: function () {
|
|
1164
1289
|
return Mul.fold(this);
|
|
1165
1290
|
},
|
|
1166
1291
|
|
|
1167
|
-
negate: function() {
|
|
1168
|
-
var isNum = function(expr) {
|
|
1292
|
+
negate: function () {
|
|
1293
|
+
var isNum = function (expr) {
|
|
1294
|
+
return expr instanceof Num;
|
|
1295
|
+
};
|
|
1169
1296
|
if (_.any(this.terms, isNum)) {
|
|
1170
1297
|
var num = _.find(this.terms, isNum);
|
|
1171
1298
|
return this.replace(num, num.negate());
|
|
1172
1299
|
} else {
|
|
1173
1300
|
return new Mul([Num.Neg].concat(this.terms));
|
|
1174
1301
|
}
|
|
1175
|
-
}
|
|
1302
|
+
},
|
|
1176
1303
|
});
|
|
1177
1304
|
|
|
1178
1305
|
// static methods for the sequence types
|
|
1179
|
-
_.each([Add, Mul], function(type) {
|
|
1306
|
+
_.each([Add, Mul], function (type) {
|
|
1180
1307
|
_.extend(type, {
|
|
1181
1308
|
// create a new sequence unless left is already one (returns a copy)
|
|
1182
|
-
createOrAppend: function(left, right) {
|
|
1309
|
+
createOrAppend: function (left, right) {
|
|
1183
1310
|
if (left instanceof type) {
|
|
1184
1311
|
return new type(left.terms.concat(right));
|
|
1185
1312
|
} else {
|
|
1186
1313
|
return new type(left, right);
|
|
1187
1314
|
}
|
|
1188
|
-
}
|
|
1315
|
+
},
|
|
1189
1316
|
});
|
|
1190
1317
|
});
|
|
1191
1318
|
|
|
@@ -1194,7 +1321,7 @@ _.extend(Mul, {
|
|
|
1194
1321
|
// never fold into a Num that's already negative or a Mul that has a negative Num
|
|
1195
1322
|
// an optional hint is kept track of to properly render user input
|
|
1196
1323
|
// an empty hint means negation
|
|
1197
|
-
handleNegative: function(expr, hint) {
|
|
1324
|
+
handleNegative: function (expr, hint) {
|
|
1198
1325
|
if (expr instanceof Num && expr.n > 0) {
|
|
1199
1326
|
// e.g. - 2 -> -2
|
|
1200
1327
|
var negated = expr.negate();
|
|
@@ -1213,8 +1340,7 @@ _.extend(Mul, {
|
|
|
1213
1340
|
},
|
|
1214
1341
|
|
|
1215
1342
|
// division can create either a Rational or a Mul
|
|
1216
|
-
handleDivide: function(left, right) {
|
|
1217
|
-
|
|
1343
|
+
handleDivide: function (left, right) {
|
|
1218
1344
|
// dividing by a Mul is the same as repeated division by its terms
|
|
1219
1345
|
if (right instanceof Mul) {
|
|
1220
1346
|
var first = Mul.handleDivide(left, right.terms[0]);
|
|
@@ -1222,19 +1348,26 @@ _.extend(Mul, {
|
|
|
1222
1348
|
return Mul.handleDivide(first, rest);
|
|
1223
1349
|
}
|
|
1224
1350
|
|
|
1225
|
-
var isInt = function(expr) {
|
|
1226
|
-
|
|
1351
|
+
var isInt = function (expr) {
|
|
1352
|
+
return expr instanceof Int;
|
|
1353
|
+
};
|
|
1354
|
+
var isRational = function (expr) {
|
|
1355
|
+
return expr instanceof Rational;
|
|
1356
|
+
};
|
|
1227
1357
|
|
|
1228
1358
|
// for simplification purposes, fold Ints into Rationals if possible
|
|
1229
1359
|
// e.g. 3x / 4 -> 3/4 * x (will still render as 3x/4)
|
|
1230
1360
|
if (isInt(right) && left instanceof Mul && _.any(left.terms, isInt)) {
|
|
1231
|
-
|
|
1232
1361
|
// search from the right
|
|
1233
1362
|
var reversed = left.terms.slice().reverse();
|
|
1234
1363
|
var num = _.find(reversed, isRational);
|
|
1235
1364
|
|
|
1236
1365
|
if (!isInt(num)) {
|
|
1237
|
-
return new Mul(
|
|
1366
|
+
return new Mul(
|
|
1367
|
+
left.terms.concat([
|
|
1368
|
+
new Rational(1, right.n).addHint("fraction"),
|
|
1369
|
+
]),
|
|
1370
|
+
);
|
|
1238
1371
|
}
|
|
1239
1372
|
|
|
1240
1373
|
var rational = new Rational(num.n, right.n);
|
|
@@ -1255,12 +1388,15 @@ _.extend(Mul, {
|
|
|
1255
1388
|
}
|
|
1256
1389
|
}
|
|
1257
1390
|
|
|
1258
|
-
var divide = function(a, b) {
|
|
1391
|
+
var divide = function (a, b) {
|
|
1259
1392
|
if (b instanceof Int) {
|
|
1260
1393
|
if (a instanceof Int) {
|
|
1261
1394
|
if (a.n < 0 && b.n < 0) {
|
|
1262
1395
|
// e.g. -2 / -3 -> -1*-2/3
|
|
1263
|
-
return [
|
|
1396
|
+
return [
|
|
1397
|
+
Num.Neg,
|
|
1398
|
+
new Rational(a.n, -b.n).addHint("fraction"),
|
|
1399
|
+
];
|
|
1264
1400
|
} else {
|
|
1265
1401
|
// e.g. 2 / 3 -> 2/3
|
|
1266
1402
|
// e.g. -2 / 3 -> -2/3
|
|
@@ -1328,31 +1464,46 @@ _.extend(Mul, {
|
|
|
1328
1464
|
// e.g. sin(x)*x -> sin(x)*x
|
|
1329
1465
|
// e.g. sin(x)*(x) -> sin(x)*x
|
|
1330
1466
|
// e.g. sin(x)*sin(y) -> sin(x)*sin(y)
|
|
1331
|
-
fold: function(expr) {
|
|
1467
|
+
fold: function (expr) {
|
|
1332
1468
|
if (expr instanceof Mul) {
|
|
1333
1469
|
// assuming that this will be second to last
|
|
1334
|
-
var trigLog = _.find(_.initial(expr.terms), function(term) {
|
|
1335
|
-
return (
|
|
1470
|
+
var trigLog = _.find(_.initial(expr.terms), function (term) {
|
|
1471
|
+
return (
|
|
1472
|
+
(term instanceof Trig || term instanceof Log) &&
|
|
1473
|
+
term.hints.open
|
|
1474
|
+
);
|
|
1336
1475
|
});
|
|
1337
1476
|
var index = _.indexOf(expr.terms, trigLog);
|
|
1338
1477
|
|
|
1339
1478
|
if (trigLog) {
|
|
1340
1479
|
var last = _.last(expr.terms);
|
|
1341
|
-
if (
|
|
1342
|
-
|
|
1480
|
+
if (
|
|
1481
|
+
trigLog.hints.parens ||
|
|
1482
|
+
last.hints.parens ||
|
|
1483
|
+
last.has(Trig) ||
|
|
1484
|
+
last.has(Log)
|
|
1485
|
+
) {
|
|
1343
1486
|
trigLog.hints.open = false;
|
|
1344
1487
|
} else {
|
|
1345
1488
|
var newTrigLog;
|
|
1346
1489
|
if (trigLog instanceof Trig) {
|
|
1347
|
-
newTrigLog = Trig.create(
|
|
1490
|
+
newTrigLog = Trig.create(
|
|
1491
|
+
[trigLog.type, trigLog.exp],
|
|
1492
|
+
Mul.createOrAppend(trigLog.arg, last).fold(),
|
|
1493
|
+
);
|
|
1348
1494
|
} else {
|
|
1349
|
-
newTrigLog = Log.create(
|
|
1495
|
+
newTrigLog = Log.create(
|
|
1496
|
+
trigLog.base,
|
|
1497
|
+
Mul.createOrAppend(trigLog.power, last).fold(),
|
|
1498
|
+
);
|
|
1350
1499
|
}
|
|
1351
1500
|
|
|
1352
1501
|
if (index === 0) {
|
|
1353
1502
|
return newTrigLog;
|
|
1354
1503
|
} else {
|
|
1355
|
-
return new Mul(
|
|
1504
|
+
return new Mul(
|
|
1505
|
+
expr.terms.slice(0, index).concat(newTrigLog),
|
|
1506
|
+
).fold();
|
|
1356
1507
|
}
|
|
1357
1508
|
}
|
|
1358
1509
|
}
|
|
@@ -1360,42 +1511,53 @@ _.extend(Mul, {
|
|
|
1360
1511
|
var partitioned = expr.partition();
|
|
1361
1512
|
var numbers = partitioned[0].terms;
|
|
1362
1513
|
|
|
1363
|
-
var pos = function(num) {
|
|
1364
|
-
|
|
1365
|
-
|
|
1514
|
+
var pos = function (num) {
|
|
1515
|
+
return num.n > 0;
|
|
1516
|
+
};
|
|
1517
|
+
var neg = function (num) {
|
|
1518
|
+
return num.n === -1 && num.hints.negate;
|
|
1519
|
+
};
|
|
1520
|
+
var posOrNeg = function (num) {
|
|
1521
|
+
return pos(num) || neg(num);
|
|
1522
|
+
};
|
|
1366
1523
|
|
|
1367
|
-
if (
|
|
1524
|
+
if (
|
|
1525
|
+
numbers.length > 1 &&
|
|
1368
1526
|
_.some(numbers, neg) &&
|
|
1369
1527
|
_.some(numbers, pos) &&
|
|
1370
|
-
_.every(numbers, posOrNeg)
|
|
1371
|
-
|
|
1528
|
+
_.every(numbers, posOrNeg)
|
|
1529
|
+
) {
|
|
1372
1530
|
var firstNeg = _.indexOf(expr.terms, _.find(expr.terms, neg));
|
|
1373
1531
|
var firstNum = _.indexOf(expr.terms, _.find(expr.terms, pos));
|
|
1374
1532
|
|
|
1375
1533
|
// e.g. -x*2 -> x*-2
|
|
1376
1534
|
if (firstNeg < firstNum) {
|
|
1377
|
-
return expr
|
|
1378
|
-
|
|
1379
|
-
|
|
1535
|
+
return expr
|
|
1536
|
+
.replace(firstNum, expr.terms[firstNum].negate())
|
|
1537
|
+
.remove(firstNeg);
|
|
1380
1538
|
}
|
|
1381
1539
|
}
|
|
1382
1540
|
}
|
|
1383
1541
|
|
|
1384
1542
|
// in all other cases, make no change
|
|
1385
1543
|
return expr;
|
|
1386
|
-
}
|
|
1544
|
+
},
|
|
1387
1545
|
});
|
|
1388
1546
|
|
|
1389
|
-
|
|
1390
1547
|
/* exponentiation */
|
|
1391
|
-
export function Pow(base, exp) {
|
|
1548
|
+
export function Pow(base, exp) {
|
|
1549
|
+
this.base = base;
|
|
1550
|
+
this.exp = exp;
|
|
1551
|
+
}
|
|
1392
1552
|
Pow.prototype = new Expr();
|
|
1393
1553
|
|
|
1394
1554
|
_.extend(Pow.prototype, {
|
|
1395
1555
|
func: Pow,
|
|
1396
|
-
args: function() {
|
|
1556
|
+
args: function () {
|
|
1557
|
+
return [this.base, this.exp];
|
|
1558
|
+
},
|
|
1397
1559
|
|
|
1398
|
-
eval: function(vars, options) {
|
|
1560
|
+
eval: function (vars, options) {
|
|
1399
1561
|
var evaledBase = this.base.eval(vars, options);
|
|
1400
1562
|
var evaledExp = this.exp.eval(vars, options);
|
|
1401
1563
|
|
|
@@ -1423,7 +1585,7 @@ _.extend(Pow.prototype, {
|
|
|
1423
1585
|
var oddDenominator = Math.abs(simplifiedExp.d) % 2 === 1;
|
|
1424
1586
|
if (oddDenominator) {
|
|
1425
1587
|
var oddNumerator = Math.abs(simplifiedExp.n) % 2 === 1;
|
|
1426
|
-
var sign =
|
|
1588
|
+
var sign = oddNumerator ? -1 : 1;
|
|
1427
1589
|
return sign * Math.pow(-1 * evaledBase, evaledExp);
|
|
1428
1590
|
}
|
|
1429
1591
|
}
|
|
@@ -1431,21 +1593,24 @@ _.extend(Pow.prototype, {
|
|
|
1431
1593
|
return Math.pow(evaledBase, evaledExp);
|
|
1432
1594
|
},
|
|
1433
1595
|
|
|
1434
|
-
getUnits: function() {
|
|
1435
|
-
return this.base.getUnits().map(
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1596
|
+
getUnits: function () {
|
|
1597
|
+
return this.base.getUnits().map(
|
|
1598
|
+
function (unit) {
|
|
1599
|
+
return {
|
|
1600
|
+
unit: unit.unit,
|
|
1601
|
+
pow: unit.pow * this.exp.n,
|
|
1602
|
+
};
|
|
1603
|
+
}.bind(this),
|
|
1604
|
+
);
|
|
1441
1605
|
},
|
|
1442
1606
|
|
|
1443
|
-
codegen: function() {
|
|
1444
|
-
return
|
|
1445
|
-
", " + this.exp.codegen() + ")"
|
|
1607
|
+
codegen: function () {
|
|
1608
|
+
return (
|
|
1609
|
+
"Math.pow(" + this.base.codegen() + ", " + this.exp.codegen() + ")"
|
|
1610
|
+
);
|
|
1446
1611
|
},
|
|
1447
1612
|
|
|
1448
|
-
print: function() {
|
|
1613
|
+
print: function () {
|
|
1449
1614
|
var base = this.base.print();
|
|
1450
1615
|
if (this.base instanceof Seq || this.base instanceof Pow) {
|
|
1451
1616
|
base = "(" + base + ")";
|
|
@@ -1453,17 +1618,16 @@ _.extend(Pow.prototype, {
|
|
|
1453
1618
|
return base + "^(" + this.exp.print() + ")";
|
|
1454
1619
|
},
|
|
1455
1620
|
|
|
1456
|
-
tex: function() {
|
|
1621
|
+
tex: function () {
|
|
1457
1622
|
if (this.isDivide()) {
|
|
1458
|
-
|
|
1459
1623
|
// e.g. x ^ -1 w/hint -> 1/x
|
|
1460
1624
|
return "\\frac{1}{" + this.asDivide().tex() + "}";
|
|
1461
|
-
|
|
1462
1625
|
} else if (this.isRoot()) {
|
|
1463
|
-
|
|
1464
1626
|
if (this.exp.n !== 1) {
|
|
1465
|
-
error(
|
|
1466
|
-
|
|
1627
|
+
error(
|
|
1628
|
+
"Node marked with hint 'root' does not have exponent " +
|
|
1629
|
+
"of form 1/x.",
|
|
1630
|
+
);
|
|
1467
1631
|
}
|
|
1468
1632
|
|
|
1469
1633
|
if (this.exp.d === 2) {
|
|
@@ -1473,21 +1637,24 @@ _.extend(Pow.prototype, {
|
|
|
1473
1637
|
// e.g. x ^ 1/y w/hint -> sqrt[y]{x}
|
|
1474
1638
|
return "\\sqrt[" + this.exp.d + "]{" + this.base.tex() + "}";
|
|
1475
1639
|
}
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
this.
|
|
1479
|
-
this.exp
|
|
1480
|
-
|
|
1640
|
+
} else if (
|
|
1641
|
+
this.base instanceof Trig &&
|
|
1642
|
+
!this.base.isInverse() &&
|
|
1643
|
+
this.exp instanceof Num &&
|
|
1644
|
+
this.exp.isSimple() &&
|
|
1645
|
+
this.exp.eval() >= 0
|
|
1646
|
+
) {
|
|
1481
1647
|
// e.g sin(x) ^ 2 -> sin^2(x)
|
|
1482
1648
|
var split = this.base.tex({split: true});
|
|
1483
1649
|
return split[0] + "^{" + this.exp.tex() + "}" + split[1];
|
|
1484
|
-
|
|
1485
1650
|
} else {
|
|
1486
|
-
|
|
1487
1651
|
// e.g. x ^ y -> x^y
|
|
1488
1652
|
var base = this.base.tex();
|
|
1489
|
-
if (
|
|
1490
|
-
|
|
1653
|
+
if (
|
|
1654
|
+
this.base instanceof Seq ||
|
|
1655
|
+
this.base instanceof Pow ||
|
|
1656
|
+
(this.base instanceof Num && !this.base.isSimple())
|
|
1657
|
+
) {
|
|
1491
1658
|
// e.g. a+b ^ c -> (a+b)^c
|
|
1492
1659
|
base = "(" + base + ")";
|
|
1493
1660
|
} else if (this.base instanceof Trig || this.base instanceof Log) {
|
|
@@ -1498,30 +1665,33 @@ _.extend(Pow.prototype, {
|
|
|
1498
1665
|
}
|
|
1499
1666
|
},
|
|
1500
1667
|
|
|
1501
|
-
needsExplicitMul: function() {
|
|
1668
|
+
needsExplicitMul: function () {
|
|
1502
1669
|
return this.isRoot() ? false : this.base.needsExplicitMul();
|
|
1503
1670
|
},
|
|
1504
1671
|
|
|
1505
|
-
expand: function() {
|
|
1672
|
+
expand: function () {
|
|
1506
1673
|
var pow = this.recurse("expand");
|
|
1507
1674
|
|
|
1508
1675
|
if (pow.base instanceof Mul) {
|
|
1509
1676
|
// e.g. (ab)^c -> a^c*b^c
|
|
1510
1677
|
|
|
1511
|
-
var terms = _.map(pow.base.terms, function(term) {
|
|
1678
|
+
var terms = _.map(pow.base.terms, function (term) {
|
|
1512
1679
|
return new Pow(term, pow.exp);
|
|
1513
1680
|
});
|
|
1514
1681
|
|
|
1515
1682
|
return new Mul(terms).expand();
|
|
1516
|
-
|
|
1517
|
-
|
|
1683
|
+
} else if (
|
|
1684
|
+
pow.base instanceof Add &&
|
|
1685
|
+
pow.exp instanceof Int &&
|
|
1686
|
+
pow.exp.abs().eval() > 1
|
|
1687
|
+
) {
|
|
1518
1688
|
// e.g. (a+b)^2 -> a*a+a*b+a*b+b*b
|
|
1519
1689
|
// e.g. (a+b)^-2 -> (a*a+a*b+a*b+b*b)^-1
|
|
1520
1690
|
|
|
1521
1691
|
var positive = pow.exp.eval() > 0;
|
|
1522
1692
|
var n = pow.exp.abs().eval();
|
|
1523
1693
|
|
|
1524
|
-
var signed = function(mul) {
|
|
1694
|
+
var signed = function (mul) {
|
|
1525
1695
|
return positive ? mul : new Pow(mul, Num.Div);
|
|
1526
1696
|
};
|
|
1527
1697
|
|
|
@@ -1538,19 +1708,22 @@ _.extend(Pow.prototype, {
|
|
|
1538
1708
|
}
|
|
1539
1709
|
|
|
1540
1710
|
// otherwise decompose n into powers of 2 ...
|
|
1541
|
-
var indices = _.map(
|
|
1542
|
-
|
|
1543
|
-
|
|
1711
|
+
var indices = _.map(
|
|
1712
|
+
n.toString(2).split(""),
|
|
1713
|
+
function (str, i, list) {
|
|
1714
|
+
return Number(str) * Math.pow(2, list.length - i - 1);
|
|
1715
|
+
},
|
|
1716
|
+
);
|
|
1544
1717
|
indices = _.without(indices, 0);
|
|
1545
1718
|
|
|
1546
1719
|
// ... then combine
|
|
1547
1720
|
var mul = new Mul(_.pick(cache, indices)).expand().collect();
|
|
1548
1721
|
return signed(mul);
|
|
1549
|
-
|
|
1550
|
-
|
|
1722
|
+
} else if (pow.exp instanceof Add) {
|
|
1723
|
+
// DEFINITELY want behind super-simplify() flag
|
|
1551
1724
|
// e.g. x^(a+b) -> x^a*x^b
|
|
1552
1725
|
|
|
1553
|
-
var terms = _.map(pow.exp.terms, function(term) {
|
|
1726
|
+
var terms = _.map(pow.exp.terms, function (term) {
|
|
1554
1727
|
return new Pow(pow.base, term).expand();
|
|
1555
1728
|
});
|
|
1556
1729
|
|
|
@@ -1560,10 +1733,10 @@ _.extend(Pow.prototype, {
|
|
|
1560
1733
|
}
|
|
1561
1734
|
},
|
|
1562
1735
|
|
|
1563
|
-
factor: function() {
|
|
1736
|
+
factor: function () {
|
|
1564
1737
|
var pow = this.recurse("factor");
|
|
1565
1738
|
if (pow.base instanceof Mul) {
|
|
1566
|
-
var terms = _.map(pow.base.terms, function(term) {
|
|
1739
|
+
var terms = _.map(pow.base.terms, function (term) {
|
|
1567
1740
|
if (term instanceof Int && pow.exp.equals(Num.Div)) {
|
|
1568
1741
|
// Anything that can be a Rational should be a Rational
|
|
1569
1742
|
// e.g. 2^(-1) -> 1/2
|
|
@@ -1578,8 +1751,7 @@ _.extend(Pow.prototype, {
|
|
|
1578
1751
|
}
|
|
1579
1752
|
},
|
|
1580
1753
|
|
|
1581
|
-
collect: function(options) {
|
|
1582
|
-
|
|
1754
|
+
collect: function (options) {
|
|
1583
1755
|
if (this.base instanceof Pow) {
|
|
1584
1756
|
// collect this first to avoid having to deal with float precision
|
|
1585
1757
|
// e.g. sqrt(2)^2 -> 2, not 2.0000000000000004
|
|
@@ -1591,39 +1763,29 @@ _.extend(Pow.prototype, {
|
|
|
1591
1763
|
|
|
1592
1764
|
var pow = this.recurse("collect", options);
|
|
1593
1765
|
|
|
1594
|
-
var isSimilarLog = function(term) {
|
|
1766
|
+
var isSimilarLog = function (term) {
|
|
1595
1767
|
return term instanceof Log && term.base.equals(pow.base);
|
|
1596
1768
|
};
|
|
1597
1769
|
|
|
1598
|
-
if (pow.exp instanceof Num &&
|
|
1599
|
-
pow.exp.eval() === 0) {
|
|
1600
|
-
|
|
1770
|
+
if (pow.exp instanceof Num && pow.exp.eval() === 0) {
|
|
1601
1771
|
// e.g. x^0 -> 1
|
|
1602
1772
|
return Num.One;
|
|
1603
|
-
|
|
1604
|
-
} else if (pow.exp instanceof Num &&
|
|
1605
|
-
pow.exp.eval() === 1) {
|
|
1606
|
-
|
|
1773
|
+
} else if (pow.exp instanceof Num && pow.exp.eval() === 1) {
|
|
1607
1774
|
// e.g. x^1 -> x
|
|
1608
1775
|
return pow.base;
|
|
1609
|
-
|
|
1610
1776
|
} else if (isSimilarLog(pow.exp)) {
|
|
1611
|
-
|
|
1612
1777
|
// e.g. b^(log_b(x)) -> x
|
|
1613
1778
|
return pow.exp.power;
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
_.any(pow.exp.terms, isSimilarLog)
|
|
1617
|
-
|
|
1779
|
+
} else if (
|
|
1780
|
+
pow.exp instanceof Mul &&
|
|
1781
|
+
_.any(pow.exp.terms, isSimilarLog)
|
|
1782
|
+
) {
|
|
1618
1783
|
// e.g. b^(2*y*log_b(x)) -> x^(2*y)
|
|
1619
1784
|
var log = _.find(pow.exp.terms, isSimilarLog);
|
|
1620
1785
|
var base = log.power;
|
|
1621
1786
|
var exp = pow.exp.remove(log).flatten();
|
|
1622
1787
|
return new Pow(base, exp).collect(options);
|
|
1623
|
-
|
|
1624
|
-
} else if (pow.base instanceof Num &&
|
|
1625
|
-
pow.exp instanceof Num) {
|
|
1626
|
-
|
|
1788
|
+
} else if (pow.base instanceof Num && pow.exp instanceof Num) {
|
|
1627
1789
|
// TODO(alex): Consider encapsualting this logic (and similar logic
|
|
1628
1790
|
// elsewhere) into a separate Decimal class for user-entered floats
|
|
1629
1791
|
if (options && options.preciseFloats) {
|
|
@@ -1657,13 +1819,18 @@ _.extend(Pow.prototype, {
|
|
|
1657
1819
|
},
|
|
1658
1820
|
|
|
1659
1821
|
// checks whether this Pow represents user-entered division
|
|
1660
|
-
isDivide: function() {
|
|
1661
|
-
var isDiv = function(arg) {
|
|
1662
|
-
|
|
1822
|
+
isDivide: function () {
|
|
1823
|
+
var isDiv = function (arg) {
|
|
1824
|
+
return arg instanceof Num && arg.hints.divide;
|
|
1825
|
+
};
|
|
1826
|
+
return (
|
|
1827
|
+
isDiv(this.exp) ||
|
|
1828
|
+
(this.exp instanceof Mul && _.any(this.exp.terms, isDiv))
|
|
1829
|
+
);
|
|
1663
1830
|
},
|
|
1664
1831
|
|
|
1665
1832
|
// assuming this Pow represents user-entered division, returns the denominator
|
|
1666
|
-
asDivide: function() {
|
|
1833
|
+
asDivide: function () {
|
|
1667
1834
|
if (this.exp instanceof Num) {
|
|
1668
1835
|
if (this.exp.eval() === -1) {
|
|
1669
1836
|
return this.base;
|
|
@@ -1680,23 +1847,36 @@ _.extend(Pow.prototype, {
|
|
|
1680
1847
|
}
|
|
1681
1848
|
},
|
|
1682
1849
|
|
|
1683
|
-
isRoot: function() {
|
|
1850
|
+
isRoot: function () {
|
|
1684
1851
|
return this.exp instanceof Rational && this.exp.hints.root;
|
|
1685
1852
|
},
|
|
1686
1853
|
|
|
1687
|
-
isSquaredTrig: function() {
|
|
1688
|
-
return
|
|
1689
|
-
this.
|
|
1854
|
+
isSquaredTrig: function () {
|
|
1855
|
+
return (
|
|
1856
|
+
this.base instanceof Trig &&
|
|
1857
|
+
!this.base.isInverse() &&
|
|
1858
|
+
this.exp instanceof Num &&
|
|
1859
|
+
this.exp.eval() === 2
|
|
1860
|
+
);
|
|
1690
1861
|
},
|
|
1691
1862
|
|
|
1692
1863
|
// extract whatever denominator makes sense, ignoring hints
|
|
1693
1864
|
// if negative exponent, will recursively include the base's denominator as well
|
|
1694
|
-
getDenominator: function() {
|
|
1865
|
+
getDenominator: function () {
|
|
1695
1866
|
if (this.exp instanceof Num && this.exp.eval() === -1) {
|
|
1696
|
-
return Mul.createOrAppend(
|
|
1867
|
+
return Mul.createOrAppend(
|
|
1868
|
+
this.base,
|
|
1869
|
+
this.base.getDenominator(),
|
|
1870
|
+
).flatten();
|
|
1697
1871
|
} else if (this.exp.isNegative()) {
|
|
1698
|
-
var pow = new Pow(
|
|
1699
|
-
|
|
1872
|
+
var pow = new Pow(
|
|
1873
|
+
this.base,
|
|
1874
|
+
Mul.handleNegative(this.exp).collect(),
|
|
1875
|
+
);
|
|
1876
|
+
return Mul.createOrAppend(
|
|
1877
|
+
pow,
|
|
1878
|
+
pow.collect().getDenominator(),
|
|
1879
|
+
).flatten();
|
|
1700
1880
|
} else if (this.base instanceof Num) {
|
|
1701
1881
|
return new Pow(this.base.getDenominator(), this.exp).collect();
|
|
1702
1882
|
} else {
|
|
@@ -1704,7 +1884,7 @@ _.extend(Pow.prototype, {
|
|
|
1704
1884
|
}
|
|
1705
1885
|
},
|
|
1706
1886
|
|
|
1707
|
-
findGCD: function(factor) {
|
|
1887
|
+
findGCD: function (factor) {
|
|
1708
1888
|
var base, exp;
|
|
1709
1889
|
if (factor instanceof Pow) {
|
|
1710
1890
|
base = factor.base;
|
|
@@ -1745,7 +1925,7 @@ _.extend(Pow.prototype, {
|
|
|
1745
1925
|
return Num.One;
|
|
1746
1926
|
},
|
|
1747
1927
|
|
|
1748
|
-
isPositive: function() {
|
|
1928
|
+
isPositive: function () {
|
|
1749
1929
|
if (this.base.isPositive()) {
|
|
1750
1930
|
return true;
|
|
1751
1931
|
}
|
|
@@ -1754,7 +1934,7 @@ _.extend(Pow.prototype, {
|
|
|
1754
1934
|
return exp instanceof Int && exp.eval() % 2 === 0;
|
|
1755
1935
|
},
|
|
1756
1936
|
|
|
1757
|
-
asPositiveFactor: function() {
|
|
1937
|
+
asPositiveFactor: function () {
|
|
1758
1938
|
if (this.isPositive()) {
|
|
1759
1939
|
return this;
|
|
1760
1940
|
} else {
|
|
@@ -1763,23 +1943,23 @@ _.extend(Pow.prototype, {
|
|
|
1763
1943
|
var n = exp.eval();
|
|
1764
1944
|
if (n > 2) {
|
|
1765
1945
|
// e.g. x^3 -> x^2
|
|
1766
|
-
return new Pow(this.base, new Int(n-1));
|
|
1946
|
+
return new Pow(this.base, new Int(n - 1));
|
|
1767
1947
|
} else if (n < -2) {
|
|
1768
1948
|
// e.g. x^-3 -> x^-2
|
|
1769
|
-
return new Pow(this.base, new Int(n+1));
|
|
1949
|
+
return new Pow(this.base, new Int(n + 1));
|
|
1770
1950
|
}
|
|
1771
1951
|
}
|
|
1772
1952
|
return Num.One;
|
|
1773
1953
|
}
|
|
1774
|
-
}
|
|
1954
|
+
},
|
|
1775
1955
|
});
|
|
1776
1956
|
|
|
1777
1957
|
_.extend(Pow, {
|
|
1778
|
-
sqrt: function(arg) {
|
|
1958
|
+
sqrt: function (arg) {
|
|
1779
1959
|
return new Pow(arg, Num.Sqrt);
|
|
1780
1960
|
},
|
|
1781
1961
|
|
|
1782
|
-
nthroot: function(radicand, degree) {
|
|
1962
|
+
nthroot: function (radicand, degree) {
|
|
1783
1963
|
var exp = Mul.fold(Mul.handleDivide(new Int(1), degree));
|
|
1784
1964
|
|
|
1785
1965
|
// FIXME(johnsullivan): If oneOverDegree ends up being a pow object,
|
|
@@ -1788,25 +1968,37 @@ _.extend(Pow, {
|
|
|
1788
1968
|
},
|
|
1789
1969
|
});
|
|
1790
1970
|
|
|
1791
|
-
|
|
1792
1971
|
/* logarithm */
|
|
1793
|
-
export function Log(base, power) {
|
|
1972
|
+
export function Log(base, power) {
|
|
1973
|
+
this.base = base;
|
|
1974
|
+
this.power = power;
|
|
1975
|
+
}
|
|
1794
1976
|
Log.prototype = new Expr();
|
|
1795
1977
|
|
|
1796
1978
|
_.extend(Log.prototype, {
|
|
1797
1979
|
func: Log,
|
|
1798
|
-
args: function() {
|
|
1980
|
+
args: function () {
|
|
1981
|
+
return [this.base, this.power];
|
|
1982
|
+
},
|
|
1799
1983
|
|
|
1800
|
-
eval: function(vars, options) {
|
|
1801
|
-
return
|
|
1984
|
+
eval: function (vars, options) {
|
|
1985
|
+
return (
|
|
1986
|
+
Math.log(this.power.eval(vars, options)) /
|
|
1987
|
+
Math.log(this.base.eval(vars, options))
|
|
1988
|
+
);
|
|
1802
1989
|
},
|
|
1803
1990
|
|
|
1804
|
-
codegen: function() {
|
|
1805
|
-
return
|
|
1806
|
-
"
|
|
1991
|
+
codegen: function () {
|
|
1992
|
+
return (
|
|
1993
|
+
"(Math.log(" +
|
|
1994
|
+
this.power.codegen() +
|
|
1995
|
+
") / Math.log(" +
|
|
1996
|
+
this.base.codegen() +
|
|
1997
|
+
"))"
|
|
1998
|
+
);
|
|
1807
1999
|
},
|
|
1808
2000
|
|
|
1809
|
-
print: function() {
|
|
2001
|
+
print: function () {
|
|
1810
2002
|
var power = "(" + this.power.print() + ")";
|
|
1811
2003
|
if (this.isNatural()) {
|
|
1812
2004
|
return "ln" + power;
|
|
@@ -1815,7 +2007,7 @@ _.extend(Log.prototype, {
|
|
|
1815
2007
|
}
|
|
1816
2008
|
},
|
|
1817
2009
|
|
|
1818
|
-
tex: function() {
|
|
2010
|
+
tex: function () {
|
|
1819
2011
|
var power = "(" + this.power.tex() + ")";
|
|
1820
2012
|
if (this.isNatural()) {
|
|
1821
2013
|
return "\\ln" + power;
|
|
@@ -1824,22 +2016,19 @@ _.extend(Log.prototype, {
|
|
|
1824
2016
|
}
|
|
1825
2017
|
},
|
|
1826
2018
|
|
|
1827
|
-
collect: function(options) {
|
|
2019
|
+
collect: function (options) {
|
|
1828
2020
|
var log = this.recurse("collect", options);
|
|
1829
2021
|
|
|
1830
2022
|
if (log.power instanceof Num && log.power.eval() === 1) {
|
|
1831
|
-
|
|
1832
2023
|
// e.g. ln(1) -> 0
|
|
1833
2024
|
return Num.Zero;
|
|
1834
|
-
|
|
1835
2025
|
} else if (log.base.equals(log.power)) {
|
|
1836
|
-
|
|
1837
2026
|
// e.g. log_b(b) -> 1
|
|
1838
2027
|
return Num.One;
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
log.power.base.equals(log.base)
|
|
1842
|
-
|
|
2028
|
+
} else if (
|
|
2029
|
+
log.power instanceof Pow &&
|
|
2030
|
+
log.power.base.equals(log.base)
|
|
2031
|
+
) {
|
|
1843
2032
|
// e.g. log_b(b^x) -> x
|
|
1844
2033
|
return log.power.exp;
|
|
1845
2034
|
} else {
|
|
@@ -1847,242 +2036,339 @@ _.extend(Log.prototype, {
|
|
|
1847
2036
|
}
|
|
1848
2037
|
},
|
|
1849
2038
|
|
|
1850
|
-
expand: function() {
|
|
2039
|
+
expand: function () {
|
|
1851
2040
|
var log = this.recurse("expand");
|
|
1852
2041
|
|
|
1853
|
-
if (log.power instanceof Mul) {
|
|
2042
|
+
if (log.power instanceof Mul) {
|
|
2043
|
+
// might want behind super-simplify() flag
|
|
1854
2044
|
// e.g. ln(xy) -> ln(x) + ln(y)
|
|
1855
2045
|
|
|
1856
|
-
var terms = _.map(log.power.terms, function(term) {
|
|
2046
|
+
var terms = _.map(log.power.terms, function (term) {
|
|
1857
2047
|
// need to expand again in case new log powers are Pows
|
|
1858
2048
|
return new Log(log.base, term).expand();
|
|
1859
2049
|
});
|
|
1860
2050
|
|
|
1861
2051
|
return new Add(terms);
|
|
1862
|
-
|
|
1863
2052
|
} else if (log.power instanceof Pow) {
|
|
1864
2053
|
// e.g. ln(x^y) -> y*ln(x)
|
|
1865
2054
|
|
|
1866
|
-
return new Mul(
|
|
2055
|
+
return new Mul(
|
|
2056
|
+
log.power.exp,
|
|
2057
|
+
new Log(log.base, log.power.base).expand(),
|
|
2058
|
+
).flatten();
|
|
1867
2059
|
} else if (!log.isNatural()) {
|
|
1868
2060
|
// e.g. log_b(x) -> ln(x)/ln(b)
|
|
1869
2061
|
|
|
1870
|
-
return Mul.handleDivide(
|
|
2062
|
+
return Mul.handleDivide(
|
|
2063
|
+
new Log(Const.e, log.power),
|
|
2064
|
+
new Log(Const.e, log.base),
|
|
2065
|
+
);
|
|
1871
2066
|
} else {
|
|
1872
2067
|
return log;
|
|
1873
2068
|
}
|
|
1874
2069
|
},
|
|
1875
2070
|
|
|
1876
2071
|
hints: _.extend(Log.prototype.hints, {
|
|
1877
|
-
open: false
|
|
2072
|
+
open: false,
|
|
1878
2073
|
}),
|
|
1879
2074
|
|
|
1880
|
-
isPositive: function() {
|
|
2075
|
+
isPositive: function () {
|
|
1881
2076
|
var log = this.collect();
|
|
1882
2077
|
|
|
1883
|
-
if (log.base instanceof Num &&
|
|
1884
|
-
log.power instanceof Num) {
|
|
2078
|
+
if (log.base instanceof Num && log.power instanceof Num) {
|
|
1885
2079
|
return this.eval() > 0;
|
|
1886
2080
|
} else {
|
|
1887
2081
|
return false;
|
|
1888
2082
|
}
|
|
1889
2083
|
},
|
|
1890
2084
|
|
|
1891
|
-
needsExplicitMul: function() {
|
|
2085
|
+
needsExplicitMul: function () {
|
|
2086
|
+
return false;
|
|
2087
|
+
},
|
|
1892
2088
|
|
|
1893
|
-
isNatural: function() {
|
|
2089
|
+
isNatural: function () {
|
|
2090
|
+
return this.base.equals(Const.e);
|
|
2091
|
+
},
|
|
1894
2092
|
});
|
|
1895
2093
|
|
|
1896
2094
|
_.extend(Log, {
|
|
1897
|
-
natural: function() {
|
|
1898
|
-
|
|
2095
|
+
natural: function () {
|
|
2096
|
+
return Const.e;
|
|
2097
|
+
},
|
|
2098
|
+
common: function () {
|
|
2099
|
+
return Num.Ten;
|
|
2100
|
+
},
|
|
1899
2101
|
|
|
1900
|
-
create: function(base, power) {
|
|
2102
|
+
create: function (base, power) {
|
|
1901
2103
|
var log = new Log(base, power);
|
|
1902
2104
|
if (!power.hints.parens) {
|
|
1903
2105
|
log = log.addHint("open");
|
|
1904
2106
|
}
|
|
1905
2107
|
return log;
|
|
1906
|
-
}
|
|
2108
|
+
},
|
|
1907
2109
|
});
|
|
1908
2110
|
|
|
1909
|
-
|
|
1910
2111
|
/* trigonometric functions */
|
|
1911
|
-
export function Trig(type, arg) {
|
|
2112
|
+
export function Trig(type, arg) {
|
|
2113
|
+
this.type = type;
|
|
2114
|
+
this.arg = arg;
|
|
2115
|
+
}
|
|
1912
2116
|
Trig.prototype = new Expr();
|
|
1913
2117
|
|
|
1914
2118
|
_.extend(Trig.prototype, {
|
|
1915
2119
|
func: Trig,
|
|
1916
|
-
args: function() {
|
|
2120
|
+
args: function () {
|
|
2121
|
+
return [this.type, this.arg];
|
|
2122
|
+
},
|
|
1917
2123
|
|
|
1918
2124
|
functions: {
|
|
1919
2125
|
sin: {
|
|
1920
2126
|
eval: Math.sin,
|
|
1921
2127
|
codegen: "Math.sin((",
|
|
1922
2128
|
tex: "\\sin",
|
|
1923
|
-
expand: function() {
|
|
2129
|
+
expand: function () {
|
|
2130
|
+
return this;
|
|
2131
|
+
},
|
|
1924
2132
|
},
|
|
1925
2133
|
cos: {
|
|
1926
2134
|
eval: Math.cos,
|
|
1927
2135
|
codegen: "Math.cos((",
|
|
1928
2136
|
tex: "\\cos",
|
|
1929
|
-
expand: function() {
|
|
2137
|
+
expand: function () {
|
|
2138
|
+
return this;
|
|
2139
|
+
},
|
|
1930
2140
|
},
|
|
1931
2141
|
tan: {
|
|
1932
2142
|
eval: Math.tan,
|
|
1933
2143
|
codegen: "Math.tan((",
|
|
1934
2144
|
tex: "\\tan",
|
|
1935
|
-
expand: function() {
|
|
2145
|
+
expand: function () {
|
|
1936
2146
|
return Mul.handleDivide(Trig.sin(this.arg), Trig.cos(this.arg));
|
|
1937
|
-
}
|
|
2147
|
+
},
|
|
1938
2148
|
},
|
|
1939
2149
|
csc: {
|
|
1940
|
-
eval: function(arg) {
|
|
2150
|
+
eval: function (arg) {
|
|
2151
|
+
return 1 / Math.sin(arg);
|
|
2152
|
+
},
|
|
1941
2153
|
codegen: "(1/Math.sin(",
|
|
1942
2154
|
tex: "\\csc",
|
|
1943
|
-
expand: function() {
|
|
2155
|
+
expand: function () {
|
|
1944
2156
|
return Mul.handleDivide(Num.One, Trig.sin(this.arg));
|
|
1945
|
-
}
|
|
2157
|
+
},
|
|
1946
2158
|
},
|
|
1947
2159
|
sec: {
|
|
1948
|
-
eval: function(arg) {
|
|
2160
|
+
eval: function (arg) {
|
|
2161
|
+
return 1 / Math.cos(arg);
|
|
2162
|
+
},
|
|
1949
2163
|
codegen: "(1/Math.cos(",
|
|
1950
2164
|
tex: "\\sec",
|
|
1951
|
-
expand: function() {
|
|
2165
|
+
expand: function () {
|
|
1952
2166
|
return Mul.handleDivide(Num.One, Trig.cos(this.arg));
|
|
1953
|
-
}
|
|
2167
|
+
},
|
|
1954
2168
|
},
|
|
1955
2169
|
cot: {
|
|
1956
|
-
eval: function(arg) {
|
|
2170
|
+
eval: function (arg) {
|
|
2171
|
+
return 1 / Math.tan(arg);
|
|
2172
|
+
},
|
|
1957
2173
|
codegen: "(1/Math.tan(",
|
|
1958
2174
|
tex: "\\cot",
|
|
1959
|
-
expand: function() {
|
|
2175
|
+
expand: function () {
|
|
1960
2176
|
return Mul.handleDivide(Trig.cos(this.arg), Trig.sin(this.arg));
|
|
1961
|
-
}
|
|
2177
|
+
},
|
|
1962
2178
|
},
|
|
1963
2179
|
arcsin: {
|
|
1964
2180
|
eval: Math.asin,
|
|
1965
2181
|
codegen: "Math.asin((",
|
|
1966
|
-
tex: "\\arcsin"
|
|
2182
|
+
tex: "\\arcsin",
|
|
1967
2183
|
},
|
|
1968
2184
|
arccos: {
|
|
1969
2185
|
eval: Math.acos,
|
|
1970
2186
|
codegen: "Math.acos((",
|
|
1971
|
-
tex: "\\arccos"
|
|
2187
|
+
tex: "\\arccos",
|
|
1972
2188
|
},
|
|
1973
2189
|
arctan: {
|
|
1974
2190
|
eval: Math.atan,
|
|
1975
2191
|
codegen: "Math.atan((",
|
|
1976
|
-
tex: "\\arctan"
|
|
2192
|
+
tex: "\\arctan",
|
|
1977
2193
|
},
|
|
1978
2194
|
arccsc: {
|
|
1979
|
-
eval: function(arg) {
|
|
2195
|
+
eval: function (arg) {
|
|
2196
|
+
return Math.asin(1 / arg);
|
|
2197
|
+
},
|
|
1980
2198
|
codegen: "Math.asin(1/(",
|
|
1981
|
-
tex: "\\operatorname{arccsc}"
|
|
2199
|
+
tex: "\\operatorname{arccsc}",
|
|
1982
2200
|
},
|
|
1983
2201
|
arcsec: {
|
|
1984
|
-
eval: function(arg) {
|
|
2202
|
+
eval: function (arg) {
|
|
2203
|
+
return Math.acos(1 / arg);
|
|
2204
|
+
},
|
|
1985
2205
|
codegen: "Math.acos(1/(",
|
|
1986
|
-
tex: "\\operatorname{arcsec}"
|
|
2206
|
+
tex: "\\operatorname{arcsec}",
|
|
1987
2207
|
},
|
|
1988
2208
|
arccot: {
|
|
1989
|
-
eval: function(arg) {
|
|
2209
|
+
eval: function (arg) {
|
|
2210
|
+
return Math.atan(1 / arg);
|
|
2211
|
+
},
|
|
1990
2212
|
codegen: "Math.atan(1/(",
|
|
1991
|
-
tex: "\\operatorname{arccot}"
|
|
2213
|
+
tex: "\\operatorname{arccot}",
|
|
1992
2214
|
},
|
|
1993
2215
|
sinh: {
|
|
1994
|
-
eval: function(arg) {
|
|
2216
|
+
eval: function (arg) {
|
|
1995
2217
|
return (Math.exp(arg) - Math.exp(-arg)) / 2;
|
|
1996
2218
|
},
|
|
1997
|
-
codegen: function(argStr) {
|
|
1998
|
-
return
|
|
2219
|
+
codegen: function (argStr) {
|
|
2220
|
+
return (
|
|
2221
|
+
"((Math.exp(" +
|
|
2222
|
+
argStr +
|
|
2223
|
+
") - Math.exp(-(" +
|
|
2224
|
+
argStr +
|
|
2225
|
+
"))) / 2)"
|
|
2226
|
+
);
|
|
1999
2227
|
},
|
|
2000
2228
|
tex: "\\sinh",
|
|
2001
|
-
expand: function() {
|
|
2229
|
+
expand: function () {
|
|
2230
|
+
return this;
|
|
2231
|
+
},
|
|
2002
2232
|
},
|
|
2003
2233
|
cosh: {
|
|
2004
|
-
eval: function(arg) {
|
|
2234
|
+
eval: function (arg) {
|
|
2005
2235
|
return (Math.exp(arg) + Math.exp(-arg)) / 2;
|
|
2006
2236
|
},
|
|
2007
|
-
codegen: function(argStr) {
|
|
2008
|
-
return
|
|
2237
|
+
codegen: function (argStr) {
|
|
2238
|
+
return (
|
|
2239
|
+
"((Math.exp(" +
|
|
2240
|
+
argStr +
|
|
2241
|
+
") + Math.exp(-(" +
|
|
2242
|
+
argStr +
|
|
2243
|
+
"))) / 2)"
|
|
2244
|
+
);
|
|
2009
2245
|
},
|
|
2010
2246
|
tex: "\\cosh",
|
|
2011
|
-
expand: function() {
|
|
2247
|
+
expand: function () {
|
|
2248
|
+
return this;
|
|
2249
|
+
},
|
|
2012
2250
|
},
|
|
2013
2251
|
tanh: {
|
|
2014
|
-
eval: function(arg) {
|
|
2015
|
-
return (
|
|
2252
|
+
eval: function (arg) {
|
|
2253
|
+
return (
|
|
2254
|
+
(Math.exp(arg) - Math.exp(-arg)) /
|
|
2255
|
+
(Math.exp(arg) + Math.exp(-arg))
|
|
2256
|
+
);
|
|
2016
2257
|
},
|
|
2017
|
-
codegen: function(argStr) {
|
|
2018
|
-
return
|
|
2019
|
-
"(
|
|
2258
|
+
codegen: function (argStr) {
|
|
2259
|
+
return (
|
|
2260
|
+
"(" +
|
|
2261
|
+
"(Math.exp(" +
|
|
2262
|
+
argStr +
|
|
2263
|
+
") - Math.exp(-(" +
|
|
2264
|
+
argStr +
|
|
2265
|
+
")))" +
|
|
2020
2266
|
" / " +
|
|
2021
|
-
"(Math.exp(" +
|
|
2022
|
-
|
|
2267
|
+
"(Math.exp(" +
|
|
2268
|
+
argStr +
|
|
2269
|
+
") + Math.exp(-(" +
|
|
2270
|
+
argStr +
|
|
2271
|
+
")))" +
|
|
2272
|
+
")"
|
|
2273
|
+
);
|
|
2023
2274
|
},
|
|
2024
2275
|
tex: "\\tanh",
|
|
2025
|
-
expand: function() {
|
|
2026
|
-
return Mul.handleDivide(
|
|
2027
|
-
|
|
2276
|
+
expand: function () {
|
|
2277
|
+
return Mul.handleDivide(
|
|
2278
|
+
Trig.sinh(this.arg),
|
|
2279
|
+
Trig.cosh(this.arg),
|
|
2280
|
+
);
|
|
2281
|
+
},
|
|
2028
2282
|
},
|
|
2029
2283
|
csch: {
|
|
2030
|
-
eval: function(arg) {
|
|
2031
|
-
|
|
2032
|
-
|
|
2284
|
+
eval: function (arg) {
|
|
2285
|
+
return 2 / (Math.exp(arg) - Math.exp(-arg));
|
|
2286
|
+
},
|
|
2287
|
+
codegen: function (argStr) {
|
|
2288
|
+
return (
|
|
2289
|
+
"(2 / (Math.exp(" +
|
|
2290
|
+
argStr +
|
|
2291
|
+
") - Math.exp(-(" +
|
|
2292
|
+
argStr +
|
|
2293
|
+
"))))"
|
|
2294
|
+
);
|
|
2033
2295
|
},
|
|
2034
2296
|
tex: "\\csch",
|
|
2035
|
-
expand: function() {
|
|
2297
|
+
expand: function () {
|
|
2036
2298
|
return Mul.handleDivide(Num.One, Trig.sinh(this.arg));
|
|
2037
|
-
}
|
|
2299
|
+
},
|
|
2038
2300
|
},
|
|
2039
2301
|
sech: {
|
|
2040
|
-
eval: function(arg) {
|
|
2041
|
-
|
|
2042
|
-
|
|
2302
|
+
eval: function (arg) {
|
|
2303
|
+
return 2 / (Math.exp(arg) + Math.exp(-arg));
|
|
2304
|
+
},
|
|
2305
|
+
codegen: function (argStr) {
|
|
2306
|
+
return (
|
|
2307
|
+
"(2 / (Math.exp(" +
|
|
2308
|
+
argStr +
|
|
2309
|
+
") + Math.exp(-(" +
|
|
2310
|
+
argStr +
|
|
2311
|
+
"))))"
|
|
2312
|
+
);
|
|
2043
2313
|
},
|
|
2044
2314
|
tex: "\\sech",
|
|
2045
|
-
expand: function() {
|
|
2315
|
+
expand: function () {
|
|
2046
2316
|
return Mul.handleDivide(Num.One, Trig.cosh(this.arg));
|
|
2047
|
-
}
|
|
2317
|
+
},
|
|
2048
2318
|
},
|
|
2049
2319
|
coth: {
|
|
2050
|
-
eval: function(arg) {
|
|
2051
|
-
return (
|
|
2320
|
+
eval: function (arg) {
|
|
2321
|
+
return (
|
|
2322
|
+
(Math.exp(arg) + Math.exp(-arg)) /
|
|
2323
|
+
(Math.exp(arg) - Math.exp(-arg))
|
|
2324
|
+
);
|
|
2052
2325
|
},
|
|
2053
|
-
codegen: function(argStr) {
|
|
2054
|
-
return
|
|
2055
|
-
"(
|
|
2326
|
+
codegen: function (argStr) {
|
|
2327
|
+
return (
|
|
2328
|
+
"(" +
|
|
2329
|
+
"(Math.exp(" +
|
|
2330
|
+
argStr +
|
|
2331
|
+
") + Math.exp(-(" +
|
|
2332
|
+
argStr +
|
|
2333
|
+
")))" +
|
|
2056
2334
|
" / " +
|
|
2057
|
-
"(Math.exp(" +
|
|
2058
|
-
|
|
2335
|
+
"(Math.exp(" +
|
|
2336
|
+
argStr +
|
|
2337
|
+
") - Math.exp(-(" +
|
|
2338
|
+
argStr +
|
|
2339
|
+
")))" +
|
|
2340
|
+
")"
|
|
2341
|
+
);
|
|
2059
2342
|
},
|
|
2060
2343
|
tex: "\\coth",
|
|
2061
|
-
expand: function() {
|
|
2062
|
-
return Mul.handleDivide(
|
|
2063
|
-
|
|
2344
|
+
expand: function () {
|
|
2345
|
+
return Mul.handleDivide(
|
|
2346
|
+
Trig.cosh(this.arg),
|
|
2347
|
+
Trig.sinh(this.arg),
|
|
2348
|
+
);
|
|
2349
|
+
},
|
|
2064
2350
|
},
|
|
2065
2351
|
},
|
|
2066
2352
|
|
|
2067
|
-
isEven: function() {
|
|
2353
|
+
isEven: function () {
|
|
2068
2354
|
return _.contains(["cos", "sec"], this.type);
|
|
2069
2355
|
},
|
|
2070
2356
|
|
|
2071
|
-
isInverse: function() {
|
|
2357
|
+
isInverse: function () {
|
|
2072
2358
|
return this.type.indexOf("arc") === 0;
|
|
2073
2359
|
},
|
|
2074
2360
|
|
|
2075
|
-
isBasic: function() {
|
|
2361
|
+
isBasic: function () {
|
|
2076
2362
|
return _.contains(["sin", "cos"], this.type);
|
|
2077
2363
|
},
|
|
2078
2364
|
|
|
2079
|
-
eval: function(vars, options) {
|
|
2365
|
+
eval: function (vars, options) {
|
|
2080
2366
|
var func = this.functions[this.type].eval;
|
|
2081
2367
|
var arg = this.arg.eval(vars, options);
|
|
2082
2368
|
return func(arg);
|
|
2083
2369
|
},
|
|
2084
2370
|
|
|
2085
|
-
codegen: function() {
|
|
2371
|
+
codegen: function () {
|
|
2086
2372
|
var func = this.functions[this.type].codegen;
|
|
2087
2373
|
if (typeof func === "function") {
|
|
2088
2374
|
return func(this.arg.codegen());
|
|
@@ -2093,21 +2379,21 @@ _.extend(Trig.prototype, {
|
|
|
2093
2379
|
}
|
|
2094
2380
|
},
|
|
2095
2381
|
|
|
2096
|
-
print: function() {
|
|
2382
|
+
print: function () {
|
|
2097
2383
|
return this.type + "(" + this.arg.print() + ")";
|
|
2098
2384
|
},
|
|
2099
2385
|
|
|
2100
|
-
tex: function(options) {
|
|
2386
|
+
tex: function (options) {
|
|
2101
2387
|
var func = this.functions[this.type].tex;
|
|
2102
2388
|
var arg = "(" + this.arg.tex() + ")";
|
|
2103
|
-
return
|
|
2389
|
+
return options && options.split ? [func, arg] : func + arg;
|
|
2104
2390
|
},
|
|
2105
2391
|
|
|
2106
2392
|
hints: _.extend(Trig.prototype.hints, {
|
|
2107
|
-
open: false
|
|
2393
|
+
open: false,
|
|
2108
2394
|
}),
|
|
2109
2395
|
|
|
2110
|
-
isPositive: function() {
|
|
2396
|
+
isPositive: function () {
|
|
2111
2397
|
var trig = this.collect();
|
|
2112
2398
|
|
|
2113
2399
|
if (trig.arg instanceof Num) {
|
|
@@ -2117,7 +2403,7 @@ _.extend(Trig.prototype, {
|
|
|
2117
2403
|
}
|
|
2118
2404
|
},
|
|
2119
2405
|
|
|
2120
|
-
completeParse: function() {
|
|
2406
|
+
completeParse: function () {
|
|
2121
2407
|
if (this.exp) {
|
|
2122
2408
|
var pow = new Pow(this, this.exp);
|
|
2123
2409
|
this.exp = undefined;
|
|
@@ -2128,9 +2414,11 @@ _.extend(Trig.prototype, {
|
|
|
2128
2414
|
},
|
|
2129
2415
|
|
|
2130
2416
|
// TODO(alex): does every new node type need to redefine these?
|
|
2131
|
-
needsExplicitMul: function() {
|
|
2417
|
+
needsExplicitMul: function () {
|
|
2418
|
+
return false;
|
|
2419
|
+
},
|
|
2132
2420
|
|
|
2133
|
-
expand: function() {
|
|
2421
|
+
expand: function () {
|
|
2134
2422
|
var trig = this.recurse("expand");
|
|
2135
2423
|
if (!trig.isInverse()) {
|
|
2136
2424
|
// e.g. tan(x) -> sin(x)/cos(x)
|
|
@@ -2141,7 +2429,7 @@ _.extend(Trig.prototype, {
|
|
|
2141
2429
|
}
|
|
2142
2430
|
},
|
|
2143
2431
|
|
|
2144
|
-
collect: function(options) {
|
|
2432
|
+
collect: function (options) {
|
|
2145
2433
|
var trig = this.recurse("collect", options);
|
|
2146
2434
|
if (!trig.isInverse() && trig.arg.isNegative()) {
|
|
2147
2435
|
var arg;
|
|
@@ -2154,7 +2442,6 @@ _.extend(Trig.prototype, {
|
|
|
2154
2442
|
if (trig.isEven()) {
|
|
2155
2443
|
// e.g. cos(-x) -> cos(x)
|
|
2156
2444
|
return new Trig(trig.type, arg);
|
|
2157
|
-
|
|
2158
2445
|
} else {
|
|
2159
2446
|
// e.g. sin(-x) -> -sin(x)
|
|
2160
2447
|
return new Mul(Num.Neg, new Trig(trig.type, arg));
|
|
@@ -2162,11 +2449,11 @@ _.extend(Trig.prototype, {
|
|
|
2162
2449
|
} else {
|
|
2163
2450
|
return trig;
|
|
2164
2451
|
}
|
|
2165
|
-
}
|
|
2452
|
+
},
|
|
2166
2453
|
});
|
|
2167
2454
|
|
|
2168
2455
|
_.extend(Trig, {
|
|
2169
|
-
create: function(pair, arg) {
|
|
2456
|
+
create: function (pair, arg) {
|
|
2170
2457
|
var type = pair[0];
|
|
2171
2458
|
var exp = pair[1];
|
|
2172
2459
|
|
|
@@ -2188,39 +2475,48 @@ _.extend(Trig, {
|
|
|
2188
2475
|
return trig;
|
|
2189
2476
|
},
|
|
2190
2477
|
|
|
2191
|
-
sin: function(arg) {
|
|
2478
|
+
sin: function (arg) {
|
|
2192
2479
|
return new Trig("sin", arg);
|
|
2193
2480
|
},
|
|
2194
2481
|
|
|
2195
|
-
cos: function(arg) {
|
|
2482
|
+
cos: function (arg) {
|
|
2196
2483
|
return new Trig("cos", arg);
|
|
2197
2484
|
},
|
|
2198
2485
|
|
|
2199
|
-
sinh: function(arg) {
|
|
2486
|
+
sinh: function (arg) {
|
|
2200
2487
|
return new Trig("sinh", arg);
|
|
2201
2488
|
},
|
|
2202
2489
|
|
|
2203
|
-
cosh: function(arg) {
|
|
2490
|
+
cosh: function (arg) {
|
|
2204
2491
|
return new Trig("cosh", arg);
|
|
2205
|
-
}
|
|
2492
|
+
},
|
|
2206
2493
|
});
|
|
2207
2494
|
|
|
2208
|
-
|
|
2209
|
-
|
|
2495
|
+
export function Abs(arg) {
|
|
2496
|
+
this.arg = arg;
|
|
2497
|
+
}
|
|
2210
2498
|
Abs.prototype = new Expr();
|
|
2211
2499
|
|
|
2212
2500
|
_.extend(Abs.prototype, {
|
|
2213
2501
|
func: Abs,
|
|
2214
|
-
args: function() {
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2502
|
+
args: function () {
|
|
2503
|
+
return [this.arg];
|
|
2504
|
+
},
|
|
2505
|
+
eval: function (vars, options) {
|
|
2506
|
+
return Math.abs(this.arg.eval(vars, options));
|
|
2507
|
+
},
|
|
2508
|
+
codegen: function () {
|
|
2509
|
+
return "Math.abs(" + this.arg.codegen() + ")";
|
|
2510
|
+
},
|
|
2511
|
+
print: function () {
|
|
2512
|
+
return "abs(" + this.arg.print() + ")";
|
|
2513
|
+
},
|
|
2218
2514
|
|
|
2219
|
-
tex: function() {
|
|
2515
|
+
tex: function () {
|
|
2220
2516
|
return "\\left|" + this.arg.tex() + "\\right|";
|
|
2221
2517
|
},
|
|
2222
2518
|
|
|
2223
|
-
collect: function(options) {
|
|
2519
|
+
collect: function (options) {
|
|
2224
2520
|
var abs = this.recurse("collect", options);
|
|
2225
2521
|
|
|
2226
2522
|
if (abs.arg.isPositive()) {
|
|
@@ -2231,7 +2527,7 @@ _.extend(Abs.prototype, {
|
|
|
2231
2527
|
return abs.arg.abs();
|
|
2232
2528
|
} else if (abs.arg instanceof Mul) {
|
|
2233
2529
|
// e.g. |-2*pi*x| -> 2*pi*|x|
|
|
2234
|
-
var terms = _.groupBy(abs.arg.terms, function(term) {
|
|
2530
|
+
var terms = _.groupBy(abs.arg.terms, function (term) {
|
|
2235
2531
|
if (term.isPositive()) {
|
|
2236
2532
|
return "positive";
|
|
2237
2533
|
} else if (term instanceof Num) {
|
|
@@ -2241,7 +2537,9 @@ _.extend(Abs.prototype, {
|
|
|
2241
2537
|
}
|
|
2242
2538
|
});
|
|
2243
2539
|
|
|
2244
|
-
var positives = terms.positive.concat(
|
|
2540
|
+
var positives = terms.positive.concat(
|
|
2541
|
+
_.invoke(terms.number, "abs"),
|
|
2542
|
+
);
|
|
2245
2543
|
|
|
2246
2544
|
if (terms.other.length) {
|
|
2247
2545
|
positives.push(new Abs(new Mul(terms.other).flatten()));
|
|
@@ -2254,12 +2552,12 @@ _.extend(Abs.prototype, {
|
|
|
2254
2552
|
},
|
|
2255
2553
|
|
|
2256
2554
|
// this should definitely be behind a super-simplify flag
|
|
2257
|
-
expand: function() {
|
|
2555
|
+
expand: function () {
|
|
2258
2556
|
var abs = this.recurse("expand");
|
|
2259
2557
|
|
|
2260
2558
|
if (abs.arg instanceof Mul) {
|
|
2261
2559
|
// e.g. |xyz| -> |x|*|y|*|z|
|
|
2262
|
-
var terms = _.map(abs.arg.terms, function(term) {
|
|
2560
|
+
var terms = _.map(abs.arg.terms, function (term) {
|
|
2263
2561
|
return new Abs(term);
|
|
2264
2562
|
});
|
|
2265
2563
|
return new Mul(terms);
|
|
@@ -2268,10 +2566,11 @@ _.extend(Abs.prototype, {
|
|
|
2268
2566
|
}
|
|
2269
2567
|
},
|
|
2270
2568
|
|
|
2271
|
-
isPositive: function() {
|
|
2569
|
+
isPositive: function () {
|
|
2570
|
+
return true;
|
|
2571
|
+
},
|
|
2272
2572
|
});
|
|
2273
2573
|
|
|
2274
|
-
|
|
2275
2574
|
/* equation */
|
|
2276
2575
|
export function Eq(left, type, right) {
|
|
2277
2576
|
this.left = left;
|
|
@@ -2282,11 +2581,15 @@ Eq.prototype = new Expr();
|
|
|
2282
2581
|
|
|
2283
2582
|
_.extend(Eq.prototype, {
|
|
2284
2583
|
func: Eq,
|
|
2285
|
-
args: function() {
|
|
2584
|
+
args: function () {
|
|
2585
|
+
return [this.left, this.type, this.right];
|
|
2586
|
+
},
|
|
2286
2587
|
|
|
2287
|
-
needsExplicitMul: function() {
|
|
2588
|
+
needsExplicitMul: function () {
|
|
2589
|
+
return false;
|
|
2590
|
+
},
|
|
2288
2591
|
|
|
2289
|
-
print: function() {
|
|
2592
|
+
print: function () {
|
|
2290
2593
|
return this.left.print() + this.type + this.right.print();
|
|
2291
2594
|
},
|
|
2292
2595
|
|
|
@@ -2296,14 +2599,14 @@ _.extend(Eq.prototype, {
|
|
|
2296
2599
|
">": " > ",
|
|
2297
2600
|
"<>": " \\ne ",
|
|
2298
2601
|
"<=": " \\le ",
|
|
2299
|
-
">=": " \\ge "
|
|
2602
|
+
">=": " \\ge ",
|
|
2300
2603
|
},
|
|
2301
2604
|
|
|
2302
|
-
tex: function() {
|
|
2605
|
+
tex: function () {
|
|
2303
2606
|
return this.left.tex() + this.signs[this.type] + this.right.tex();
|
|
2304
2607
|
},
|
|
2305
2608
|
|
|
2306
|
-
normalize: function() {
|
|
2609
|
+
normalize: function () {
|
|
2307
2610
|
var eq = this.recurse("normalize");
|
|
2308
2611
|
|
|
2309
2612
|
if (_.contains([">", ">="], eq.type)) {
|
|
@@ -2318,8 +2621,8 @@ _.extend(Eq.prototype, {
|
|
|
2318
2621
|
// the expression is normalized to a canonical form
|
|
2319
2622
|
// e.g. y/2=x/4 -> y/2-x/4(=0) -> 2y-x(=0)
|
|
2320
2623
|
// unless unfactored is specified, will then divide through
|
|
2321
|
-
asExpr: function(unfactored) {
|
|
2322
|
-
var isZero = function(expr) {
|
|
2624
|
+
asExpr: function (unfactored) {
|
|
2625
|
+
var isZero = function (expr) {
|
|
2323
2626
|
return expr instanceof Num && expr.isSimple() && expr.eval() === 0;
|
|
2324
2627
|
};
|
|
2325
2628
|
|
|
@@ -2357,10 +2660,10 @@ _.extend(Eq.prototype, {
|
|
|
2357
2660
|
}
|
|
2358
2661
|
|
|
2359
2662
|
if (!denominator.equals(Num.One)) {
|
|
2360
|
-
terms = _.map(terms, function(term) {
|
|
2663
|
+
terms = _.map(terms, function (term) {
|
|
2361
2664
|
return Mul.createOrAppend(term, denominator).simplify({
|
|
2362
2665
|
once: true,
|
|
2363
|
-
preciseFloats: true
|
|
2666
|
+
preciseFloats: true,
|
|
2364
2667
|
});
|
|
2365
2668
|
});
|
|
2366
2669
|
}
|
|
@@ -2374,7 +2677,7 @@ _.extend(Eq.prototype, {
|
|
|
2374
2677
|
// e.g. 2y-4x(=0) -> y-2x(=0)
|
|
2375
2678
|
// TODO(alex): Make it an option to only divide by variables/expressions
|
|
2376
2679
|
// guaranteed to be nonzero
|
|
2377
|
-
divideThrough: function(expr) {
|
|
2680
|
+
divideThrough: function (expr) {
|
|
2378
2681
|
var isInequality = !this.isEquality();
|
|
2379
2682
|
|
|
2380
2683
|
var simplified = expr.simplify({once: true});
|
|
@@ -2386,9 +2689,15 @@ _.extend(Eq.prototype, {
|
|
|
2386
2689
|
|
|
2387
2690
|
var terms = factored.terms;
|
|
2388
2691
|
|
|
2389
|
-
var isAdd = function(term) {
|
|
2390
|
-
|
|
2391
|
-
|
|
2692
|
+
var isAdd = function (term) {
|
|
2693
|
+
return term instanceof Add;
|
|
2694
|
+
};
|
|
2695
|
+
var hasVar = function (term) {
|
|
2696
|
+
return !!term.getVars().length;
|
|
2697
|
+
};
|
|
2698
|
+
var isOne = function (term) {
|
|
2699
|
+
return term.equals(Num.One);
|
|
2700
|
+
};
|
|
2392
2701
|
|
|
2393
2702
|
var grouped = _.groupBy(terms, isAdd);
|
|
2394
2703
|
var adds = grouped[true] || [];
|
|
@@ -2417,7 +2726,7 @@ _.extend(Eq.prototype, {
|
|
|
2417
2726
|
// don't need to divide by one
|
|
2418
2727
|
denominator = _.reject(denominator, isOne);
|
|
2419
2728
|
|
|
2420
|
-
denominator = _.map(denominator, function(term) {
|
|
2729
|
+
denominator = _.map(denominator, function (term) {
|
|
2421
2730
|
return new Pow(term, Num.Div);
|
|
2422
2731
|
});
|
|
2423
2732
|
|
|
@@ -2432,11 +2741,11 @@ _.extend(Eq.prototype, {
|
|
|
2432
2741
|
}
|
|
2433
2742
|
},
|
|
2434
2743
|
|
|
2435
|
-
isEquality: function() {
|
|
2744
|
+
isEquality: function () {
|
|
2436
2745
|
return _.contains(["=", "<>"], this.type);
|
|
2437
2746
|
},
|
|
2438
2747
|
|
|
2439
|
-
compare: function(other) {
|
|
2748
|
+
compare: function (other) {
|
|
2440
2749
|
// expression comparisons are handled by Expr.compare()
|
|
2441
2750
|
if (!(other instanceof Eq)) {
|
|
2442
2751
|
return false;
|
|
@@ -2451,20 +2760,25 @@ _.extend(Eq.prototype, {
|
|
|
2451
2760
|
|
|
2452
2761
|
// need to collect to properly factor out common factors
|
|
2453
2762
|
// e.g x+2x=6 -> 3x=6 -> 3x-6(=0) -> x-2(=0)
|
|
2454
|
-
var expr1 = eq1.divideThrough(
|
|
2455
|
-
|
|
2763
|
+
var expr1 = eq1.divideThrough(
|
|
2764
|
+
eq1.asExpr(/* unfactored */ true).collect(),
|
|
2765
|
+
);
|
|
2766
|
+
var expr2 = eq2.divideThrough(
|
|
2767
|
+
eq2.asExpr(/* unfactored */ true).collect(),
|
|
2768
|
+
);
|
|
2456
2769
|
|
|
2457
2770
|
if (eq1.isEquality()) {
|
|
2458
2771
|
// equals and not-equals can be subtracted either way
|
|
2459
|
-
return
|
|
2460
|
-
|
|
2772
|
+
return (
|
|
2773
|
+
expr1.compare(expr2) || expr1.compare(Mul.handleNegative(expr2))
|
|
2774
|
+
);
|
|
2461
2775
|
} else {
|
|
2462
2776
|
return expr1.compare(expr2);
|
|
2463
2777
|
}
|
|
2464
2778
|
},
|
|
2465
2779
|
|
|
2466
2780
|
// should only be done after compare() returns true to avoid false positives
|
|
2467
|
-
sameForm: function(other) {
|
|
2781
|
+
sameForm: function (other) {
|
|
2468
2782
|
var eq1 = this.normalize();
|
|
2469
2783
|
var eq2 = other.normalize();
|
|
2470
2784
|
|
|
@@ -2472,7 +2786,10 @@ _.extend(Eq.prototype, {
|
|
|
2472
2786
|
|
|
2473
2787
|
if (eq1.isEquality()) {
|
|
2474
2788
|
// equals and not-equals can be commutative with respect to the sign
|
|
2475
|
-
return
|
|
2789
|
+
return (
|
|
2790
|
+
same ||
|
|
2791
|
+
(eq1.left.sameForm(eq2.right) && eq1.right.sameForm(eq2.left))
|
|
2792
|
+
);
|
|
2476
2793
|
} else {
|
|
2477
2794
|
return same;
|
|
2478
2795
|
}
|
|
@@ -2480,25 +2797,29 @@ _.extend(Eq.prototype, {
|
|
|
2480
2797
|
|
|
2481
2798
|
// we don't want to override collect because it would turn y=x into y-x(=0)
|
|
2482
2799
|
// instead, we ask if the equation was in that form, would it be simplified?
|
|
2483
|
-
isSimplified: function() {
|
|
2800
|
+
isSimplified: function () {
|
|
2484
2801
|
var expr = this.asExpr(/* unfactored */ true);
|
|
2485
2802
|
var simplified = this.divideThrough(expr).simplify();
|
|
2486
|
-
return
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2803
|
+
return (
|
|
2804
|
+
expr.equals(simplified) &&
|
|
2805
|
+
this.left.isSimplified() &&
|
|
2806
|
+
this.right.isSimplified()
|
|
2807
|
+
);
|
|
2808
|
+
},
|
|
2490
2809
|
});
|
|
2491
2810
|
|
|
2492
2811
|
_.extend(Eq.prototype, {
|
|
2493
2812
|
// Assumptions: Expression is of the form a+bx, and we solve for x
|
|
2494
|
-
solveLinearEquationForVariable: function(variable) {
|
|
2813
|
+
solveLinearEquationForVariable: function (variable) {
|
|
2495
2814
|
var expr = this.asExpr();
|
|
2496
2815
|
if (!expr.is(Add) || expr.terms.length !== 2) {
|
|
2497
|
-
throw new Error(
|
|
2498
|
-
|
|
2816
|
+
throw new Error(
|
|
2817
|
+
"Can only handle linear equations of the form " +
|
|
2818
|
+
"a + bx (= 0)",
|
|
2819
|
+
);
|
|
2499
2820
|
}
|
|
2500
2821
|
|
|
2501
|
-
var hasVar = function(term) {
|
|
2822
|
+
var hasVar = function (term) {
|
|
2502
2823
|
return term.has(Var) && _.contains(term.getVars(), variable.symbol);
|
|
2503
2824
|
};
|
|
2504
2825
|
|
|
@@ -2513,51 +2834,53 @@ _.extend(Eq.prototype, {
|
|
|
2513
2834
|
}
|
|
2514
2835
|
|
|
2515
2836
|
return Mul.handleDivide(a, b).simplify();
|
|
2516
|
-
}
|
|
2837
|
+
},
|
|
2517
2838
|
});
|
|
2518
2839
|
|
|
2519
|
-
|
|
2520
2840
|
/* abstract symbol node */
|
|
2521
2841
|
function Symbol() {}
|
|
2522
2842
|
Symbol.prototype = new Expr();
|
|
2523
2843
|
|
|
2524
2844
|
_.extend(Symbol.prototype, {
|
|
2845
|
+
needsExplicitMul: function () {
|
|
2846
|
+
return false;
|
|
2847
|
+
},
|
|
2525
2848
|
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
findGCD: function(factor) {
|
|
2849
|
+
findGCD: function (factor) {
|
|
2529
2850
|
if (factor instanceof Symbol || factor instanceof Num) {
|
|
2530
2851
|
return this.equals(factor) ? this : Num.One;
|
|
2531
2852
|
} else {
|
|
2532
2853
|
return factor.findGCD(this);
|
|
2533
2854
|
}
|
|
2534
|
-
}
|
|
2855
|
+
},
|
|
2535
2856
|
});
|
|
2536
2857
|
|
|
2537
|
-
|
|
2538
2858
|
/* function variable */
|
|
2539
2859
|
export function Func(symbol, arg) {
|
|
2540
|
-
this.symbol = symbol;
|
|
2860
|
+
this.symbol = symbol;
|
|
2861
|
+
this.arg = arg;
|
|
2541
2862
|
}
|
|
2542
2863
|
Func.prototype = new Symbol();
|
|
2543
2864
|
|
|
2544
2865
|
_.extend(Func.prototype, {
|
|
2545
2866
|
func: Func,
|
|
2546
|
-
args: function() {
|
|
2867
|
+
args: function () {
|
|
2868
|
+
return [this.symbol, this.arg];
|
|
2869
|
+
},
|
|
2547
2870
|
|
|
2548
|
-
print: function() {
|
|
2871
|
+
print: function () {
|
|
2549
2872
|
return this.symbol + "(" + this.arg.print() + ")";
|
|
2550
2873
|
},
|
|
2551
2874
|
|
|
2552
|
-
tex: function() {
|
|
2875
|
+
tex: function () {
|
|
2553
2876
|
return this.symbol + "(" + this.arg.tex() + ")";
|
|
2554
2877
|
},
|
|
2555
2878
|
|
|
2556
|
-
eval: function(vars, options) {
|
|
2879
|
+
eval: function (vars, options) {
|
|
2557
2880
|
var arg = this.arg;
|
|
2558
2881
|
var func = vars[this.symbol];
|
|
2559
2882
|
var newVars = _.extend(_.clone(vars), {
|
|
2560
|
-
x: arg.eval(vars, options)
|
|
2883
|
+
x: arg.eval(vars, options),
|
|
2561
2884
|
});
|
|
2562
2885
|
var parsedFunc = parse(func, options);
|
|
2563
2886
|
if (parsedFunc.parsed) {
|
|
@@ -2567,16 +2890,15 @@ _.extend(Func.prototype, {
|
|
|
2567
2890
|
return parsedFunc;
|
|
2568
2891
|
},
|
|
2569
2892
|
|
|
2570
|
-
codegen: function() {
|
|
2571
|
-
return 'vars["' + this.symbol + '"](' +
|
|
2572
|
-
this.arg.codegen() + ')';
|
|
2893
|
+
codegen: function () {
|
|
2894
|
+
return 'vars["' + this.symbol + '"](' + this.arg.codegen() + ")";
|
|
2573
2895
|
},
|
|
2574
2896
|
|
|
2575
|
-
getUnits: function() {
|
|
2897
|
+
getUnits: function () {
|
|
2576
2898
|
return this.arg.getUnits();
|
|
2577
2899
|
},
|
|
2578
2900
|
|
|
2579
|
-
getVars: function(excludeFunc) {
|
|
2901
|
+
getVars: function (excludeFunc) {
|
|
2580
2902
|
if (excludeFunc) {
|
|
2581
2903
|
return this.arg.getVars();
|
|
2582
2904
|
} else {
|
|
@@ -2584,12 +2906,11 @@ _.extend(Func.prototype, {
|
|
|
2584
2906
|
}
|
|
2585
2907
|
},
|
|
2586
2908
|
|
|
2587
|
-
getConsts: function() {
|
|
2909
|
+
getConsts: function () {
|
|
2588
2910
|
return this.arg.getConsts();
|
|
2589
2911
|
},
|
|
2590
2912
|
});
|
|
2591
2913
|
|
|
2592
|
-
|
|
2593
2914
|
/* variable */
|
|
2594
2915
|
export function Var(symbol, subscript) {
|
|
2595
2916
|
this.symbol = symbol;
|
|
@@ -2599,12 +2920,18 @@ Var.prototype = new Symbol();
|
|
|
2599
2920
|
|
|
2600
2921
|
_.extend(Var.prototype, {
|
|
2601
2922
|
func: Var,
|
|
2602
|
-
args: function() {
|
|
2923
|
+
args: function () {
|
|
2924
|
+
return [this.symbol, this.subscript];
|
|
2925
|
+
},
|
|
2603
2926
|
|
|
2604
|
-
exprArgs: function() {
|
|
2605
|
-
|
|
2927
|
+
exprArgs: function () {
|
|
2928
|
+
return [];
|
|
2929
|
+
},
|
|
2930
|
+
recurse: function () {
|
|
2931
|
+
return this;
|
|
2932
|
+
},
|
|
2606
2933
|
|
|
2607
|
-
print: function() {
|
|
2934
|
+
print: function () {
|
|
2608
2935
|
var sub = "";
|
|
2609
2936
|
if (this.subscript) {
|
|
2610
2937
|
sub = "_(" + this.subscript.print() + ")";
|
|
@@ -2614,7 +2941,7 @@ _.extend(Var.prototype, {
|
|
|
2614
2941
|
|
|
2615
2942
|
// Provide a way to easily evalate expressions with the common case,
|
|
2616
2943
|
// subscripts that consist of a single number or symbol e.g. x_a or x_42
|
|
2617
|
-
prettyPrint: function() {
|
|
2944
|
+
prettyPrint: function () {
|
|
2618
2945
|
var sub = this.subscript;
|
|
2619
2946
|
if (sub && (sub instanceof Num || sub instanceof Symbol)) {
|
|
2620
2947
|
return this.symbol + "_" + sub.print();
|
|
@@ -2623,7 +2950,7 @@ _.extend(Var.prototype, {
|
|
|
2623
2950
|
}
|
|
2624
2951
|
},
|
|
2625
2952
|
|
|
2626
|
-
tex: function() {
|
|
2953
|
+
tex: function () {
|
|
2627
2954
|
var sub = "";
|
|
2628
2955
|
if (this.subscript) {
|
|
2629
2956
|
sub = "_{" + this.subscript.tex() + "}";
|
|
@@ -2632,32 +2959,43 @@ _.extend(Var.prototype, {
|
|
|
2632
2959
|
return prefix + this.symbol + sub;
|
|
2633
2960
|
},
|
|
2634
2961
|
|
|
2635
|
-
repr: function() {
|
|
2962
|
+
repr: function () {
|
|
2963
|
+
return "Var(" + this.print() + ")";
|
|
2964
|
+
},
|
|
2636
2965
|
|
|
2637
|
-
eval: function(vars, options) {
|
|
2966
|
+
eval: function (vars, options) {
|
|
2638
2967
|
return vars[this.prettyPrint()];
|
|
2639
2968
|
},
|
|
2640
2969
|
|
|
2641
|
-
codegen: function() {
|
|
2970
|
+
codegen: function () {
|
|
2642
2971
|
return 'vars["' + this.prettyPrint() + '"]';
|
|
2643
2972
|
},
|
|
2644
2973
|
|
|
2645
|
-
getVars: function() {
|
|
2974
|
+
getVars: function () {
|
|
2975
|
+
return [this.prettyPrint()];
|
|
2976
|
+
},
|
|
2646
2977
|
|
|
2647
|
-
isPositive: function() {
|
|
2978
|
+
isPositive: function () {
|
|
2979
|
+
return false;
|
|
2980
|
+
},
|
|
2648
2981
|
});
|
|
2649
2982
|
|
|
2650
|
-
|
|
2651
2983
|
/* constant */
|
|
2652
|
-
export function Const(symbol) {
|
|
2984
|
+
export function Const(symbol) {
|
|
2985
|
+
this.symbol = symbol;
|
|
2986
|
+
}
|
|
2653
2987
|
Const.prototype = new Symbol();
|
|
2654
2988
|
|
|
2655
2989
|
_.extend(Const.prototype, {
|
|
2656
2990
|
func: Const,
|
|
2657
|
-
args: function() {
|
|
2658
|
-
|
|
2991
|
+
args: function () {
|
|
2992
|
+
return [this.symbol];
|
|
2993
|
+
},
|
|
2994
|
+
recurse: function () {
|
|
2995
|
+
return this;
|
|
2996
|
+
},
|
|
2659
2997
|
|
|
2660
|
-
eval: function(vars, options) {
|
|
2998
|
+
eval: function (vars, options) {
|
|
2661
2999
|
if (this.symbol === "pi") {
|
|
2662
3000
|
return Math.PI;
|
|
2663
3001
|
} else if (this.symbol === "e") {
|
|
@@ -2665,7 +3003,7 @@ _.extend(Const.prototype, {
|
|
|
2665
3003
|
}
|
|
2666
3004
|
},
|
|
2667
3005
|
|
|
2668
|
-
codegen: function() {
|
|
3006
|
+
codegen: function () {
|
|
2669
3007
|
if (this.symbol === "pi") {
|
|
2670
3008
|
return "Math.PI";
|
|
2671
3009
|
} else if (this.symbol === "e") {
|
|
@@ -2673,9 +3011,11 @@ _.extend(Const.prototype, {
|
|
|
2673
3011
|
}
|
|
2674
3012
|
},
|
|
2675
3013
|
|
|
2676
|
-
print: function() {
|
|
3014
|
+
print: function () {
|
|
3015
|
+
return this.symbol;
|
|
3016
|
+
},
|
|
2677
3017
|
|
|
2678
|
-
tex: function() {
|
|
3018
|
+
tex: function () {
|
|
2679
3019
|
if (this.symbol === "pi") {
|
|
2680
3020
|
return "\\pi ";
|
|
2681
3021
|
} else if (this.symbol === "e") {
|
|
@@ -2683,11 +3023,11 @@ _.extend(Const.prototype, {
|
|
|
2683
3023
|
}
|
|
2684
3024
|
},
|
|
2685
3025
|
|
|
2686
|
-
isPositive: function() {
|
|
3026
|
+
isPositive: function () {
|
|
2687
3027
|
return this.eval() > 0;
|
|
2688
3028
|
},
|
|
2689
3029
|
|
|
2690
|
-
abs: function() {
|
|
3030
|
+
abs: function () {
|
|
2691
3031
|
if (this.eval() > 0) {
|
|
2692
3032
|
return this;
|
|
2693
3033
|
} else {
|
|
@@ -2695,7 +3035,7 @@ _.extend(Const.prototype, {
|
|
|
2695
3035
|
}
|
|
2696
3036
|
},
|
|
2697
3037
|
|
|
2698
|
-
getConsts: function() {
|
|
3038
|
+
getConsts: function () {
|
|
2699
3039
|
return [this.print()];
|
|
2700
3040
|
},
|
|
2701
3041
|
});
|
|
@@ -2703,16 +3043,23 @@ _.extend(Const.prototype, {
|
|
|
2703
3043
|
Const.e = new Const("e");
|
|
2704
3044
|
Const.pi = new Const("pi");
|
|
2705
3045
|
|
|
2706
|
-
|
|
2707
3046
|
/* abstract number node */
|
|
2708
3047
|
function Num() {}
|
|
2709
3048
|
Num.prototype = new Expr();
|
|
2710
3049
|
|
|
2711
3050
|
_.extend(Num.prototype, {
|
|
2712
|
-
repr: function() {
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
3051
|
+
repr: function () {
|
|
3052
|
+
return this.print();
|
|
3053
|
+
},
|
|
3054
|
+
strip: function () {
|
|
3055
|
+
return this.abs();
|
|
3056
|
+
},
|
|
3057
|
+
recurse: function () {
|
|
3058
|
+
return this;
|
|
3059
|
+
},
|
|
3060
|
+
codegen: function () {
|
|
3061
|
+
return this.print();
|
|
3062
|
+
},
|
|
2716
3063
|
|
|
2717
3064
|
// takes another Num and returns a new Num
|
|
2718
3065
|
add: abstract,
|
|
@@ -2721,24 +3068,28 @@ _.extend(Num.prototype, {
|
|
|
2721
3068
|
// returns this Num's additive inverse
|
|
2722
3069
|
negate: abstract,
|
|
2723
3070
|
|
|
2724
|
-
isSubtract: function() {
|
|
3071
|
+
isSubtract: function () {
|
|
3072
|
+
return this.hints.subtract;
|
|
3073
|
+
},
|
|
2725
3074
|
|
|
2726
3075
|
// return the absolute value of the number
|
|
2727
3076
|
abs: abstract,
|
|
2728
3077
|
|
|
2729
|
-
needsExplicitMul: function() {
|
|
3078
|
+
needsExplicitMul: function () {
|
|
3079
|
+
return true;
|
|
3080
|
+
},
|
|
2730
3081
|
|
|
2731
3082
|
findGCD: abstract,
|
|
2732
3083
|
|
|
2733
|
-
isPositive: function() {
|
|
3084
|
+
isPositive: function () {
|
|
2734
3085
|
return this.eval() > 0;
|
|
2735
3086
|
},
|
|
2736
3087
|
|
|
2737
|
-
isNegative: function() {
|
|
3088
|
+
isNegative: function () {
|
|
2738
3089
|
return this.eval() < 0;
|
|
2739
3090
|
},
|
|
2740
3091
|
|
|
2741
|
-
asPositiveFactor: function() {
|
|
3092
|
+
asPositiveFactor: function () {
|
|
2742
3093
|
return this.isPositive() ? this : this.abs();
|
|
2743
3094
|
},
|
|
2744
3095
|
|
|
@@ -2749,7 +3100,7 @@ _.extend(Num.prototype, {
|
|
|
2749
3100
|
divide: false,
|
|
2750
3101
|
root: false,
|
|
2751
3102
|
fraction: false,
|
|
2752
|
-
entered: false
|
|
3103
|
+
entered: false,
|
|
2753
3104
|
}),
|
|
2754
3105
|
|
|
2755
3106
|
// whether a number is considered simple (one term)
|
|
@@ -2757,58 +3108,72 @@ _.extend(Num.prototype, {
|
|
|
2757
3108
|
isSimple: abstract,
|
|
2758
3109
|
|
|
2759
3110
|
// Based on http://stackoverflow.com/a/10454560/2571482
|
|
2760
|
-
getDecimalPlaces: function() {
|
|
3111
|
+
getDecimalPlaces: function () {
|
|
2761
3112
|
var match = ("" + this.n).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
|
|
2762
3113
|
if (match) {
|
|
2763
3114
|
return Math.max(
|
|
2764
3115
|
0,
|
|
2765
3116
|
// Number of digits right of decimal point
|
|
2766
3117
|
(match[1] ? match[1].length : 0) -
|
|
2767
|
-
|
|
2768
|
-
|
|
3118
|
+
// Adjust for scientific notation
|
|
3119
|
+
(match[2] ? +match[2] : 0),
|
|
2769
3120
|
);
|
|
2770
3121
|
} else {
|
|
2771
3122
|
return 0;
|
|
2772
3123
|
}
|
|
2773
3124
|
},
|
|
2774
3125
|
|
|
2775
|
-
asRational: abstract
|
|
3126
|
+
asRational: abstract,
|
|
2776
3127
|
});
|
|
2777
3128
|
|
|
2778
|
-
|
|
2779
3129
|
/* rational number (n: numerator, d: denominator) */
|
|
2780
3130
|
export function Rational(numerator, denominator) {
|
|
2781
|
-
var n = numerator;
|
|
3131
|
+
var n = numerator;
|
|
3132
|
+
var d = denominator;
|
|
2782
3133
|
if (d < 0) {
|
|
2783
|
-
n = -n;
|
|
3134
|
+
n = -n;
|
|
3135
|
+
d = -d;
|
|
2784
3136
|
}
|
|
2785
|
-
this.n = n;
|
|
3137
|
+
this.n = n;
|
|
3138
|
+
this.d = d;
|
|
2786
3139
|
}
|
|
2787
3140
|
Rational.prototype = new Num();
|
|
2788
3141
|
|
|
2789
3142
|
_.extend(Rational.prototype, {
|
|
2790
3143
|
func: Rational,
|
|
2791
|
-
args: function() {
|
|
2792
|
-
|
|
3144
|
+
args: function () {
|
|
3145
|
+
return [this.n, this.d];
|
|
3146
|
+
},
|
|
3147
|
+
eval: function () {
|
|
3148
|
+
return this.n / this.d;
|
|
3149
|
+
},
|
|
2793
3150
|
|
|
2794
|
-
print: function() {
|
|
3151
|
+
print: function () {
|
|
2795
3152
|
return this.n.toString() + "/" + this.d.toString();
|
|
2796
3153
|
},
|
|
2797
3154
|
|
|
2798
|
-
tex: function() {
|
|
2799
|
-
var tex =
|
|
3155
|
+
tex: function () {
|
|
3156
|
+
var tex =
|
|
3157
|
+
"\\frac{" +
|
|
3158
|
+
Math.abs(this.n).toString() +
|
|
3159
|
+
"}{" +
|
|
3160
|
+
this.d.toString() +
|
|
3161
|
+
"}";
|
|
2800
3162
|
return this.n < 0 ? "-" + tex : tex;
|
|
2801
3163
|
},
|
|
2802
3164
|
|
|
2803
|
-
add: function(num, options) {
|
|
3165
|
+
add: function (num, options) {
|
|
2804
3166
|
if (num instanceof Rational) {
|
|
2805
|
-
return new Rational(
|
|
3167
|
+
return new Rational(
|
|
3168
|
+
this.n * num.d + this.d * num.n,
|
|
3169
|
+
this.d * num.d,
|
|
3170
|
+
).collect();
|
|
2806
3171
|
} else {
|
|
2807
3172
|
return num.add(this, options);
|
|
2808
3173
|
}
|
|
2809
3174
|
},
|
|
2810
3175
|
|
|
2811
|
-
mul: function(num, options) {
|
|
3176
|
+
mul: function (num, options) {
|
|
2812
3177
|
if (num instanceof Rational) {
|
|
2813
3178
|
return new Rational(this.n * num.n, this.d * num.d).collect();
|
|
2814
3179
|
} else {
|
|
@@ -2816,7 +3181,7 @@ _.extend(Rational.prototype, {
|
|
|
2816
3181
|
}
|
|
2817
3182
|
},
|
|
2818
3183
|
|
|
2819
|
-
collect: function() {
|
|
3184
|
+
collect: function () {
|
|
2820
3185
|
var gcd = Num.findGCD(this.n, this.d);
|
|
2821
3186
|
|
|
2822
3187
|
var n = this.n / gcd;
|
|
@@ -2829,15 +3194,15 @@ _.extend(Rational.prototype, {
|
|
|
2829
3194
|
}
|
|
2830
3195
|
},
|
|
2831
3196
|
|
|
2832
|
-
negate: function() {
|
|
3197
|
+
negate: function () {
|
|
2833
3198
|
return new Rational(-this.n, this.d);
|
|
2834
3199
|
},
|
|
2835
3200
|
|
|
2836
|
-
abs: function() {
|
|
3201
|
+
abs: function () {
|
|
2837
3202
|
return new Rational(Math.abs(this.n), this.d);
|
|
2838
3203
|
},
|
|
2839
3204
|
|
|
2840
|
-
findGCD: function(factor) {
|
|
3205
|
+
findGCD: function (factor) {
|
|
2841
3206
|
// Attempt to factor out common numerators and denominators to return
|
|
2842
3207
|
// a Rational instead of a Float
|
|
2843
3208
|
if (factor instanceof Rational) {
|
|
@@ -2855,7 +3220,7 @@ _.extend(Rational.prototype, {
|
|
|
2855
3220
|
},
|
|
2856
3221
|
|
|
2857
3222
|
// for now, assuming that exp is a Num
|
|
2858
|
-
raiseToThe: function(exp) {
|
|
3223
|
+
raiseToThe: function (exp) {
|
|
2859
3224
|
if (exp instanceof Int) {
|
|
2860
3225
|
var positive = exp.eval() > 0;
|
|
2861
3226
|
var abs = exp.abs().eval();
|
|
@@ -2871,87 +3236,120 @@ _.extend(Rational.prototype, {
|
|
|
2871
3236
|
}
|
|
2872
3237
|
},
|
|
2873
3238
|
|
|
2874
|
-
getDenominator: function() {
|
|
3239
|
+
getDenominator: function () {
|
|
2875
3240
|
return new Int(this.d);
|
|
2876
3241
|
},
|
|
2877
3242
|
|
|
2878
|
-
isSimple: function() {
|
|
3243
|
+
isSimple: function () {
|
|
3244
|
+
return false;
|
|
3245
|
+
},
|
|
2879
3246
|
|
|
2880
|
-
asRational: function() {
|
|
3247
|
+
asRational: function () {
|
|
3248
|
+
return this;
|
|
3249
|
+
},
|
|
2881
3250
|
});
|
|
2882
3251
|
|
|
2883
|
-
|
|
2884
3252
|
/* integer (n: numerator/number) */
|
|
2885
|
-
export function Int(number) {
|
|
3253
|
+
export function Int(number) {
|
|
3254
|
+
this.n = number;
|
|
3255
|
+
}
|
|
2886
3256
|
Int.prototype = new Rational(0, 1);
|
|
2887
3257
|
|
|
2888
3258
|
_.extend(Int.prototype, {
|
|
2889
3259
|
func: Int,
|
|
2890
|
-
args: function() {
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
3260
|
+
args: function () {
|
|
3261
|
+
return [this.n];
|
|
3262
|
+
},
|
|
3263
|
+
print: function () {
|
|
3264
|
+
return this.n.toString();
|
|
3265
|
+
},
|
|
3266
|
+
tex: function () {
|
|
3267
|
+
return this.n.toString();
|
|
3268
|
+
},
|
|
3269
|
+
negate: function () {
|
|
3270
|
+
return new Int(-this.n);
|
|
3271
|
+
},
|
|
3272
|
+
abs: function () {
|
|
3273
|
+
return new Int(Math.abs(this.n));
|
|
3274
|
+
},
|
|
3275
|
+
isSimple: function () {
|
|
3276
|
+
return true;
|
|
3277
|
+
},
|
|
3278
|
+
findGCD: function (factor) {
|
|
2897
3279
|
if (factor instanceof Int) {
|
|
2898
3280
|
return new Int(Num.findGCD(this.n, factor.n));
|
|
2899
3281
|
} else {
|
|
2900
3282
|
return factor.findGCD(this);
|
|
2901
3283
|
}
|
|
2902
|
-
}
|
|
3284
|
+
},
|
|
2903
3285
|
});
|
|
2904
3286
|
|
|
2905
3287
|
_.extend(Int, {
|
|
2906
|
-
create: function(n) {
|
|
3288
|
+
create: function (n) {
|
|
3289
|
+
return new Int(n).addHint("entered");
|
|
3290
|
+
},
|
|
2907
3291
|
});
|
|
2908
3292
|
|
|
2909
3293
|
/* float (n: number) */
|
|
2910
|
-
export function Float(number) {
|
|
3294
|
+
export function Float(number) {
|
|
3295
|
+
this.n = number;
|
|
3296
|
+
}
|
|
2911
3297
|
Float.prototype = new Num();
|
|
2912
3298
|
|
|
2913
3299
|
_.extend(Float.prototype, {
|
|
2914
3300
|
func: Float,
|
|
2915
|
-
args: function() {
|
|
2916
|
-
|
|
3301
|
+
args: function () {
|
|
3302
|
+
return [this.n];
|
|
3303
|
+
},
|
|
3304
|
+
eval: function () {
|
|
3305
|
+
return this.n;
|
|
3306
|
+
},
|
|
2917
3307
|
|
|
2918
3308
|
// TODO(alex): when we internationalize number parsing/display
|
|
2919
3309
|
// we should make sure to use the appropriate decimal mark here
|
|
2920
|
-
print: function() {
|
|
2921
|
-
|
|
3310
|
+
print: function () {
|
|
3311
|
+
return this.n.toString();
|
|
3312
|
+
},
|
|
3313
|
+
tex: function () {
|
|
3314
|
+
return this.n.toString();
|
|
3315
|
+
},
|
|
2922
3316
|
|
|
2923
|
-
add: function(num, options) {
|
|
3317
|
+
add: function (num, options) {
|
|
2924
3318
|
if (options && options.preciseFloats) {
|
|
2925
3319
|
return Float.toDecimalPlaces(
|
|
2926
3320
|
this.n + num.eval(),
|
|
2927
|
-
Math.max(this.getDecimalPlaces(), num.getDecimalPlaces())
|
|
3321
|
+
Math.max(this.getDecimalPlaces(), num.getDecimalPlaces()),
|
|
2928
3322
|
);
|
|
2929
3323
|
} else {
|
|
2930
3324
|
return new Float(this.n + num.eval()).collect();
|
|
2931
3325
|
}
|
|
2932
3326
|
},
|
|
2933
3327
|
|
|
2934
|
-
mul: function(num, options) {
|
|
3328
|
+
mul: function (num, options) {
|
|
2935
3329
|
if (options && options.preciseFloats) {
|
|
2936
3330
|
return Float.toDecimalPlaces(
|
|
2937
3331
|
this.n * num.eval(),
|
|
2938
|
-
this.getDecimalPlaces() + num.getDecimalPlaces()
|
|
3332
|
+
this.getDecimalPlaces() + num.getDecimalPlaces(),
|
|
2939
3333
|
);
|
|
2940
3334
|
} else {
|
|
2941
3335
|
return new Float(this.n * num.eval()).collect();
|
|
2942
3336
|
}
|
|
2943
3337
|
},
|
|
2944
3338
|
|
|
2945
|
-
collect: function() {
|
|
3339
|
+
collect: function () {
|
|
2946
3340
|
// We used to simplify Floats to Ints here whenever possible, but no
|
|
2947
3341
|
// longer do so in order to preserve significant figures.
|
|
2948
3342
|
return this;
|
|
2949
3343
|
},
|
|
2950
3344
|
|
|
2951
|
-
negate: function() {
|
|
2952
|
-
|
|
3345
|
+
negate: function () {
|
|
3346
|
+
return new Float(-this.n);
|
|
3347
|
+
},
|
|
3348
|
+
abs: function () {
|
|
3349
|
+
return new Float(Math.abs(this.n));
|
|
3350
|
+
},
|
|
2953
3351
|
|
|
2954
|
-
findGCD: function(factor) {
|
|
3352
|
+
findGCD: function (factor) {
|
|
2955
3353
|
if (factor instanceof Num) {
|
|
2956
3354
|
return new Float(Num.findGCD(this.eval(), factor.eval())).collect();
|
|
2957
3355
|
} else {
|
|
@@ -2960,12 +3358,16 @@ _.extend(Float.prototype, {
|
|
|
2960
3358
|
},
|
|
2961
3359
|
|
|
2962
3360
|
// for now, assuming that exp is a Num
|
|
2963
|
-
raiseToThe: function(exp, options) {
|
|
2964
|
-
if (
|
|
2965
|
-
|
|
3361
|
+
raiseToThe: function (exp, options) {
|
|
3362
|
+
if (
|
|
3363
|
+
options &&
|
|
3364
|
+
options.preciseFloats &&
|
|
3365
|
+
exp instanceof Int &&
|
|
3366
|
+
exp.n > 1
|
|
3367
|
+
) {
|
|
2966
3368
|
return Float.toDecimalPlaces(
|
|
2967
3369
|
new Pow(this, exp).eval(),
|
|
2968
|
-
this.getDecimalPlaces() * exp.n
|
|
3370
|
+
this.getDecimalPlaces() * exp.n,
|
|
2969
3371
|
);
|
|
2970
3372
|
} else {
|
|
2971
3373
|
return new Float(new Pow(this, exp).eval()).collect();
|
|
@@ -2973,7 +3375,7 @@ _.extend(Float.prototype, {
|
|
|
2973
3375
|
},
|
|
2974
3376
|
|
|
2975
3377
|
// only to be used on non-repeating decimals (e.g. user-provided)
|
|
2976
|
-
asRational: function() {
|
|
3378
|
+
asRational: function () {
|
|
2977
3379
|
var parts = this.n.toString().split(".");
|
|
2978
3380
|
if (parts.length === 1) {
|
|
2979
3381
|
return new Rational(this.n, 1);
|
|
@@ -2984,26 +3386,30 @@ _.extend(Float.prototype, {
|
|
|
2984
3386
|
}
|
|
2985
3387
|
},
|
|
2986
3388
|
|
|
2987
|
-
getDenominator: function() {
|
|
3389
|
+
getDenominator: function () {
|
|
2988
3390
|
return this.asRational().getDenominator();
|
|
2989
3391
|
},
|
|
2990
3392
|
|
|
2991
|
-
isSimple: function() {
|
|
3393
|
+
isSimple: function () {
|
|
3394
|
+
return true;
|
|
3395
|
+
},
|
|
2992
3396
|
});
|
|
2993
3397
|
|
|
2994
3398
|
_.extend(Float, {
|
|
2995
|
-
create: function(n) {
|
|
3399
|
+
create: function (n) {
|
|
3400
|
+
return new Float(n).addHint("entered");
|
|
3401
|
+
},
|
|
2996
3402
|
|
|
2997
3403
|
// Account for floating point imprecision by explicitly controlling the
|
|
2998
3404
|
// number of decimal places in common operations (e.g. +, *, ^)
|
|
2999
|
-
toDecimalPlaces: function(n, places) {
|
|
3405
|
+
toDecimalPlaces: function (n, places) {
|
|
3000
3406
|
return new Float(+n.toFixed(Math.min(places, 20))).collect();
|
|
3001
|
-
}
|
|
3407
|
+
},
|
|
3002
3408
|
});
|
|
3003
3409
|
|
|
3004
3410
|
// static methods and fields that are best defined on Num
|
|
3005
3411
|
_.extend(Num, {
|
|
3006
|
-
negativeOne: function(hint) {
|
|
3412
|
+
negativeOne: function (hint) {
|
|
3007
3413
|
if (hint === "subtract") {
|
|
3008
3414
|
return Num.Sub;
|
|
3009
3415
|
} else if (hint === "divide") {
|
|
@@ -3014,7 +3420,7 @@ _.extend(Num, {
|
|
|
3014
3420
|
},
|
|
3015
3421
|
|
|
3016
3422
|
// find the greatest common denominator
|
|
3017
|
-
findGCD: function(a, b) {
|
|
3423
|
+
findGCD: function (a, b) {
|
|
3018
3424
|
var mod;
|
|
3019
3425
|
|
|
3020
3426
|
a = Math.abs(a);
|
|
@@ -3036,17 +3442,17 @@ _.extend(Num, {
|
|
|
3036
3442
|
return a;
|
|
3037
3443
|
},
|
|
3038
3444
|
|
|
3039
|
-
min: function() {
|
|
3040
|
-
return _.min(_.toArray(arguments), function(num) {
|
|
3445
|
+
min: function () {
|
|
3446
|
+
return _.min(_.toArray(arguments), function (num) {
|
|
3041
3447
|
return num.eval();
|
|
3042
3448
|
});
|
|
3043
3449
|
},
|
|
3044
3450
|
|
|
3045
|
-
max: function() {
|
|
3046
|
-
return _.max(_.toArray(arguments), function(num) {
|
|
3451
|
+
max: function () {
|
|
3452
|
+
return _.max(_.toArray(arguments), function (num) {
|
|
3047
3453
|
return num.eval();
|
|
3048
3454
|
});
|
|
3049
|
-
}
|
|
3455
|
+
},
|
|
3050
3456
|
});
|
|
3051
3457
|
|
|
3052
3458
|
Num.Neg = new Int(-1).addHint("negate");
|
|
@@ -3059,12 +3465,11 @@ Num.Zero = new Int(0);
|
|
|
3059
3465
|
Num.One = new Int(1);
|
|
3060
3466
|
Num.Ten = new Int(10);
|
|
3061
3467
|
|
|
3062
|
-
|
|
3063
3468
|
// set identities here
|
|
3064
3469
|
Add.prototype.identity = Num.Zero;
|
|
3065
3470
|
Mul.prototype.identity = Num.One;
|
|
3066
3471
|
|
|
3067
|
-
var parseError = function(str, hash) {
|
|
3472
|
+
var parseError = function (str, hash) {
|
|
3068
3473
|
// return int location of parsing error
|
|
3069
3474
|
throw new Error(hash.loc.first_column);
|
|
3070
3475
|
};
|
|
@@ -3087,7 +3492,7 @@ parser.yy = {
|
|
|
3087
3492
|
parseError: parseError,
|
|
3088
3493
|
|
|
3089
3494
|
constants: ["e"],
|
|
3090
|
-
symbolLexer: function(symbol) {
|
|
3495
|
+
symbolLexer: function (symbol) {
|
|
3091
3496
|
if (_.contains(parser.yy.constants, symbol)) {
|
|
3092
3497
|
return "CONST";
|
|
3093
3498
|
} else if (_.contains(parser.yy.functions, symbol)) {
|
|
@@ -3095,10 +3500,10 @@ parser.yy = {
|
|
|
3095
3500
|
} else {
|
|
3096
3501
|
return "VAR";
|
|
3097
3502
|
}
|
|
3098
|
-
}
|
|
3503
|
+
},
|
|
3099
3504
|
};
|
|
3100
3505
|
|
|
3101
|
-
export const parse = function(input, options) {
|
|
3506
|
+
export const parse = function (input, options) {
|
|
3102
3507
|
try {
|
|
3103
3508
|
if (options && options.functions) {
|
|
3104
3509
|
// reserve the symbol "i" for complex numbers
|
|
@@ -3132,13 +3537,13 @@ Unit.prototype = new Symbol();
|
|
|
3132
3537
|
//
|
|
3133
3538
|
// "g" -> Unit("g")
|
|
3134
3539
|
// "kg" -> 1000 * Unit("g")
|
|
3135
|
-
var unprefixify = function(symbol) {
|
|
3540
|
+
var unprefixify = function (symbol) {
|
|
3136
3541
|
if (_(baseUnits).has(symbol) || _(derivedUnits).has(symbol)) {
|
|
3137
3542
|
return new Unit(symbol);
|
|
3138
3543
|
}
|
|
3139
3544
|
|
|
3140
3545
|
// check for prefix
|
|
3141
|
-
var prefix = _(_(siPrefixes).keys()).find(function(testPrefix) {
|
|
3546
|
+
var prefix = _(_(siPrefixes).keys()).find(function (testPrefix) {
|
|
3142
3547
|
return new RegExp("^" + testPrefix).test(symbol);
|
|
3143
3548
|
});
|
|
3144
3549
|
|
|
@@ -3151,10 +3556,10 @@ var unprefixify = function(symbol) {
|
|
|
3151
3556
|
//
|
|
3152
3557
|
// Otherwise, we're trying to parse a unit label which is not
|
|
3153
3558
|
// allowed (mwk, mBTU, etc).
|
|
3154
|
-
if (
|
|
3155
|
-
(
|
|
3156
|
-
|
|
3157
|
-
|
|
3559
|
+
if (
|
|
3560
|
+
_(baseUnits).has(base) ||
|
|
3561
|
+
(derivedUnits[base] && derivedUnits[base].prefixes === hasPrefixes)
|
|
3562
|
+
) {
|
|
3158
3563
|
return new Mul(siPrefixes[prefix], new Unit(base));
|
|
3159
3564
|
} else {
|
|
3160
3565
|
throw new Error(base + " does not allow prefixes");
|
|
@@ -3164,7 +3569,7 @@ var unprefixify = function(symbol) {
|
|
|
3164
3569
|
}
|
|
3165
3570
|
};
|
|
3166
3571
|
|
|
3167
|
-
export const unitParse = function(input) {
|
|
3572
|
+
export const unitParse = function (input) {
|
|
3168
3573
|
try {
|
|
3169
3574
|
var parseResult = unitParser.parse(input);
|
|
3170
3575
|
|
|
@@ -3185,15 +3590,15 @@ export const unitParse = function(input) {
|
|
|
3185
3590
|
|
|
3186
3591
|
var unitArray = [];
|
|
3187
3592
|
|
|
3188
|
-
_(parseResult.unit.num).each(function(unitSpec) {
|
|
3593
|
+
_(parseResult.unit.num).each(function (unitSpec) {
|
|
3189
3594
|
unitArray.push(
|
|
3190
|
-
new Pow(unprefixify(unitSpec.name), new Int(unitSpec.pow))
|
|
3595
|
+
new Pow(unprefixify(unitSpec.name), new Int(unitSpec.pow)),
|
|
3191
3596
|
);
|
|
3192
3597
|
});
|
|
3193
3598
|
|
|
3194
|
-
_(parseResult.unit.denom).each(function(unitSpec) {
|
|
3599
|
+
_(parseResult.unit.denom).each(function (unitSpec) {
|
|
3195
3600
|
unitArray.push(
|
|
3196
|
-
new Pow(unprefixify(unitSpec.name), new Int(-1 * unitSpec.pow))
|
|
3601
|
+
new Pow(unprefixify(unitSpec.name), new Int(-1 * unitSpec.pow)),
|
|
3197
3602
|
);
|
|
3198
3603
|
});
|
|
3199
3604
|
|
|
@@ -3202,18 +3607,18 @@ export const unitParse = function(input) {
|
|
|
3202
3607
|
if (parseResult.type === "unitMagnitude") {
|
|
3203
3608
|
// in the first case we have a magnitude coefficient as well as the
|
|
3204
3609
|
// unit itself.
|
|
3205
|
-
var coefArray =
|
|
3206
|
-
|
|
3610
|
+
var coefArray = [new Float(+parseResult.magnitude)].concat(
|
|
3611
|
+
unitArray,
|
|
3612
|
+
);
|
|
3207
3613
|
var expr = new Mul(coefArray);
|
|
3208
3614
|
return {
|
|
3209
3615
|
parsed: true,
|
|
3210
3616
|
unit: unit,
|
|
3211
3617
|
expr: expr,
|
|
3212
3618
|
coefficient: parseResult.magnitude,
|
|
3213
|
-
type: parseResult.type
|
|
3619
|
+
type: parseResult.type,
|
|
3214
3620
|
};
|
|
3215
3621
|
} else {
|
|
3216
|
-
|
|
3217
3622
|
// in the second case it's just the unit with no magnitude.
|
|
3218
3623
|
return {
|
|
3219
3624
|
parsed: true,
|
|
@@ -3228,10 +3633,14 @@ export const unitParse = function(input) {
|
|
|
3228
3633
|
|
|
3229
3634
|
_.extend(Unit.prototype, {
|
|
3230
3635
|
func: Unit,
|
|
3231
|
-
args: function() {
|
|
3232
|
-
|
|
3636
|
+
args: function () {
|
|
3637
|
+
return [this.symbol];
|
|
3638
|
+
},
|
|
3639
|
+
recurse: function () {
|
|
3640
|
+
return this;
|
|
3641
|
+
},
|
|
3233
3642
|
|
|
3234
|
-
eval: function(vars, options) {
|
|
3643
|
+
eval: function (vars, options) {
|
|
3235
3644
|
// This is called when comparing units. A unit doesn't affect the
|
|
3236
3645
|
// numerical value of its coefficient, so this needs to be 1.
|
|
3237
3646
|
//
|
|
@@ -3242,16 +3651,24 @@ _.extend(Unit.prototype, {
|
|
|
3242
3651
|
return 1;
|
|
3243
3652
|
},
|
|
3244
3653
|
|
|
3245
|
-
getUnits: function() {
|
|
3654
|
+
getUnits: function () {
|
|
3655
|
+
return [{unit: this.symbol, pow: 1}];
|
|
3656
|
+
},
|
|
3246
3657
|
|
|
3247
|
-
codegen: function() {
|
|
3658
|
+
codegen: function () {
|
|
3659
|
+
return "1";
|
|
3660
|
+
},
|
|
3248
3661
|
|
|
3249
|
-
print: function() {
|
|
3662
|
+
print: function () {
|
|
3663
|
+
return this.symbol;
|
|
3664
|
+
},
|
|
3250
3665
|
|
|
3251
|
-
tex: function() {
|
|
3666
|
+
tex: function () {
|
|
3667
|
+
return this.symbol;
|
|
3668
|
+
},
|
|
3252
3669
|
|
|
3253
3670
|
// Simplify units by replacing prefixes with multiplication
|
|
3254
|
-
collect: function(options) {
|
|
3671
|
+
collect: function (options) {
|
|
3255
3672
|
if (_(baseUnits).has(this.symbol)) {
|
|
3256
3673
|
return this;
|
|
3257
3674
|
} else if (_(derivedUnits).has(this.symbol)) {
|
|
@@ -3298,7 +3715,7 @@ var siPrefixes = {
|
|
|
3298
3715
|
var hasPrefixes = {};
|
|
3299
3716
|
var hasntPrefixes = {};
|
|
3300
3717
|
|
|
3301
|
-
var makeAlias = function(str, prefixes) {
|
|
3718
|
+
var makeAlias = function (str, prefixes) {
|
|
3302
3719
|
var splits = str.split("|");
|
|
3303
3720
|
var coefficientStr = splits[0].trim();
|
|
3304
3721
|
var unitsStr = splits[1].trim();
|
|
@@ -3314,9 +3731,10 @@ var makeAlias = function(str, prefixes) {
|
|
|
3314
3731
|
if (numdenomStr[0]) {
|
|
3315
3732
|
numdenomStr[0]
|
|
3316
3733
|
.split(" ")
|
|
3317
|
-
.filter(function(x) {
|
|
3734
|
+
.filter(function (x) {
|
|
3318
3735
|
return x !== "";
|
|
3319
|
-
})
|
|
3736
|
+
})
|
|
3737
|
+
.map(function (x) {
|
|
3320
3738
|
numdenom.push(new Unit(x));
|
|
3321
3739
|
});
|
|
3322
3740
|
}
|
|
@@ -3324,9 +3742,10 @@ var makeAlias = function(str, prefixes) {
|
|
|
3324
3742
|
if (numdenomStr[1]) {
|
|
3325
3743
|
numdenomStr[1]
|
|
3326
3744
|
.split(" ")
|
|
3327
|
-
.filter(function(x) {
|
|
3745
|
+
.filter(function (x) {
|
|
3328
3746
|
return x !== "";
|
|
3329
|
-
})
|
|
3747
|
+
})
|
|
3748
|
+
.map(function (x) {
|
|
3330
3749
|
numdenom.push(new Pow(new Unit(x), Num.Div));
|
|
3331
3750
|
});
|
|
3332
3751
|
}
|
|
@@ -3366,27 +3785,27 @@ var derivedUnits = {
|
|
|
3366
3785
|
u: makeAlias("| Da", hasntPrefixes),
|
|
3367
3786
|
|
|
3368
3787
|
// length
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3788
|
+
meter: makeAlias("| m", hasntPrefixes),
|
|
3789
|
+
meters: makeAlias("| m", hasntPrefixes),
|
|
3790
|
+
in: makeAlias("254 / 10000 | m", hasntPrefixes),
|
|
3791
|
+
ft: makeAlias("3048 / 10000 | m", hasntPrefixes),
|
|
3792
|
+
yd: makeAlias("9144 / 10000 | m", hasntPrefixes),
|
|
3793
|
+
mi: makeAlias("1609344 / 1000 | m", hasntPrefixes),
|
|
3794
|
+
ly: makeAlias("9.4607 x 10^15 | m", hasntPrefixes),
|
|
3795
|
+
nmi: makeAlias("1852 | m", hasntPrefixes),
|
|
3796
|
+
Å: makeAlias("10^-10 | m", hasntPrefixes),
|
|
3797
|
+
pc: makeAlias("3.0857 x 10^16 | m", hasntPrefixes),
|
|
3379
3798
|
|
|
3380
3799
|
// time
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3800
|
+
min: makeAlias("60 | s", hasntPrefixes),
|
|
3801
|
+
hr: makeAlias("3600 | s", hasntPrefixes),
|
|
3802
|
+
sec: makeAlias("| s", hasntPrefixes),
|
|
3384
3803
|
// TODO(joel) make day work
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3804
|
+
day: makeAlias("86400 | s", hasntPrefixes),
|
|
3805
|
+
wk: makeAlias("604800 | s", hasntPrefixes),
|
|
3806
|
+
fortnight: makeAlias("14 | day", hasntPrefixes),
|
|
3807
|
+
shake: makeAlias("10^-8 | s", hasntPrefixes),
|
|
3808
|
+
olympiad: makeAlias("126200000 | s", hasntPrefixes),
|
|
3390
3809
|
|
|
3391
3810
|
// temperature
|
|
3392
3811
|
"°C": makeAlias("1 | K", hasntPrefixes),
|
|
@@ -3394,113 +3813,113 @@ var derivedUnits = {
|
|
|
3394
3813
|
"°R": makeAlias("5/9 | K", hasntPrefixes),
|
|
3395
3814
|
|
|
3396
3815
|
// electric charge
|
|
3397
|
-
|
|
3816
|
+
e: makeAlias("1.6021765314 x 10^-19 | C", hasntPrefixes),
|
|
3398
3817
|
|
|
3399
3818
|
// speed
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3819
|
+
c: makeAlias("299792458 | m / s", hasntPrefixes),
|
|
3820
|
+
kn: makeAlias("514/1000 | m / s", hasntPrefixes),
|
|
3821
|
+
kt: makeAlias("| kn", hasntPrefixes),
|
|
3822
|
+
knot: makeAlias("| kn", hasntPrefixes),
|
|
3404
3823
|
|
|
3405
3824
|
// energy
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3825
|
+
J: makeAlias("| N m", hasPrefixes),
|
|
3826
|
+
BTU: makeAlias("1060 | J", hasntPrefixes),
|
|
3827
|
+
cal: makeAlias("4184 / 1000 | J", hasPrefixes),
|
|
3828
|
+
eV: makeAlias("1.602176514 x 10^-19 | J", hasPrefixes),
|
|
3829
|
+
erg: makeAlias("10^−7 | J", hasPrefixes),
|
|
3411
3830
|
|
|
3412
3831
|
// power
|
|
3413
|
-
|
|
3832
|
+
W: makeAlias("| J / s", hasPrefixes),
|
|
3414
3833
|
"H-e": makeAlias("80 | W", hasntPrefixes),
|
|
3415
3834
|
|
|
3416
3835
|
// force
|
|
3417
|
-
|
|
3836
|
+
N: makeAlias("1000 | g m / s s", hasPrefixes),
|
|
3418
3837
|
// "lb": makeAlias("4448 / 1000 | N", hasntPrefixes),
|
|
3419
3838
|
// 4.4482216152605
|
|
3420
|
-
|
|
3421
|
-
|
|
3839
|
+
lb: makeAlias("4448221615 / 1000000000 | N", hasntPrefixes),
|
|
3840
|
+
dyn: makeAlias("10^-5 | N", hasntPrefixes),
|
|
3422
3841
|
|
|
3423
3842
|
// pressure
|
|
3424
|
-
|
|
3425
|
-
|
|
3843
|
+
Pa: makeAlias("1 | N / m m m", hasPrefixes),
|
|
3844
|
+
bar: makeAlias("10^5 | Pa", hasPrefixes),
|
|
3426
3845
|
"㏔": makeAlias("1/1000 | bar", hasntPrefixes),
|
|
3427
3846
|
"㍴": makeAlias("| bar", hasntPrefixes),
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3847
|
+
atm: makeAlias("101325 | Pa", hasntPrefixes),
|
|
3848
|
+
Torr: makeAlias("1/760 | atm", hasntPrefixes),
|
|
3849
|
+
mmHg: makeAlias("| Torr", hasntPrefixes),
|
|
3431
3850
|
|
|
3432
3851
|
// area
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3852
|
+
ha: makeAlias("10^4 | m m", hasntPrefixes),
|
|
3853
|
+
b: makeAlias("10^−28 | m m", hasPrefixes),
|
|
3854
|
+
barn: makeAlias("| b", hasPrefixes),
|
|
3855
|
+
acre: makeAlias("4046.87 | m m", hasntPrefixes),
|
|
3856
|
+
skilodge: makeAlias("10^-31 | m m", hasntPrefixes),
|
|
3857
|
+
outhouse: makeAlias("10^-34 | m m", hasntPrefixes),
|
|
3858
|
+
shed: makeAlias("10^-52 | m m", hasntPrefixes),
|
|
3440
3859
|
|
|
3441
3860
|
// volume
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3861
|
+
L: makeAlias("1/1000 | m m m", hasPrefixes),
|
|
3862
|
+
gal: makeAlias("3785/1000 | L", hasPrefixes),
|
|
3863
|
+
cup: makeAlias("1/16 | gal", hasntPrefixes),
|
|
3864
|
+
qt: makeAlias("1/4 | gal", hasntPrefixes),
|
|
3865
|
+
quart: makeAlias("| qt", hasntPrefixes),
|
|
3866
|
+
p: makeAlias("1/8 | gal", hasntPrefixes),
|
|
3867
|
+
pt: makeAlias("| p", hasntPrefixes),
|
|
3868
|
+
pint: makeAlias("| p", hasntPrefixes),
|
|
3450
3869
|
"fl oz": makeAlias("1/8 | cup", hasntPrefixes),
|
|
3451
3870
|
"fl. oz.": makeAlias("1/8 | cup", hasntPrefixes),
|
|
3452
|
-
|
|
3453
|
-
|
|
3871
|
+
tbsp: makeAlias("1/16 | cup", hasntPrefixes),
|
|
3872
|
+
tsp: makeAlias("1/3 | tbsp", hasntPrefixes),
|
|
3454
3873
|
|
|
3455
3874
|
// rotational
|
|
3456
3875
|
// "rad":
|
|
3457
|
-
|
|
3458
|
-
|
|
3876
|
+
rev: makeAlias("2 pi | rad", hasntPrefixes),
|
|
3877
|
+
deg: makeAlias("180 pi | rad", hasntPrefixes),
|
|
3459
3878
|
"°": makeAlias("| deg", hasntPrefixes),
|
|
3460
|
-
|
|
3461
|
-
|
|
3879
|
+
arcminute: makeAlias("1/60 | deg", hasntPrefixes),
|
|
3880
|
+
arcsec: makeAlias("1/3600 | deg", hasntPrefixes),
|
|
3462
3881
|
|
|
3463
3882
|
// dimensionless
|
|
3464
3883
|
// "B": makeAlias("10 | dB", hasntPrefixes), // XXX danger - logarithmic
|
|
3465
3884
|
// "dB"
|
|
3466
3885
|
// "nP"
|
|
3467
|
-
|
|
3468
|
-
|
|
3886
|
+
Hu: makeAlias("1000 | dB", hasPrefixes),
|
|
3887
|
+
dozen: makeAlias("12 |", hasntPrefixes),
|
|
3469
3888
|
// XXX
|
|
3470
|
-
|
|
3889
|
+
mol: makeAlias("6.0221412927 x 10^23 |", hasPrefixes),
|
|
3471
3890
|
"%": makeAlias("1/100 |", hasntPrefixes),
|
|
3472
|
-
|
|
3473
|
-
|
|
3891
|
+
percent: makeAlias("| %", hasntPrefixes),
|
|
3892
|
+
ppm: makeAlias("1/1000000 |", hasntPrefixes),
|
|
3474
3893
|
|
|
3475
3894
|
// electric / magnetic
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3895
|
+
V: makeAlias("1000 | g m m / s s C", hasPrefixes),
|
|
3896
|
+
C: makeAlias("| A s", hasPrefixes),
|
|
3897
|
+
ampere: makeAlias("| A", hasntPrefixes),
|
|
3898
|
+
Ω: makeAlias("| V / A", hasPrefixes),
|
|
3899
|
+
ohm: makeAlias("| Ω", hasntPrefixes),
|
|
3900
|
+
F: makeAlias("| C / V", hasPrefixes),
|
|
3901
|
+
H: makeAlias("| ohm s", hasPrefixes),
|
|
3902
|
+
T: makeAlias("1000 | g / C s", hasPrefixes),
|
|
3903
|
+
Wb: makeAlias("1000 | g m m / C s", hasPrefixes),
|
|
3485
3904
|
|
|
3486
3905
|
// photometry
|
|
3487
3906
|
// TODO not sure this is right
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3907
|
+
lm: makeAlias("pi x 10^4 | cd / m m", hasntPrefixes),
|
|
3908
|
+
lx: makeAlias("| lm / m m", hasntPrefixes),
|
|
3909
|
+
nit: makeAlias("| cd / m m", hasntPrefixes),
|
|
3910
|
+
sb: makeAlias("10^4 | cd / m m", hasntPrefixes),
|
|
3911
|
+
stilb: makeAlias("1 | sb", hasntPrefixes),
|
|
3912
|
+
apostilb: makeAlias("1 / pi x 10^(-4) | sb", hasntPrefixes),
|
|
3913
|
+
blondel: makeAlias("| apostilb", hasntPrefixes),
|
|
3914
|
+
asb: makeAlias("| apostilb", hasntPrefixes),
|
|
3915
|
+
la: makeAlias("| lm", hasntPrefixes),
|
|
3916
|
+
Lb: makeAlias("| lm", hasntPrefixes),
|
|
3917
|
+
sk: makeAlias("10^-7 | lm", hasntPrefixes),
|
|
3918
|
+
skot: makeAlias("| sk", hasntPrefixes),
|
|
3919
|
+
bril: makeAlias("10^-11 | lm", hasntPrefixes),
|
|
3501
3920
|
|
|
3502
3921
|
// other
|
|
3503
|
-
|
|
3922
|
+
Hz: makeAlias("| / s", hasPrefixes),
|
|
3504
3923
|
};
|
|
3505
3924
|
|
|
3506
3925
|
export const Zero = Num.Zero;
|