@khanacademy/kas 0.3.13 → 0.3.15

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/dist/es/index.js CHANGED
@@ -3,9 +3,19 @@ import _ from 'underscore';
3
3
 
4
4
  // This file is processed by a Rollup plugin (replace) to inject the production
5
5
  const libName = "@khanacademy/kas";
6
- const libVersion = "0.3.13";
6
+ const libVersion = "0.3.15";
7
7
  addLibraryVersionToPerseusDebug(libName, libVersion);
8
8
 
9
+ function _extends() {
10
+ return _extends = Object.assign ? Object.assign.bind() : function (n) {
11
+ for (var e = 1; e < arguments.length; e++) {
12
+ var t = arguments[e];
13
+ for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
14
+ }
15
+ return n;
16
+ }, _extends.apply(null, arguments);
17
+ }
18
+
9
19
  // this is a @generated file
10
20
 
11
21
  /* parser generated by jison 0.4.15 */
@@ -2615,7 +2625,7 @@ var parser = function () {
2615
2625
  return new Parser();
2616
2626
  }();
2617
2627
 
2618
- /* eslint-disable prettier/prettier */
2628
+ var _class5, _class12;
2619
2629
 
2620
2630
  /* The node hierarcy is as follows:
2621
2631
 
@@ -2628,7 +2638,7 @@ var parser = function () {
2628
2638
  Eq 2 children
2629
2639
  Trig 1 child
2630
2640
  Abs 1 child
2631
- (Symbol)
2641
+ (Sym)
2632
2642
  Func 1 child e.g. f(x)
2633
2643
  Var leaf node e.g. x, x_n
2634
2644
  Const leaf node e.g. pi, e, <i>
@@ -2648,27 +2658,14 @@ var parser = function () {
2648
2658
 
2649
2659
  /* non user-facing functions */
2650
2660
 
2651
- // assert that all abstract methods have been overridden
2652
- var abstract = function abstract() {
2653
- // Try to give people a bit of information when this happens
2654
- throw new Error("Abstract method - must override for expr: " +
2655
- // eslint-disable-next-line @babel/no-invalid-this
2656
- this.print());
2657
- };
2658
-
2659
- // throw an error that is meant to be caught by the test suite (not user facing)
2660
- var error = function error(message) {
2661
- throw new Error(message);
2662
- };
2663
-
2664
2661
  // reliably detect NaN
2665
- var isNaN = function isNaN(object) {
2662
+ const isNaN = function isNaN(object) {
2666
2663
  return object !== object;
2667
2664
  };
2668
2665
 
2669
2666
  // return a random float between min (inclusive) and max (exclusive),
2670
2667
  // not that inclusivity means much, probabilistically, on floats
2671
- var randomFloat = function randomFloat(min, max) {
2668
+ const randomFloat = function randomFloat(min, max) {
2672
2669
  var extent = max - min;
2673
2670
  return Math.random() * extent + min;
2674
2671
  };
@@ -2677,53 +2674,113 @@ var randomFloat = function randomFloat(min, max) {
2677
2674
  var ITERATIONS = 12;
2678
2675
  var TOLERANCE = 9; // decimal places
2679
2676
 
2677
+ // NOTE(kevinb): _.partition exists in a more recent version of underscore.
2678
+ // To avoid having to update underscore I've added a hacky version of this
2679
+ // method here.
2680
+ function partition(list, iteratee) {
2681
+ const a = [];
2682
+ const b = [];
2683
+ _.forEach(list, (elem, key, ctx) => {
2684
+ if (iteratee(elem, key, ctx)) {
2685
+ a.push(elem);
2686
+ } else {
2687
+ b.push(elem);
2688
+ }
2689
+ });
2690
+ return [a, b];
2691
+ }
2692
+ function isExpr(arg) {
2693
+ return arg instanceof Expr;
2694
+ }
2695
+ const isAdd = function isAdd(term) {
2696
+ return term instanceof Add;
2697
+ };
2698
+ function isRational(arg) {
2699
+ return arg instanceof Rational;
2700
+ }
2701
+ function getFactors(expr) {
2702
+ if (expr instanceof Mul) {
2703
+ return expr.terms;
2704
+ } else {
2705
+ return [expr];
2706
+ }
2707
+ }
2680
2708
  /* abstract base expression node */
2681
- function Expr() {}
2682
- _.extend(Expr.prototype, {
2709
+ class Expr {
2710
+ constructor() {
2711
+ this.hints = void 0;
2712
+ this.hints = {
2713
+ parens: false
2714
+ };
2715
+ }
2716
+
2683
2717
  // this node's immediate constructor
2684
- func: abstract,
2718
+ // The `new (...args: any[]): any;` part of the type is a
2719
+ // "construct" signature. It indicates that `func` is a class.
2720
+ // See https://www.typescriptlang.org/docs/handbook/2/functions.html#construct-signatures.
2685
2721
  // an array of the arguments to this node's immediate constructor
2686
- args: abstract,
2687
2722
  // make a new node with the given arguments
2688
- construct: function (args) {
2689
- var instance = new this.func();
2690
- this.func.apply(instance, args);
2723
+ construct(args) {
2724
+ const func = this.func;
2725
+ const instance = new func(...args);
2726
+ if (typeof instance === "undefined") {
2727
+ throw new Error("constructor function returning undefined");
2728
+ }
2691
2729
  return instance;
2692
- },
2730
+ }
2731
+
2693
2732
  // an abstraction for chainable, bottom-up recursion
2694
- recurse: function (method) {
2695
- var passed = Array.prototype.slice.call(arguments, 1);
2733
+ // NOTE(kevinb): This method is highly dynamic. It's possible that it
2734
+ // could be made more type-safe using overload signatures.
2735
+ recurse(method, ...passed) {
2696
2736
  var args = _.map(this.args(), function (arg) {
2697
- return _.isString(arg) ? arg : arg[method].apply(arg, passed);
2737
+ return _.isString(arg) || _.isNumber(arg) ? arg : arg[method].apply(arg, passed);
2698
2738
  });
2699
2739
  return this.construct(args);
2700
- },
2740
+ }
2741
+
2701
2742
  // evaluate numerically with given variable mapping
2702
- eval: abstract,
2703
- codegen: abstract,
2704
- compile: function () {
2743
+ // NOTE(kevin): This could made into an abstract method but
2744
+ // Eq doesn't implement it. This indicates that we probably
2745
+ // need to introduce another class in our hierarchy.
2746
+ eval(vars = {}, options) {
2747
+ throw new Error("Abstract method - must override for expr: " +
2748
+ // eslint-disable-next-line @babel/no-invalid-this
2749
+ this.print());
2750
+ }
2751
+
2752
+ // NOTE(kevin): This could made into an abstract method but
2753
+ // Eq doesn't implement it. This indicates that we probably
2754
+ // need to introduce another class in our hierarchy.
2755
+ codegen() {
2756
+ throw new Error("Abstract method - must override for expr: " +
2757
+ // eslint-disable-next-line @babel/no-invalid-this
2758
+ this.print());
2759
+ }
2760
+ compile() {
2705
2761
  var code = this.codegen();
2706
2762
  try {
2763
+ // @ts-expect-error: TypeScript doesn't want to unify
2764
+ // `Function` with the `compile`'s return type.
2707
2765
  return new Function("vars", "return " + code + ";");
2708
2766
  } catch (e) {
2709
2767
  throw new Error("Function did not compile: " + code);
2710
2768
  }
2711
- },
2769
+ }
2770
+
2712
2771
  // returns a string unambiguously representing the expression
2713
2772
  // should be valid as input
2714
2773
  // e.g. this.equals(parse(this.print())) === true
2715
- print: abstract,
2716
2774
  // returns a TeX string representing the expression
2717
- tex: abstract,
2718
2775
  // returns a TeX string, modified by the given options
2719
- asTex: function (options) {
2776
+ asTex(options) {
2720
2777
  options = options || {};
2721
2778
  _.defaults(options, {
2722
2779
  display: true,
2723
2780
  dynamic: true,
2724
2781
  times: false
2725
2782
  });
2726
- var tex = this.tex();
2783
+ let tex = this.tex();
2727
2784
  if (options.display) {
2728
2785
  tex = "\\displaystyle " + tex;
2729
2786
  }
@@ -2735,49 +2792,54 @@ _.extend(Expr.prototype, {
2735
2792
  tex = tex.replace(/\\cdot/g, "\\times");
2736
2793
  }
2737
2794
  return tex;
2738
- },
2795
+ }
2796
+
2739
2797
  // returns the name of this expression's constructor as a string
2740
- // only used for testing and debugging (the ugly regex is for IE8)
2741
- name: function () {
2742
- if (this.func.name) {
2743
- return this.func.name;
2744
- } else {
2745
- return this.func.toString().match(/^function\s*([^\s(]+)/)[1];
2746
- }
2747
- },
2798
+ // only used for testing and debugging
2799
+ name() {
2800
+ return this.func.name;
2801
+ }
2802
+
2748
2803
  // returns a string representing current node structure
2749
- repr: function () {
2804
+ repr() {
2750
2805
  return this.name() + "(" + _.map(this.args(), function (arg) {
2751
- return _.isString(arg) ? arg : arg.repr();
2806
+ return _.isString(arg) || _.isNumber(arg) ? arg : arg.repr();
2752
2807
  }).join(",") + ")";
2753
- },
2808
+ }
2809
+
2754
2810
  // removes all negative signs
2755
- strip: function () {
2811
+ strip() {
2756
2812
  return this.recurse("strip");
2757
- },
2813
+ }
2814
+
2758
2815
  // canonically reorders all commutative elements
2759
- normalize: function () {
2816
+ normalize() {
2760
2817
  return this.recurse("normalize");
2761
- },
2818
+ }
2819
+
2762
2820
  // expands the expression
2763
- expand: function () {
2821
+ expand() {
2764
2822
  return this.recurse("expand");
2765
- },
2823
+ }
2824
+
2766
2825
  // naively factors out like terms
2767
- factor: function (options) {
2826
+ factor(options) {
2768
2827
  return this.recurse("factor", options);
2769
- },
2828
+ }
2829
+
2770
2830
  // collect all like terms
2771
- collect: function (options) {
2831
+ collect(options) {
2772
2832
  return this.recurse("collect", options);
2773
- },
2833
+ }
2834
+
2774
2835
  // strict syntactic equality check
2775
- equals: function (other) {
2836
+ equals(other) {
2776
2837
  return this.normalize().print() === other.normalize().print();
2777
- },
2838
+ }
2839
+
2778
2840
  // expand and collect until the expression no longer changes
2779
- simplify: function (options) {
2780
- options = _.extend({
2841
+ simplify(options) {
2842
+ options = _extends({
2781
2843
  once: false
2782
2844
  }, options);
2783
2845
 
@@ -2791,7 +2853,7 @@ _.extend(Expr.prototype, {
2791
2853
  }
2792
2854
 
2793
2855
  // Attempt to expand and collect
2794
- var step3 = step2.expand(options);
2856
+ var step3 = step2.expand();
2795
2857
  var step4 = step3.collect(options);
2796
2858
 
2797
2859
  // Rollback if collect didn't do anything
@@ -2806,62 +2868,69 @@ _.extend(Expr.prototype, {
2806
2868
  } else {
2807
2869
  return simplified.simplify(options);
2808
2870
  }
2809
- },
2871
+ }
2872
+
2810
2873
  // check whether this expression is simplified
2811
- isSimplified: function () {
2874
+ isSimplified() {
2812
2875
  return this.equals(this.simplify());
2813
- },
2876
+ }
2877
+
2814
2878
  // return the child nodes of this node
2815
- exprArgs: function () {
2816
- return _.filter(this.args(), function (arg) {
2817
- return arg instanceof Expr;
2818
- });
2819
- },
2879
+ exprArgs() {
2880
+ return this.args().filter(isExpr);
2881
+ }
2882
+
2820
2883
  // return the variables (function and non) within the expression
2821
- getVars: function (excludeFunc) {
2884
+ getVars(excludeFunc) {
2822
2885
  return _.uniq(_.flatten(_.invoke(this.exprArgs(), "getVars", excludeFunc))).sort();
2823
- },
2824
- getConsts: function () {
2886
+ }
2887
+ getConsts() {
2825
2888
  return _.uniq(_.flatten(_.invoke(this.exprArgs(), "getConsts"))).sort();
2826
- },
2827
- getUnits: function () {
2889
+ }
2890
+ getUnits() {
2828
2891
  return _.flatten(_.invoke(this.exprArgs(), "getUnits"));
2829
- },
2892
+ }
2893
+
2830
2894
  // check whether this expression node is of a particular type
2831
- is: function (func) {
2895
+ is(func) {
2832
2896
  return this instanceof func;
2833
- },
2897
+ }
2898
+
2834
2899
  // check whether this expression has a particular node type
2835
- has: function (func) {
2900
+ has(func) {
2836
2901
  if (this instanceof func) {
2837
2902
  return true;
2838
2903
  }
2839
2904
  return _.any(this.exprArgs(), function (arg) {
2840
2905
  return arg.has(func);
2841
2906
  });
2842
- },
2907
+ }
2908
+
2843
2909
  // raise this expression to a given exponent
2844
2910
  // most useful for eventually implementing i^3 = -i, etc.
2845
- raiseToThe: function (exp) {
2911
+ raiseToThe(exp, options) {
2846
2912
  return new Pow(this, exp);
2847
- },
2913
+ }
2914
+
2848
2915
  // does this expression have a specific rendering hint?
2849
2916
  // rendering hints are picked up while parsing, but are lost during transformations
2850
- isSubtract: function () {
2917
+ isSubtract() {
2851
2918
  return false;
2852
- },
2853
- isDivide: function () {
2919
+ }
2920
+ isDivide() {
2854
2921
  return false;
2855
- },
2856
- isRoot: function () {
2922
+ }
2923
+ isRoot() {
2857
2924
  return false;
2858
- },
2925
+ }
2926
+
2859
2927
  // whether this node needs an explicit multiplication sign if following a Num
2860
- needsExplicitMul: function () {
2861
- return this.args()[0].needsExplicitMul();
2862
- },
2928
+ needsExplicitMul() {
2929
+ return this.exprArgs()[0].needsExplicitMul();
2930
+ }
2931
+
2863
2932
  // check that the variables in both expressions are the same
2864
- sameVars: function (other) {
2933
+ sameVars(other) {
2865
2934
  var vars1 = this.getVars();
2866
2935
  var vars2 = other.getVars();
2867
2936
 
@@ -2879,11 +2948,12 @@ _.extend(Expr.prototype, {
2879
2948
  equal: equal,
2880
2949
  equalIgnoringCase: equalIgnoringCase
2881
2950
  };
2882
- },
2951
+ }
2952
+
2883
2953
  // semantic equality check, call after sameVars() to avoid potential false positives
2884
2954
  // plug in random numbers for the variables in both expressions
2885
2955
  // if they both consistently evaluate the same, then they're the same
2886
- compare: function (other) {
2956
+ compare(other) {
2887
2957
  // equation comparisons are handled by Eq.compare()
2888
2958
  if (other instanceof Eq) {
2889
2959
  return false;
@@ -2954,14 +3024,14 @@ _.extend(Expr.prototype, {
2954
3024
  _.each(varList, function (v) {
2955
3025
  vars[v] = useFloats ? randomFloat(-range, range) : _.random(-range, range);
2956
3026
  });
2957
- var equal;
3027
+ let equal;
2958
3028
  if (expr1.has(Func) || expr2.has(Func) || expr1.has(Unit) || expr2.has(Unit)) {
2959
- var result1 = expr1.partialEval(vars);
2960
- var result2 = expr2.partialEval(vars);
3029
+ const result1 = expr1.partialEval(vars);
3030
+ const result2 = expr2.partialEval(vars);
2961
3031
  equal = result1.simplify().equals(result2.simplify());
2962
3032
  } else {
2963
- var result1 = expr1.eval(vars);
2964
- var result2 = expr2.eval(vars);
3033
+ const result1 = expr1.eval(vars);
3034
+ const result2 = expr2.eval(vars);
2965
3035
  equal = equalNumbers(result1, result2);
2966
3036
  }
2967
3037
  if (!equal) {
@@ -2969,52 +3039,65 @@ _.extend(Expr.prototype, {
2969
3039
  }
2970
3040
  }
2971
3041
  return true;
2972
- },
3042
+ }
3043
+
2973
3044
  // evaluate as much of the expression as possible
2974
- partialEval: function (vars) {
3045
+ partialEval(vars) {
2975
3046
  if (this instanceof Unit) {
2976
3047
  return this;
2977
3048
  } else if (!this.has(Func)) {
2978
- return new Float(this.eval(vars).toFixed(TOLERANCE)).collect();
3049
+ return new Float(+this.eval(vars).toFixed(TOLERANCE)).collect();
2979
3050
  } else if (this instanceof Func) {
2980
3051
  return new Func(this.symbol, this.arg.partialEval(vars));
2981
3052
  } else {
2982
3053
  return this.recurse("partialEval", vars);
2983
3054
  }
2984
- },
3055
+ }
3056
+
2985
3057
  // check that the structure of both expressions is the same
2986
3058
  // all negative signs are stripped and the expressions are converted to
2987
3059
  // a canonical commutative form
2988
3060
  // should only be done after compare() returns true to avoid false positives
2989
- sameForm: function (other) {
3061
+ sameForm(other) {
2990
3062
  return this.strip().equals(other.strip());
2991
- },
3063
+ }
3064
+
2992
3065
  // returns the GCD of this expression and the given factor
2993
- findGCD: function (factor) {
2994
- return this.equals(factor) ? factor : Num.One;
2995
- },
3066
+ findGCD(factor) {
3067
+ return this.equals(factor) ? factor : NumOne;
3068
+ }
3069
+
2996
3070
  // return this expression's denominator
2997
- getDenominator: function () {
2998
- return Num.One;
2999
- },
3071
+ getDenominator() {
3072
+ return NumOne;
3073
+ }
3074
+
3000
3075
  // return this expression as a Mul
3001
- asMul: function () {
3002
- return new Mul(Num.One, this);
3003
- },
3076
+ asMul() {
3077
+ return new Mul(NumOne, this);
3078
+ }
3079
+
3004
3080
  // TODO(alex): rename to isDefinitePositive or similar?
3005
3081
  // return whether this expression is 100% positive
3006
- isPositive: abstract,
3082
+ isPositive() {
3083
+ throw new Error("Abstract method - must override for expr: " +
3084
+ // eslint-disable-next-line @babel/no-invalid-this
3085
+ this.print());
3086
+ }
3087
+
3007
3088
  // TODO(alex): rename to hasNegativeSign or similar?
3008
3089
  // return whether this expression has a negative sign
3009
- isNegative: function () {
3090
+ isNegative() {
3010
3091
  return false;
3011
- },
3092
+ }
3093
+
3012
3094
  // return a factor of this expression that is 100% positive
3013
- asPositiveFactor: function () {
3014
- return this.isPositive() ? this : Num.One;
3015
- },
3095
+ asPositiveFactor() {
3096
+ return this.isPositive() ? this : NumOne;
3097
+ }
3098
+
3016
3099
  // return a copy of the expression with a new hint set (preserves hints)
3017
- addHint: function (hint) {
3100
+ addHint(hint) {
3018
3101
  if (!hint) {
3019
3102
  return this;
3020
3103
  }
@@ -3022,141 +3105,139 @@ _.extend(Expr.prototype, {
3022
3105
  expr.hints = _.clone(this.hints);
3023
3106
  expr.hints[hint] = true;
3024
3107
  return expr;
3025
- },
3026
- hints: {
3027
- parens: false
3028
- },
3029
- // currently unused!
3030
- asExpr: function () {
3031
- return this;
3032
- },
3108
+ }
3109
+
3033
3110
  // complete parse by performing a few necessary transformations
3034
- completeParse: function () {
3111
+ completeParse() {
3035
3112
  return this.recurse("completeParse");
3036
- },
3037
- abs: abstract,
3038
- negate: function () {
3039
- return new Mul(Num.Neg, this);
3040
3113
  }
3041
- });
3114
+ abs() {
3115
+ throw new Error("Abstract method - must override for expr: " +
3116
+ // eslint-disable-next-line @babel/no-invalid-this
3117
+ this.print());
3118
+ }
3119
+ negate() {
3120
+ return new Mul(NumNeg, this);
3121
+ }
3122
+ }
3042
3123
 
3043
3124
  /* abstract sequence node */
3044
- function Seq() {}
3045
- Seq.prototype = new Expr();
3046
- _.extend(Seq.prototype, {
3047
- args: function () {
3125
+ class Seq extends Expr {
3126
+ // TODO(kevinb): Update this use `...args: Expr[]`
3127
+ constructor(...args) {
3128
+ super();
3129
+ // This should always have at least two terms.
3130
+ // TODO(kevinb): Try enforcing this at the type-level using [T, T, T[]]
3131
+ this.terms = void 0;
3132
+ if (args.length === 1) {
3133
+ this.terms = args[0];
3134
+ } else {
3135
+ this.terms = args;
3136
+ }
3137
+ }
3138
+ args() {
3048
3139
  return this.terms;
3049
- },
3050
- normalize: function () {
3051
- var terms = _.sortBy(_.invoke(this.terms, "normalize"), function (term) {
3140
+ }
3141
+ normalize() {
3142
+ var terms = _.sortBy(_.invoke(this.terms, "normalize"), term => {
3052
3143
  return term.print();
3053
3144
  });
3054
3145
  return new this.func(terms);
3055
- },
3056
- expand: function () {
3146
+ }
3147
+ expand() {
3057
3148
  return this.recurse("expand").flatten();
3058
- },
3149
+ }
3150
+
3059
3151
  // partition the sequence into its numeric and non-numeric parts
3060
3152
  // makes no guarantees about the validity of either part!
3061
- partition: function () {
3062
- var terms = _.groupBy(this.terms, function (term) {
3153
+ partition() {
3154
+ var [numbers, others] = partition(this.terms, term => {
3063
3155
  return term instanceof Num;
3064
3156
  });
3065
-
3066
- // XXX using a boolean as a key just converts it to a string. I don't
3067
- // think this code was written with that in mind. Probably doesn't
3068
- // matter except for readability.
3069
- var numbers = terms[true] || [];
3070
- var others = terms[false] || [];
3071
3157
  return [new this.func(numbers), new this.func(others)];
3072
- },
3158
+ }
3159
+
3073
3160
  // ensure that sequences have 2+ terms and no nested sequences of the same type
3074
3161
  // this is a shallow flattening and will return a non-Seq if terms.length <= 1
3075
- flatten: function () {
3162
+ flatten() {
3076
3163
  var type = this;
3077
- var terms = _.reject(this.terms, function (term) {
3164
+ var terms = _.reject(this.terms, term => {
3165
+ // @ts-expect-error: `identity` is defined on Add and Mul but doesn't
3166
+ // exist on Seq itself.
3078
3167
  return term.equals(type.identity);
3079
3168
  });
3080
3169
  if (terms.length === 0) {
3170
+ // @ts-expect-error: `identity` is defined on Add and Mul but doesn't
3171
+ // exist on Seq itself.
3081
3172
  return type.identity;
3082
3173
  }
3083
3174
  if (terms.length === 1) {
3084
3175
  return terms[0];
3085
3176
  }
3086
- var grouped = _.groupBy(terms, function (term) {
3087
- return term instanceof type.func;
3088
- });
3089
3177
 
3090
3178
  // same contains the children which are Seqs of the same type as this Seq
3091
- var same = grouped[true] || [];
3092
- var others = grouped[false] || [];
3179
+ const [same, others] = partition(terms, term => {
3180
+ return term instanceof type.func;
3181
+ });
3093
3182
  var flattened = others.concat(_.flatten(_.pluck(same, "terms"), /* shallow: */true));
3094
3183
  return new type.func(flattened);
3095
- },
3096
- // the identity associated with the sequence
3097
- identity: undefined,
3184
+ }
3185
+
3098
3186
  // reduce a numeric sequence to a Num
3099
- reduce: abstract,
3100
- isPositive: function () {
3187
+
3188
+ isPositive() {
3101
3189
  var terms = _.invoke(this.terms, "collect");
3102
3190
  return _.all(_.invoke(terms, "isPositive"));
3103
- },
3191
+ }
3192
+
3104
3193
  // return a new Seq with a given term replaced by a different term
3105
3194
  // (or array of terms). given term can be passed directly, or by index
3106
3195
  // if no new term is provided, the old one is simply removed
3107
- replace: function (oldTerm, newTerm) {
3108
- var index;
3109
- if (oldTerm instanceof Expr) {
3110
- index = _.indexOf(this.terms, oldTerm);
3111
- } else {
3112
- index = oldTerm;
3113
- }
3196
+ replace(oldTerm, newTerm) {
3197
+ const index = oldTerm instanceof Expr ? _.indexOf(this.terms, oldTerm) : oldTerm;
3114
3198
  var newTerms = [];
3115
- if (_.isArray(newTerm)) {
3199
+ if (Array.isArray(newTerm)) {
3116
3200
  newTerms = newTerm;
3117
3201
  } else if (newTerm) {
3118
3202
  newTerms = [newTerm];
3119
3203
  }
3120
3204
  var terms = this.terms.slice(0, index).concat(newTerms).concat(this.terms.slice(index + 1));
3121
3205
  return new this.func(terms);
3122
- },
3206
+ }
3207
+
3123
3208
  // syntactic sugar for replace()
3124
- remove: function (term) {
3209
+ remove(term) {
3125
3210
  return this.replace(term);
3126
- },
3127
- getDenominator: function () {
3211
+ }
3212
+ getDenominator() {
3128
3213
  // TODO(alex): find and return LCM
3129
3214
  return new Mul(_.invoke(this.terms, "getDenominator")).flatten();
3130
3215
  }
3131
- });
3216
+ }
3132
3217
 
3133
3218
  /* sequence of additive terms */
3134
- function Add() {
3135
- if (arguments.length === 1) {
3136
- this.terms = arguments[0];
3137
- } else {
3138
- this.terms = _.toArray(arguments);
3219
+ class Add extends Seq {
3220
+ constructor(...args) {
3221
+ super(...args);
3222
+ this.identity = NumZero;
3223
+ this.func = Add;
3139
3224
  }
3140
- }
3141
- Add.prototype = new Seq();
3142
- _.extend(Add.prototype, {
3143
- func: Add,
3144
- eval: function (vars, options) {
3145
- return _.reduce(this.terms, function (memo, term) {
3225
+ eval(vars = {}, options) {
3226
+ return _.reduce(this.terms, (memo, term) => {
3146
3227
  return memo + term.eval(vars, options);
3147
3228
  }, 0);
3148
- },
3149
- codegen: function () {
3150
- return _.map(this.terms, function (term) {
3229
+ }
3230
+ codegen() {
3231
+ return _.map(this.terms, term => {
3151
3232
  return "(" + term.codegen() + ")";
3152
3233
  }).join(" + ") || "0";
3153
- },
3154
- print: function () {
3234
+ }
3235
+ print() {
3155
3236
  return _.invoke(this.terms, "print").join("+");
3156
- },
3157
- tex: function () {
3158
- var tex = "";
3159
- _.each(this.terms, function (term) {
3237
+ }
3238
+ tex() {
3239
+ let tex = "";
3240
+ _.each(this.terms, term => {
3160
3241
  if (!tex || term.isSubtract()) {
3161
3242
  tex += term.tex();
3162
3243
  } else {
@@ -3164,28 +3245,26 @@ _.extend(Add.prototype, {
3164
3245
  }
3165
3246
  });
3166
3247
  return tex;
3167
- },
3168
- collect: function (options) {
3248
+ }
3249
+ collect(options) {
3169
3250
  var terms = _.invoke(this.terms, "collect", options);
3170
-
3171
- // [Expr expr, Num coefficient]
3172
3251
  var pairs = [];
3173
- _.each(terms, function (term) {
3252
+ _.each(terms, term => {
3174
3253
  if (term instanceof Mul) {
3175
3254
  var muls = term.partition();
3176
3255
  pairs.push([muls[1].flatten(), muls[0].reduce(options)]);
3177
3256
  } else if (term instanceof Num) {
3178
- pairs.push([Num.One, term]);
3257
+ pairs.push([NumOne, term]);
3179
3258
  } else {
3180
- pairs.push([term, Num.One]);
3259
+ pairs.push([term, NumOne]);
3181
3260
  }
3182
3261
  });
3183
3262
 
3184
3263
  // { (Expr expr).print(): [[Expr expr, Num coefficient]] }
3185
- var grouped = _.groupBy(pairs, function (pair) {
3264
+ var grouped = _.groupBy(pairs, pair => {
3186
3265
  return pair[0].normalize().print();
3187
3266
  });
3188
- var collected = _.compact(_.map(grouped, function (pairs) {
3267
+ var collected = _.compact(_.map(grouped, pairs => {
3189
3268
  var expr = pairs[0][0];
3190
3269
  var sum = new Add(_.zip.apply(_, pairs)[1]);
3191
3270
  var coefficient = sum.reduce(options);
@@ -3196,90 +3275,94 @@ _.extend(Add.prototype, {
3196
3275
  // e.g. x*sin^2(y) + x*cos^2(y) -> x
3197
3276
 
3198
3277
  return new Add(collected).flatten();
3199
- },
3278
+ }
3279
+
3200
3280
  // naively factor out anything that is common to all terms
3201
3281
  // if options.keepNegative is specified, won't factor out a common -1
3202
- factor: function (options) {
3203
- options = _.extend({
3204
- keepNegative: false
3205
- }, options);
3206
- var terms = _.invoke(this.terms, "collect");
3207
- var factors;
3282
+ factor(options = {
3283
+ keepNegative: false
3284
+ }) {
3285
+ const terms = this.terms.map(term => term.collect());
3286
+ let factors;
3208
3287
  if (terms[0] instanceof Mul) {
3209
3288
  factors = terms[0].terms;
3210
3289
  } else {
3211
3290
  factors = [terms[0]];
3212
3291
  }
3213
- _.each(_.rest(this.terms), function (term) {
3214
- factors = _.map(factors, function (factor) {
3292
+ _.each(_.rest(this.terms), term => {
3293
+ factors = _.map(factors, factor => {
3215
3294
  return term.findGCD(factor);
3216
3295
  });
3217
3296
  });
3218
3297
  if (!options.keepNegative && this.isNegative()) {
3219
- factors.push(Num.Neg);
3298
+ factors.push(NumNeg);
3220
3299
  }
3221
- factors = new Mul(factors).flatten().collect();
3222
- var remainder = _.map(terms, function (term) {
3223
- return Mul.handleDivide(term, factors).simplify();
3224
- });
3225
- remainder = new Add(remainder).flatten();
3226
- return Mul.createOrAppend(factors, remainder).flatten();
3227
- },
3228
- reduce: function (options) {
3229
- return _.reduce(this.terms, function (memo, term) {
3300
+ const left = new Mul(factors).flatten().collect();
3301
+ const remainder = terms.map(term => Mul.handleDivide(term, left).simplify());
3302
+ const right = new Add(remainder).flatten();
3303
+ return Mul.createOrAppend(left, right).flatten();
3304
+ }
3305
+ reduce(options) {
3306
+ return _.reduce(this.terms, (memo, term) => {
3230
3307
  return memo.add(term, options);
3231
3308
  }, this.identity);
3232
- },
3233
- needsExplicitMul: function () {
3309
+ }
3310
+ needsExplicitMul() {
3234
3311
  return false;
3235
- },
3236
- isNegative: function () {
3312
+ }
3313
+ isNegative() {
3237
3314
  var terms = _.invoke(this.terms, "collect");
3238
3315
  return _.all(_.invoke(terms, "isNegative"));
3239
- },
3240
- negate: function () {
3316
+ }
3317
+ negate() {
3241
3318
  return new Add(_.invoke(this.terms, "negate"));
3242
3319
  }
3243
- });
3244
3320
 
3245
- /* sequence of multiplicative terms */
3246
- function Mul() {
3247
- if (arguments.length === 1) {
3248
- this.terms = arguments[0];
3249
- } else {
3250
- this.terms = _.toArray(arguments);
3321
+ // create a new sequence unless left is already one (returns a copy)
3322
+ static createOrAppend(left, right) {
3323
+ if (left instanceof Add) {
3324
+ return new Add(left.terms.concat(right));
3325
+ } else {
3326
+ return new Add(left, right);
3327
+ }
3251
3328
  }
3252
3329
  }
3253
- Mul.prototype = new Seq();
3254
- _.extend(Mul.prototype, {
3255
- func: Mul,
3256
- eval: function (vars, options) {
3257
- return _.reduce(this.terms, function (memo, term) {
3330
+
3331
+ /* sequence of multiplicative terms */
3332
+ class Mul extends Seq {
3333
+ constructor(...args) {
3334
+ super(...args);
3335
+ this.identity = NumOne;
3336
+ this.func = Mul;
3337
+ }
3338
+ eval(vars = {}, options) {
3339
+ return _.reduce(this.terms, (memo, term) => {
3258
3340
  return memo * term.eval(vars, options);
3259
3341
  }, 1);
3260
- },
3261
- codegen: function () {
3262
- return _.map(this.terms, function (term) {
3342
+ }
3343
+ codegen() {
3344
+ return _.map(this.terms, term => {
3263
3345
  return "(" + term.codegen() + ")";
3264
3346
  }).join(" * ") || "0";
3265
- },
3266
- print: function () {
3267
- return _.map(this.terms, function (term) {
3347
+ }
3348
+ print() {
3349
+ return _.map(this.terms, term => {
3268
3350
  return term instanceof Add ? "(" + term.print() + ")" : term.print();
3269
3351
  }).join("*");
3270
- },
3271
- getUnits: function () {
3272
- var tmUnits = _(this.terms).chain().map(function (term) {
3352
+ }
3353
+ getUnits() {
3354
+ var tmUnits = _(this.terms).chain().map(term => {
3273
3355
  return term.getUnits();
3274
3356
  }).flatten().value();
3275
3357
  tmUnits.sort((a, b) => a.unit.localeCompare(b.unit));
3276
3358
  return tmUnits;
3277
- },
3359
+ }
3360
+
3278
3361
  // since we don't care about commutativity, we can render a Mul any way we choose
3279
3362
  // so we follow convention: first any negatives, then any numbers, then everything else
3280
- tex: function () {
3363
+ tex() {
3281
3364
  var cdot = " \\cdot ";
3282
- var terms = _.groupBy(this.terms, function (term) {
3365
+ var terms = _.groupBy(this.terms, term => {
3283
3366
  if (term.isDivide()) {
3284
3367
  return "inverse";
3285
3368
  } else if (term instanceof Num) {
@@ -3306,12 +3389,11 @@ _.extend(Mul.prototype, {
3306
3389
  return numbers[i].tex() + new Mul(newTerms).tex();
3307
3390
  }
3308
3391
  }
3309
- numbers = _.compact(_.map(numbers, function (term) {
3310
- var hasDenom = term instanceof Rational && !(term instanceof Int);
3392
+ numbers = _.compact(_.map(numbers, term => {
3311
3393
  var shouldPushDown = !term.hints.fraction || inverses.length > 0;
3312
- if (hasDenom && shouldPushDown) {
3394
+ if (term instanceof Rational && !(term instanceof Int) && shouldPushDown) {
3313
3395
  // e.g. 3x/4 -> 3/4*x (internally) -> 3x/4 (rendered)
3314
- inverses.push(new Pow(new Int(term.d), Num.Div));
3396
+ inverses.push(new Pow(new Int(term.d), NumDiv));
3315
3397
  var number = new Int(term.n);
3316
3398
  number.hints = term.hints;
3317
3399
  return _.any(term.hints) ? number : null;
@@ -3324,7 +3406,7 @@ _.extend(Mul.prototype, {
3324
3406
  numerator = others[0].tex();
3325
3407
  } else {
3326
3408
  var tex = "";
3327
- _.each(numbers, function (term) {
3409
+ _.each(numbers, term => {
3328
3410
  if (term.hints.subtract && term.hints.entered) {
3329
3411
  negatives += "-";
3330
3412
  tex += (tex ? cdot : "") + term.abs().tex();
@@ -3337,7 +3419,7 @@ _.extend(Mul.prototype, {
3337
3419
  tex += (tex ? cdot : "") + term.tex();
3338
3420
  }
3339
3421
  });
3340
- _.each(others, function (term) {
3422
+ _.each(others, term => {
3341
3423
  if (term.needsExplicitMul()) {
3342
3424
  // e.g. 2*2^3 -> 2(dot)2^3
3343
3425
  tex += (tex ? cdot : "") + term.tex();
@@ -3357,76 +3439,69 @@ _.extend(Mul.prototype, {
3357
3439
  var denominator = new Mul(_.invoke(inverses, "asDivide")).flatten().tex();
3358
3440
  return negatives + "\\frac{" + numerator + "}{" + denominator + "}";
3359
3441
  }
3360
- },
3361
- strip: function () {
3362
- var terms = _.map(this.terms, function (term) {
3442
+ }
3443
+ strip() {
3444
+ var terms = _.map(this.terms, term => {
3363
3445
  return term instanceof Num ? term.abs() : term.strip();
3364
3446
  });
3365
3447
  return new Mul(terms).flatten();
3366
- },
3448
+ }
3449
+
3367
3450
  // expand numerator and denominator separately
3368
- expand: function () {
3369
- var isAdd = function isAdd(term) {
3370
- return term instanceof Add;
3371
- };
3372
- var isInverse = function isInverse(term) {
3451
+ expand() {
3452
+ const isInverse = function isInverse(term) {
3373
3453
  return term instanceof Pow && term.exp.isNegative();
3374
3454
  };
3375
- var isInverseAdd = function isInverseAdd(term) {
3455
+ const isInverseAdd = function isInverseAdd(term) {
3376
3456
  return isInverse(term) && isAdd(term.base);
3377
3457
  };
3378
- var mul = this.recurse("expand").flatten();
3379
- var hasAdd = _.any(mul.terms, isAdd);
3380
- var hasInverseAdd = _.any(mul.terms, isInverseAdd);
3458
+ const mul = this.recurse("expand").flatten();
3459
+ const factors = getFactors(mul);
3460
+ const hasAdd = _.any(factors, isAdd);
3461
+ const hasInverseAdd = _.any(factors, isInverseAdd);
3381
3462
  if (!(hasAdd || hasInverseAdd)) {
3382
3463
  return mul;
3383
3464
  }
3384
- var terms = _.groupBy(mul.terms, isInverse);
3385
- var normals = terms[false] || [];
3386
- var inverses = terms[true] || [];
3465
+ let [inverses, normals] = partition(factors, isInverse);
3387
3466
  if (hasAdd) {
3388
- var grouped = _.groupBy(normals, isAdd);
3389
- var adds = grouped[true] || [];
3390
- var others = grouped[false] || [];
3467
+ const [adds, others] = partition(normals, isAdd);
3391
3468
 
3392
3469
  // loop over each additive sequence
3393
- var expanded = _.reduce(adds, function (expanded, add) {
3470
+ const expanded = _.reduce(adds, function (expanded, add) {
3394
3471
  // loop over each expanded array of terms
3395
3472
  return _.reduce(expanded, function (temp, array) {
3396
3473
  // loop over each additive sequence's terms
3397
- return temp.concat(_.map(add.terms, function (term) {
3398
- return array.concat(term);
3399
- }));
3474
+ return temp.concat(_.map(add.terms, term => array.concat(term)));
3400
3475
  }, []);
3401
3476
  }, [[]]);
3402
3477
 
3403
3478
  // join each fully expanded array of factors with remaining multiplicative factors
3404
- var muls = _.map(expanded, function (array) {
3479
+ const muls = _.map(expanded, function (array) {
3405
3480
  return new Mul(others.concat(array)).flatten();
3406
3481
  });
3407
3482
  normals = [new Add(muls)];
3408
3483
  }
3409
3484
  if (hasInverseAdd) {
3410
- var denominator = new Mul(_.invoke(inverses, "getDenominator")).flatten();
3411
- inverses = [new Pow(denominator.expand(), Num.Div)];
3485
+ const denominator = new Mul(_.invoke(inverses, "getDenominator")).flatten();
3486
+ inverses = [new Pow(denominator.expand(), NumDiv)];
3412
3487
  }
3413
3488
  return new Mul(normals.concat(inverses)).flatten();
3414
- },
3415
- factor: function (options) {
3489
+ }
3490
+ factor(options) {
3416
3491
  var factored = this.recurse("factor", options).flatten();
3417
3492
  if (!(factored instanceof Mul)) {
3418
3493
  return factored;
3419
3494
  }
3420
3495
 
3421
3496
  // Combine any factored out Rationals into one, but don't collect
3422
- var grouped = _.groupBy(factored.terms, function (term) {
3497
+ var [rationals, others] = partition(factored.terms, term => {
3423
3498
  return term instanceof Rational;
3424
3499
  });
3425
3500
 
3426
3501
  // Could also accomplish this by passing a new option
3427
3502
  // e.g. return memo.mul(term, {autocollect: false});
3428
3503
  // TODO(alex): Decide whether this is a good use of options or not
3429
- var rational = _.reduce(grouped[true], function (memo, term) {
3504
+ const ratObj = _.reduce(rationals, (memo, term) => {
3430
3505
  return {
3431
3506
  n: memo.n * term.n,
3432
3507
  d: memo.d * term.d
@@ -3435,47 +3510,39 @@ _.extend(Mul.prototype, {
3435
3510
  n: 1,
3436
3511
  d: 1
3437
3512
  });
3438
- if (rational.d === 1) {
3439
- rational = new Int(rational.n);
3440
- } else {
3441
- rational = new Rational(rational.n, rational.d);
3442
- }
3443
- return new Mul((grouped[false] || []).concat(rational)).flatten();
3444
- },
3445
- collect: function (options) {
3513
+ const rational = ratObj.d === 1 ? new Int(ratObj.n) : new Rational(ratObj.n, ratObj.d);
3514
+ return new Mul(others.concat(rational)).flatten();
3515
+ }
3516
+ collect(options) {
3446
3517
  var partitioned = this.recurse("collect", options).partition();
3447
3518
  var number = partitioned[0].reduce(options);
3448
3519
 
3449
3520
  // e.g. 0*x -> 0
3450
3521
  if (number.eval() === 0) {
3451
- return Num.Zero;
3522
+ return NumZero;
3452
3523
  }
3453
- var others = partitioned[1].flatten();
3524
+ const other = partitioned[1].flatten();
3454
3525
 
3455
3526
  // e.g. 2*2 -> 4
3456
3527
  // e.g. 2*2*x -> 4*x
3457
- if (!(others instanceof Mul)) {
3458
- return new Mul(number, others).flatten();
3528
+ if (!(other instanceof Mul)) {
3529
+ return new Mul(number, other).flatten();
3459
3530
  }
3460
- others = others.terms;
3461
-
3462
- // [Expr base, Expr exp]
3531
+ const others = other.terms;
3463
3532
  var pairs = [];
3464
- _.each(others, function (term) {
3533
+ _.each(others, term => {
3465
3534
  if (term instanceof Pow) {
3466
3535
  pairs.push([term.base, term.exp]);
3467
3536
  } else {
3468
- pairs.push([term, Num.One]);
3537
+ pairs.push([term, NumOne]);
3469
3538
  }
3470
3539
  });
3471
3540
 
3472
3541
  // {(Expr base).print(): [[Expr base, Expr exp]]}
3473
- var grouped = _.groupBy(pairs, function (pair) {
3542
+ var grouped = _.groupBy(pairs, pair => {
3474
3543
  return pair[0].normalize().print();
3475
3544
  });
3476
-
3477
- // [[Expr base, Expr exp]]
3478
- var summed = _.compact(_.map(grouped, function (pairs) {
3545
+ var summed = _.compact(_.map(grouped, pairs => {
3479
3546
  var base = pairs[0][0];
3480
3547
  var sum = new Add(_.zip.apply(_, pairs)[1]);
3481
3548
  var exp = sum.collect(options);
@@ -3487,7 +3554,7 @@ _.extend(Mul.prototype, {
3487
3554
  }));
3488
3555
 
3489
3556
  // XXX `pairs` is shadowed four or five times in this function
3490
- var pairs = _.groupBy(summed, function (pair) {
3557
+ const groupedPairs = _.groupBy(summed, pair => {
3491
3558
  if (pair[0] instanceof Trig && pair[0].isBasic()) {
3492
3559
  return "trig";
3493
3560
  } else if (pair[0] instanceof Log) {
@@ -3496,26 +3563,26 @@ _.extend(Mul.prototype, {
3496
3563
  return "expr";
3497
3564
  }
3498
3565
  });
3499
- var trigs = pairs.trig || [];
3500
- var logs = pairs.log || [];
3501
- var exprs = pairs.expr || [];
3566
+ let trigs = groupedPairs.trig || [];
3567
+ let logs = groupedPairs.log || [];
3568
+ const exprs = groupedPairs.expr || [];
3502
3569
  if (trigs.length > 1) {
3503
3570
  // combine sines and cosines into other trig functions
3504
3571
 
3505
3572
  // {Trig.arg.print(): [[Trig base, Expr exp]]}
3506
- var byArg = _.groupBy(trigs, function (pair) {
3573
+ var byArg = _.groupBy(trigs, pair => {
3507
3574
  return pair[0].arg.normalize().print();
3508
3575
  });
3509
3576
  trigs = [];
3510
- _.each(byArg, function (pairs) {
3511
- var arg = pairs[0][0].arg;
3577
+ _.each(byArg, pairs => {
3578
+ const arg = pairs[0][0].arg;
3512
3579
 
3513
3580
  // {Trig.type: Expr exp}
3514
- var funcs = {
3515
- sin: Num.Zero,
3516
- cos: Num.Zero
3581
+ let funcs = {
3582
+ sin: NumZero,
3583
+ cos: NumZero
3517
3584
  };
3518
- _.each(pairs, function (pair) {
3585
+ _.each(pairs, pair => {
3519
3586
  funcs[pair[0].type] = pair[1];
3520
3587
  });
3521
3588
  if (Mul.handleNegative(funcs.sin).collect(options).equals(funcs.cos)) {
@@ -3534,7 +3601,7 @@ _.extend(Mul.prototype, {
3534
3601
  // TODO(alex): combine even if exponents not a perfect match
3535
3602
  // TODO(alex): transform 1/sin and 1/cos into csc and sec
3536
3603
 
3537
- _.each(funcs, function (exp, type) {
3604
+ _.each(funcs, (exp, type) => {
3538
3605
  trigs.push([new Trig(type, arg), exp]);
3539
3606
  });
3540
3607
  });
@@ -3543,11 +3610,11 @@ _.extend(Mul.prototype, {
3543
3610
  // combine logs with the same base
3544
3611
 
3545
3612
  // {Log.base.print(): [[Log base, Expr exp]]}
3546
- var byBase = _.groupBy(logs, function (pair) {
3613
+ var byBase = _.groupBy(logs, pair => {
3547
3614
  return pair[0].base.normalize().print();
3548
3615
  });
3549
3616
  logs = [];
3550
- _.each(byBase, function (pairs) {
3617
+ _.each(byBase, pairs => {
3551
3618
  // only combine two logs of the same base, otherwise commutative
3552
3619
  // differences result in different equally valid output
3553
3620
  // e.g. ln(x)/ln(z)*ln(y) -> log_z(x)*ln(y)
@@ -3567,25 +3634,25 @@ _.extend(Mul.prototype, {
3567
3634
  // TODO(alex): combine if all inverses are the same e.g. ln(y)*ln(z)/ln(x)/ln(x)
3568
3635
  }
3569
3636
 
3570
- pairs = trigs.concat(logs).concat(exprs);
3571
- var collected = _.map(pairs, function (pair) {
3637
+ var collected = _.map([...trigs, ...logs, ...exprs], pair => {
3572
3638
  return new Pow(pair[0], pair[1]).collect(options);
3573
3639
  });
3574
3640
  return new Mul([number].concat(collected)).flatten();
3575
- },
3576
- isSubtract: function () {
3577
- return _.any(this.terms, function (term) {
3578
- return term instanceof Num && term.hints.subtract;
3641
+ }
3642
+ isSubtract() {
3643
+ return _.any(this.terms, term => {
3644
+ return term instanceof Num && Boolean(term.hints.subtract);
3579
3645
  });
3580
- },
3646
+ }
3647
+
3581
3648
  // factor a single -1 in to the Mul
3582
3649
  // combine with a Num if all Nums are positive, else add as a term
3583
- factorIn: function (hint) {
3650
+ factorIn(hint) {
3584
3651
  var partitioned = this.partition();
3652
+ // `partition` splits the terms into two Seqs - one containing
3653
+ // only Nums and the all non-Num nodes.
3585
3654
  var numbers = partitioned[0].terms;
3586
- var fold = numbers.length && _.all(numbers, function (num) {
3587
- return num.n > 0;
3588
- });
3655
+ var fold = numbers.length && _.all(numbers, num => num.n > 0);
3589
3656
  if (fold) {
3590
3657
  // e.g. - x*2*3 -> x*-2*3
3591
3658
  var num = numbers[0].negate();
@@ -3596,12 +3663,13 @@ _.extend(Mul.prototype, {
3596
3663
  // e.g. - x*-2 -> -1*x*-2
3597
3664
  return new Mul([Num.negativeOne(hint)].concat(this.terms));
3598
3665
  }
3599
- },
3666
+ }
3667
+
3600
3668
  // factor out a single hinted -1 (assume it is the division hint)
3601
3669
  // TODO(alex): make more general or rename to be more specific
3602
- factorOut: function () {
3670
+ factorOut() {
3603
3671
  var factored = false;
3604
- var terms = _.compact(_.map(this.terms, function (term, i, list) {
3672
+ var terms = _.compact(_.map(this.terms, term => {
3605
3673
  if (!factored && term instanceof Num && term.hints.divide) {
3606
3674
  factored = true;
3607
3675
  return term.n !== -1 ? term.negate() : null;
@@ -3614,64 +3682,50 @@ _.extend(Mul.prototype, {
3614
3682
  } else {
3615
3683
  return new Mul(terms);
3616
3684
  }
3617
- },
3618
- reduce: function (options) {
3619
- return _.reduce(this.terms, function (memo, term) {
3685
+ }
3686
+ reduce(options) {
3687
+ return _.reduce(this.terms, (memo, term) => {
3620
3688
  return memo.mul(term, options);
3621
3689
  }, this.identity);
3622
- },
3623
- findGCD: function (factor) {
3690
+ }
3691
+ findGCD(factor) {
3624
3692
  return new Mul(_.invoke(this.terms, "findGCD", factor)).flatten();
3625
- },
3626
- asMul: function () {
3693
+ }
3694
+ asMul() {
3627
3695
  return this;
3628
- },
3629
- asPositiveFactor: function () {
3696
+ }
3697
+ asPositiveFactor() {
3630
3698
  if (this.isPositive()) {
3631
3699
  return this;
3632
3700
  } else {
3633
- var terms = _.invoke(this.collect().terms, "asPositiveFactor");
3701
+ const terms = getFactors(this.collect()).map(factor => factor.asPositiveFactor());
3634
3702
  return new Mul(terms).flatten();
3635
3703
  }
3636
- },
3637
- isNegative: function () {
3638
- return _.any(_.invoke(this.collect().terms, "isNegative"));
3639
- },
3640
- fold: function () {
3704
+ }
3705
+ isNegative() {
3706
+ const terms = getFactors(this.collect()).map(factor => factor.isNegative());
3707
+ return _.any(terms);
3708
+ }
3709
+ fold() {
3641
3710
  return Mul.fold(this);
3642
- },
3643
- negate: function () {
3644
- var isNum = function isNum(expr) {
3711
+ }
3712
+ negate() {
3713
+ var isNum = expr => {
3645
3714
  return expr instanceof Num;
3646
3715
  };
3647
- if (_.any(this.terms, isNum)) {
3648
- var num = _.find(this.terms, isNum);
3716
+ const num = _.find(this.terms, isNum);
3717
+ if (num) {
3649
3718
  return this.replace(num, num.negate());
3650
3719
  } else {
3651
- return new Mul([Num.Neg].concat(this.terms));
3720
+ return new Mul([NumNeg].concat(this.terms));
3652
3721
  }
3653
3722
  }
3654
- });
3655
3723
 
3656
- // static methods for the sequence types
3657
- _.each([Add, Mul], function (type) {
3658
- _.extend(type, {
3659
- // create a new sequence unless left is already one (returns a copy)
3660
- createOrAppend: function (left, right) {
3661
- if (left instanceof type) {
3662
- return new type(left.terms.concat(right));
3663
- } else {
3664
- return new type(left, right);
3665
- }
3666
- }
3667
- });
3668
- });
3669
- _.extend(Mul, {
3670
3724
  // negative signs should be folded into numbers whenever possible
3671
3725
  // never fold into a Num that's already negative or a Mul that has a negative Num
3672
3726
  // an optional hint is kept track of to properly render user input
3673
3727
  // an empty hint means negation
3674
- handleNegative: function (expr, hint) {
3728
+ static handleNegative(expr, hint) {
3675
3729
  if (expr instanceof Num && expr.n > 0) {
3676
3730
  // e.g. - 2 -> -2
3677
3731
  var negated = expr.negate();
@@ -3687,19 +3741,20 @@ _.extend(Mul, {
3687
3741
  // e.g. - x -> -1*x
3688
3742
  return new Mul(Num.negativeOne(hint), expr);
3689
3743
  }
3690
- },
3744
+ }
3745
+
3691
3746
  // division can create either a Rational or a Mul
3692
- handleDivide: function (left, right) {
3747
+ static handleDivide(left, right) {
3693
3748
  // dividing by a Mul is the same as repeated division by its terms
3694
3749
  if (right instanceof Mul) {
3695
3750
  var first = Mul.handleDivide(left, right.terms[0]);
3696
3751
  var rest = new Mul(_.rest(right.terms)).flatten();
3697
3752
  return Mul.handleDivide(first, rest);
3698
3753
  }
3699
- var isInt = function isInt(expr) {
3754
+ var isInt = expr => {
3700
3755
  return expr instanceof Int;
3701
3756
  };
3702
- var isRational = function isRational(expr) {
3757
+ var isRational = expr => {
3703
3758
  return expr instanceof Rational;
3704
3759
  };
3705
3760
 
@@ -3708,7 +3763,7 @@ _.extend(Mul, {
3708
3763
  if (isInt(right) && left instanceof Mul && _.any(left.terms, isInt)) {
3709
3764
  // search from the right
3710
3765
  var reversed = left.terms.slice().reverse();
3711
- var num = _.find(reversed, isRational);
3766
+ var num = reversed.find(isRational);
3712
3767
  if (!isInt(num)) {
3713
3768
  return new Mul(left.terms.concat([new Rational(1, right.n).addHint("fraction")]));
3714
3769
  }
@@ -3722,17 +3777,17 @@ _.extend(Mul, {
3722
3777
  }
3723
3778
  if (num.n < 0 && right.n < 0) {
3724
3779
  rational.d = -rational.d;
3725
- return left.replace(num, [Num.Neg, rational]);
3780
+ return left.replace(num, [NumNeg, rational]);
3726
3781
  } else {
3727
3782
  return left.replace(num, rational);
3728
3783
  }
3729
3784
  }
3730
- var divide = function divide(a, b) {
3785
+ var divide = (a, b) => {
3731
3786
  if (b instanceof Int) {
3732
3787
  if (a instanceof Int) {
3733
3788
  if (a.n < 0 && b.n < 0) {
3734
3789
  // e.g. -2 / -3 -> -1*-2/3
3735
- return [Num.Neg, new Rational(a.n, -b.n).addHint("fraction")];
3790
+ return [NumNeg, new Rational(a.n, -b.n).addHint("fraction")];
3736
3791
  } else {
3737
3792
  // e.g. 2 / 3 -> 2/3
3738
3793
  // e.g. -2 / 3 -> -2/3
@@ -3764,7 +3819,7 @@ _.extend(Mul, {
3764
3819
  pow = new Pow(b.base, Mul.handleNegative(b.exp, "divide"));
3765
3820
  } else {
3766
3821
  // e.g. x ^ -1 -> x^-1
3767
- pow = new Pow(b, Num.Div);
3822
+ pow = new Pow(b, NumDiv);
3768
3823
  }
3769
3824
  if (a instanceof Int && a.n === 1) {
3770
3825
  // e.g. 1 / x -> x^-1
@@ -3776,13 +3831,16 @@ _.extend(Mul, {
3776
3831
  }
3777
3832
  };
3778
3833
  if (left instanceof Mul) {
3834
+ // NOTE(kevinb): `terms` should always have at least two
3835
+ // elements so getting the last element is safe to do.
3779
3836
  var divided = divide(_.last(left.terms), right);
3780
3837
  return new Mul(_.initial(left.terms).concat(divided));
3781
3838
  } else {
3782
3839
  var divided = divide(left, right);
3783
3840
  return new Mul(divided).flatten();
3784
3841
  }
3785
- },
3842
+ }
3843
+
3786
3844
  // fold negative signs into numbers if possible
3787
3845
  // negative signs are not the same as multiplying by negative one!
3788
3846
  // e.g. -x -> -1*x simplified
@@ -3795,24 +3853,20 @@ _.extend(Mul, {
3795
3853
  // e.g. sin(x)*x -> sin(x)*x
3796
3854
  // e.g. sin(x)*(x) -> sin(x)*x
3797
3855
  // e.g. sin(x)*sin(y) -> sin(x)*sin(y)
3798
- fold: function (expr) {
3856
+ static fold(expr) {
3799
3857
  if (expr instanceof Mul) {
3800
3858
  // assuming that this will be second to last
3801
- var trigLog = _.find(_.initial(expr.terms), function (term) {
3802
- return (term instanceof Trig || term instanceof Log) && term.hints.open;
3859
+ var trigLog = _.find(_.initial(expr.terms), term => {
3860
+ return (term instanceof Trig || term instanceof Log) && Boolean(term.hints.open);
3803
3861
  });
3804
- var index = _.indexOf(expr.terms, trigLog);
3805
3862
  if (trigLog) {
3806
- var last = _.last(expr.terms);
3863
+ // expr.terms should always have at least two terms
3864
+ const last = _.last(expr.terms);
3807
3865
  if (trigLog.hints.parens || last.hints.parens || last.has(Trig) || last.has(Log)) {
3808
3866
  trigLog.hints.open = false;
3809
3867
  } else {
3810
- var newTrigLog;
3811
- if (trigLog instanceof Trig) {
3812
- newTrigLog = Trig.create([trigLog.type, trigLog.exp], Mul.createOrAppend(trigLog.arg, last).fold());
3813
- } else {
3814
- newTrigLog = Log.create(trigLog.base, Mul.createOrAppend(trigLog.power, last).fold());
3815
- }
3868
+ const newTrigLog = trigLog instanceof Trig ? Trig.create([trigLog.type, trigLog.exp], Mul.createOrAppend(trigLog.arg, last).fold()) : Log.create(trigLog.base, Mul.createOrAppend(trigLog.power, last).fold());
3869
+ const index = _.indexOf(expr.terms, trigLog);
3816
3870
  if (index === 0) {
3817
3871
  return newTrigLog;
3818
3872
  } else {
@@ -3822,18 +3876,20 @@ _.extend(Mul, {
3822
3876
  }
3823
3877
  var partitioned = expr.partition();
3824
3878
  var numbers = partitioned[0].terms;
3825
- var pos = function pos(num) {
3879
+ var pos = num => {
3826
3880
  return num.n > 0;
3827
3881
  };
3828
- var neg = function neg(num) {
3829
- return num.n === -1 && num.hints.negate;
3882
+ var neg = num => {
3883
+ return num.n === -1 && Boolean(num.hints.negate);
3830
3884
  };
3831
3885
  var posOrNeg = function posOrNeg(num) {
3832
3886
  return pos(num) || neg(num);
3833
3887
  };
3834
- if (numbers.length > 1 && _.some(numbers, neg) && _.some(numbers, pos) && _.every(numbers, posOrNeg)) {
3835
- var firstNeg = _.indexOf(expr.terms, _.find(expr.terms, neg));
3836
- var firstNum = _.indexOf(expr.terms, _.find(expr.terms, pos));
3888
+ const posNum = numbers.find(pos);
3889
+ const negNum = numbers.find(neg);
3890
+ if (numbers.length > 1 && negNum && posNum && _.every(numbers, posOrNeg)) {
3891
+ var firstNeg = _.indexOf(expr.terms, negNum);
3892
+ var firstNum = _.indexOf(expr.terms, posNum);
3837
3893
 
3838
3894
  // e.g. -x*2 -> x*-2
3839
3895
  if (firstNeg < firstNum) {
@@ -3845,20 +3901,31 @@ _.extend(Mul, {
3845
3901
  // in all other cases, make no change
3846
3902
  return expr;
3847
3903
  }
3848
- });
3849
3904
 
3850
- /* exponentiation */
3851
- function Pow(base, exp) {
3852
- this.base = base;
3853
- this.exp = exp;
3905
+ // create a new sequence unless left is already one (returns a copy)
3906
+ static createOrAppend(left, right) {
3907
+ if (left instanceof Mul) {
3908
+ return new Mul(left.terms.concat(right));
3909
+ } else {
3910
+ return new Mul(left, right);
3911
+ }
3912
+ }
3854
3913
  }
3855
- Pow.prototype = new Expr();
3856
- _.extend(Pow.prototype, {
3857
- func: Pow,
3858
- args: function () {
3914
+
3915
+ /* exponentiation */
3916
+ class Pow extends Expr {
3917
+ constructor(base, exp) {
3918
+ super();
3919
+ this.base = void 0;
3920
+ this.exp = void 0;
3921
+ this.func = Pow;
3922
+ this.base = base;
3923
+ this.exp = exp;
3924
+ }
3925
+ args() {
3859
3926
  return [this.base, this.exp];
3860
- },
3861
- eval: function (vars, options) {
3927
+ }
3928
+ eval(vars = {}, options) {
3862
3929
  var evaledBase = this.base.eval(vars, options);
3863
3930
  var evaledExp = this.exp.eval(vars, options);
3864
3931
 
@@ -3877,7 +3944,7 @@ _.extend(Pow.prototype, {
3877
3944
  // If Float, convert to a Rational to enable the logic below
3878
3945
  if (simplifiedExp instanceof Float) {
3879
3946
  var num = simplifiedExp.n;
3880
- var decimals = (num - num.toFixed()).toString().length - 2;
3947
+ var decimals = (num - +num.toFixed()).toString().length - 2;
3881
3948
  var denominator = Math.pow(10, decimals);
3882
3949
  var rationalExp = new Rational(num * denominator, denominator);
3883
3950
  simplifiedExp = rationalExp.simplify();
@@ -3892,32 +3959,33 @@ _.extend(Pow.prototype, {
3892
3959
  }
3893
3960
  }
3894
3961
  return Math.pow(evaledBase, evaledExp);
3895
- },
3896
- getUnits: function () {
3897
- return this.base.getUnits().map(function (unit) {
3962
+ }
3963
+ getUnits() {
3964
+ return this.base.getUnits().map(unit => {
3898
3965
  return {
3899
3966
  unit: unit.unit,
3967
+ // Exponents in units should always be integers
3900
3968
  pow: unit.pow * this.exp.n
3901
3969
  };
3902
- }.bind(this));
3903
- },
3904
- codegen: function () {
3970
+ });
3971
+ }
3972
+ codegen() {
3905
3973
  return "Math.pow(" + this.base.codegen() + ", " + this.exp.codegen() + ")";
3906
- },
3907
- print: function () {
3974
+ }
3975
+ print() {
3908
3976
  var base = this.base.print();
3909
3977
  if (this.base instanceof Seq || this.base instanceof Pow) {
3910
3978
  base = "(" + base + ")";
3911
3979
  }
3912
3980
  return base + "^(" + this.exp.print() + ")";
3913
- },
3914
- tex: function () {
3981
+ }
3982
+ tex() {
3915
3983
  if (this.isDivide()) {
3916
3984
  // e.g. x ^ -1 w/hint -> 1/x
3917
3985
  return "\\frac{1}{" + this.asDivide().tex() + "}";
3918
- } else if (this.isRoot()) {
3986
+ } else if (this.isRoot() && isRational(this.exp)) {
3919
3987
  if (this.exp.n !== 1) {
3920
- error("Node marked with hint 'root' does not have exponent " + "of form 1/x.");
3988
+ throw new Error("Node marked with hint 'root' does not have exponent " + "of form 1/x.");
3921
3989
  }
3922
3990
  if (this.exp.d === 2) {
3923
3991
  // e.g. x ^ 1/2 w/hint -> sqrt{x}
@@ -3928,9 +3996,7 @@ _.extend(Pow.prototype, {
3928
3996
  }
3929
3997
  } else if (this.base instanceof Trig && !this.base.isInverse() && this.exp instanceof Num && this.exp.isSimple() && this.exp.eval() >= 0) {
3930
3998
  // e.g sin(x) ^ 2 -> sin^2(x)
3931
- var split = this.base.tex({
3932
- split: true
3933
- });
3999
+ var split = this.base.texSplit();
3934
4000
  return split[0] + "^{" + this.exp.tex() + "}" + split[1];
3935
4001
  } else {
3936
4002
  // e.g. x ^ y -> x^y
@@ -3944,16 +4010,16 @@ _.extend(Pow.prototype, {
3944
4010
  }
3945
4011
  return base + "^{" + this.exp.tex() + "}";
3946
4012
  }
3947
- },
3948
- needsExplicitMul: function () {
4013
+ }
4014
+ needsExplicitMul() {
3949
4015
  return this.isRoot() ? false : this.base.needsExplicitMul();
3950
- },
3951
- expand: function () {
4016
+ }
4017
+ expand() {
3952
4018
  var pow = this.recurse("expand");
3953
4019
  if (pow.base instanceof Mul) {
3954
4020
  // e.g. (ab)^c -> a^c*b^c
3955
4021
 
3956
- var terms = _.map(pow.base.terms, function (term) {
4022
+ var terms = _.map(pow.base.terms, term => {
3957
4023
  return new Pow(term, pow.exp);
3958
4024
  });
3959
4025
  return new Mul(terms).expand();
@@ -3964,49 +4030,55 @@ _.extend(Pow.prototype, {
3964
4030
  var positive = pow.exp.eval() > 0;
3965
4031
  var n = pow.exp.abs().eval();
3966
4032
  var signed = function signed(mul) {
3967
- return positive ? mul : new Pow(mul, Num.Div);
4033
+ return positive ? mul : new Pow(mul, NumDiv);
3968
4034
  };
3969
4035
 
3970
4036
  // compute and cache powers of 2 up to n
3971
- var cache = {
4037
+ const cache = {
3972
4038
  1: pow.base
3973
4039
  };
3974
4040
  for (var i = 2; i <= n; i *= 2) {
3975
- var mul = new Mul(cache[i / 2], cache[i / 2]);
3976
- cache[i] = mul.expand().collect();
4041
+ const _mul = new Mul(cache[i / 2], cache[i / 2]);
4042
+ cache[i] = _mul.expand().collect();
3977
4043
  }
3978
4044
 
3979
4045
  // if n is a power of 2, you're done!
3980
- if (_.has(cache, n)) {
4046
+ if (n in cache) {
3981
4047
  return signed(cache[n]);
3982
4048
  }
3983
4049
 
3984
4050
  // otherwise decompose n into powers of 2 ...
3985
- var indices = _.map(n.toString(2).split(""), function (str, i, list) {
4051
+ let indices = _.map(n.toString(2).split(""), function (str, i, list) {
3986
4052
  return Number(str) * Math.pow(2, list.length - i - 1);
3987
4053
  });
3988
4054
  indices = _.without(indices, 0);
3989
4055
 
3990
4056
  // ... then combine
3991
- var mul = new Mul(_.pick(cache, indices)).expand().collect();
4057
+ const factors = [];
4058
+ for (const index of indices) {
4059
+ if (index in cache) {
4060
+ factors.push(cache[index]);
4061
+ }
4062
+ }
4063
+ const mul = new Mul(factors).expand().collect();
3992
4064
  return signed(mul);
3993
4065
  } else if (pow.exp instanceof Add) {
3994
4066
  // DEFINITELY want behind super-simplify() flag
3995
4067
  // e.g. x^(a+b) -> x^a*x^b
3996
4068
 
3997
- var terms = _.map(pow.exp.terms, function (term) {
4069
+ const _terms = _.map(pow.exp.terms, term => {
3998
4070
  return new Pow(pow.base, term).expand();
3999
4071
  });
4000
- return new Mul(terms).expand();
4072
+ return new Mul(_terms).expand();
4001
4073
  } else {
4002
4074
  return pow;
4003
4075
  }
4004
- },
4005
- factor: function () {
4076
+ }
4077
+ factor() {
4006
4078
  var pow = this.recurse("factor");
4007
4079
  if (pow.base instanceof Mul) {
4008
- var terms = _.map(pow.base.terms, function (term) {
4009
- if (term instanceof Int && pow.exp.equals(Num.Div)) {
4080
+ var terms = _.map(pow.base.terms, term => {
4081
+ if (term instanceof Int && pow.exp.equals(NumDiv)) {
4010
4082
  // Anything that can be a Rational should be a Rational
4011
4083
  // e.g. 2^(-1) -> 1/2
4012
4084
  return new Rational(1, term.n);
@@ -4018,23 +4090,23 @@ _.extend(Pow.prototype, {
4018
4090
  } else {
4019
4091
  return pow;
4020
4092
  }
4021
- },
4022
- collect: function (options) {
4093
+ }
4094
+ collect(options) {
4023
4095
  if (this.base instanceof Pow) {
4024
4096
  // collect this first to avoid having to deal with float precision
4025
4097
  // e.g. sqrt(2)^2 -> 2, not 2.0000000000000004
4026
4098
  // e.g. (x^y)^z -> x^(yz)
4027
- var base = this.base.base;
4028
- var exp = Mul.createOrAppend(this.base.exp, this.exp);
4099
+ const base = this.base.base;
4100
+ const exp = Mul.createOrAppend(this.base.exp, this.exp);
4029
4101
  return new Pow(base, exp).collect(options);
4030
4102
  }
4031
- var pow = this.recurse("collect", options);
4032
- var isSimilarLog = function isSimilarLog(term) {
4103
+ const pow = this.recurse("collect", options);
4104
+ const isSimilarLog = function isSimilarLog(term) {
4033
4105
  return term instanceof Log && term.base.equals(pow.base);
4034
4106
  };
4035
4107
  if (pow.exp instanceof Num && pow.exp.eval() === 0) {
4036
4108
  // e.g. x^0 -> 1
4037
- return Num.One;
4109
+ return NumOne;
4038
4110
  } else if (pow.exp instanceof Num && pow.exp.eval() === 1) {
4039
4111
  // e.g. x^1 -> x
4040
4112
  return pow.base;
@@ -4043,9 +4115,11 @@ _.extend(Pow.prototype, {
4043
4115
  return pow.exp.power;
4044
4116
  } else if (pow.exp instanceof Mul && _.any(pow.exp.terms, isSimilarLog)) {
4045
4117
  // e.g. b^(2*y*log_b(x)) -> x^(2*y)
4046
- var log = _.find(pow.exp.terms, isSimilarLog);
4047
- var base = log.power;
4048
- var exp = pow.exp.remove(log).flatten();
4118
+ // `log` will always be defined here because of the
4119
+ // `_.any(pow.exp.terms, isSimilarLog)` check above.
4120
+ const log = pow.exp.terms.find(isSimilarLog);
4121
+ const base = log.power;
4122
+ const exp = pow.exp.remove(log).flatten();
4049
4123
  return new Pow(base, exp).collect(options);
4050
4124
  } else if (pow.base instanceof Num && pow.exp instanceof Num) {
4051
4125
  // TODO(alex): Consider encapsualting this logic (and similar logic
@@ -4060,14 +4134,18 @@ _.extend(Pow.prototype, {
4060
4134
  // as floats, but ideally rationals should be pre-processed
4061
4135
  // e.g. (1/27)^(1/3) -> 1/3 to avoid most cases.
4062
4136
  // TODO(alex): Catch such cases and avoid converting to floats.
4063
- var exp = pow.exp.asRational();
4064
- var decimalsInBase = pow.base.getDecimalPlaces();
4065
- var root = new Pow(pow.base, new Rational(1, exp.d));
4066
- var decimalsInRoot = root.collect().getDecimalPlaces();
4137
+ const exp = pow.exp.asRational();
4138
+ const decimalsInBase = pow.base.getDecimalPlaces();
4139
+ const root = new Pow(pow.base, new Rational(1, exp.d));
4140
+ const decimalsInRoot = root.collect()
4141
+ // @ts-expect-error: we assume that `root.collect()` returns
4142
+ // a Num here but tbh I'm not sure how this code isn't causing
4143
+ // an infinite loop.
4144
+ .getDecimalPlaces();
4067
4145
  if (decimalsInRoot > decimalsInBase) {
4068
4146
  // Collecting over this denominator would result in an
4069
4147
  // imprecise float, so avoid doing so.
4070
- var newBase = new Pow(pow.base, new Int(exp.n)).collect();
4148
+ const newBase = new Pow(pow.base, new Int(exp.n)).collect();
4071
4149
  return new Pow(newBase, new Rational(1, exp.d));
4072
4150
  }
4073
4151
  }
@@ -4077,16 +4155,18 @@ _.extend(Pow.prototype, {
4077
4155
  } else {
4078
4156
  return pow;
4079
4157
  }
4080
- },
4158
+ }
4159
+
4081
4160
  // checks whether this Pow represents user-entered division
4082
- isDivide: function () {
4161
+ isDivide() {
4083
4162
  var isDiv = function isDiv(arg) {
4084
- return arg instanceof Num && arg.hints.divide;
4163
+ return arg instanceof Num && Boolean(arg.hints.divide);
4085
4164
  };
4086
4165
  return isDiv(this.exp) || this.exp instanceof Mul && _.any(this.exp.terms, isDiv);
4087
- },
4166
+ }
4167
+
4088
4168
  // assuming this Pow represents user-entered division, returns the denominator
4089
- asDivide: function () {
4169
+ asDivide() {
4090
4170
  if (this.exp instanceof Num) {
4091
4171
  if (this.exp.eval() === -1) {
4092
4172
  return this.base;
@@ -4099,18 +4179,19 @@ _.extend(Pow.prototype, {
4099
4179
  } else if (this.exp instanceof Mul) {
4100
4180
  return new Pow(this.base, this.exp.factorOut());
4101
4181
  } else {
4102
- error("called asDivide() on an Expr that wasn't a Num or Mul");
4182
+ throw new Error("called asDivide() on an Expr that wasn't a Num or Mul");
4103
4183
  }
4104
- },
4105
- isRoot: function () {
4106
- return this.exp instanceof Rational && this.exp.hints.root;
4107
- },
4108
- isSquaredTrig: function () {
4184
+ }
4185
+ isRoot() {
4186
+ return this.exp instanceof Rational && Boolean(this.exp.hints.root);
4187
+ }
4188
+ isSquaredTrig() {
4109
4189
  return this.base instanceof Trig && !this.base.isInverse() && this.exp instanceof Num && this.exp.eval() === 2;
4110
- },
4190
+ }
4191
+
4111
4192
  // extract whatever denominator makes sense, ignoring hints
4112
4193
  // if negative exponent, will recursively include the base's denominator as well
4113
- getDenominator: function () {
4194
+ getDenominator() {
4114
4195
  if (this.exp instanceof Num && this.exp.eval() === -1) {
4115
4196
  return Mul.createOrAppend(this.base, this.base.getDenominator()).flatten();
4116
4197
  } else if (this.exp.isNegative()) {
@@ -4119,18 +4200,11 @@ _.extend(Pow.prototype, {
4119
4200
  } else if (this.base instanceof Num) {
4120
4201
  return new Pow(this.base.getDenominator(), this.exp).collect();
4121
4202
  } else {
4122
- return Num.One;
4123
- }
4124
- },
4125
- findGCD: function (factor) {
4126
- var base, exp;
4127
- if (factor instanceof Pow) {
4128
- base = factor.base;
4129
- exp = factor.exp;
4130
- } else {
4131
- base = factor;
4132
- exp = Num.One;
4203
+ return NumOne;
4133
4204
  }
4205
+ }
4206
+ findGCD(factor) {
4207
+ const [base, exp] = factor instanceof Pow ? [factor.base, factor.exp] : [factor, NumOne];
4134
4208
 
4135
4209
  // GCD is only relevant if same base
4136
4210
  if (this.base.equals(base)) {
@@ -4145,7 +4219,7 @@ _.extend(Pow.prototype, {
4145
4219
  } else if (this.exp instanceof Num || exp instanceof Num) {
4146
4220
  // one numerical exponent
4147
4221
  // e.g. GCD(x^2, x^y) -> 1
4148
- return Num.One;
4222
+ return NumOne;
4149
4223
  }
4150
4224
  var expA = this.exp.asMul().partition();
4151
4225
  var expB = exp.asMul().partition();
@@ -4157,16 +4231,16 @@ _.extend(Pow.prototype, {
4157
4231
  return new Pow(base, mul).collect();
4158
4232
  }
4159
4233
  }
4160
- return Num.One;
4161
- },
4162
- isPositive: function () {
4234
+ return NumOne;
4235
+ }
4236
+ isPositive() {
4163
4237
  if (this.base.isPositive()) {
4164
4238
  return true;
4165
4239
  }
4166
4240
  var exp = this.exp.simplify();
4167
4241
  return exp instanceof Int && exp.eval() % 2 === 0;
4168
- },
4169
- asPositiveFactor: function () {
4242
+ }
4243
+ asPositiveFactor() {
4170
4244
  if (this.isPositive()) {
4171
4245
  return this;
4172
4246
  } else {
@@ -4181,78 +4255,87 @@ _.extend(Pow.prototype, {
4181
4255
  return new Pow(this.base, new Int(n + 1));
4182
4256
  }
4183
4257
  }
4184
- return Num.One;
4258
+ return NumOne;
4185
4259
  }
4186
4260
  }
4187
- });
4188
- _.extend(Pow, {
4189
- sqrt: function (arg) {
4190
- return new Pow(arg, Num.Sqrt);
4191
- },
4192
- nthroot: function (radicand, degree) {
4193
- var exp = Mul.fold(Mul.handleDivide(new Int(1), degree));
4194
-
4195
- // FIXME(johnsullivan): If oneOverDegree ends up being a pow object,
4196
- // this "root" hint is lost between here and when tex() is called.
4197
- return new Pow(radicand, exp.addHint("root"));
4261
+ static sqrt(arg) {
4262
+ return new Pow(arg, NumSqrt);
4198
4263
  }
4199
- });
4200
4264
 
4201
- /* logarithm */
4202
- function Log(base, power) {
4203
- this.base = base;
4204
- this.power = power;
4265
+ // NOTE(kevinb): nthroot is used as a constructor so we need to
4266
+ // define it as a static property instead of a static method.
4267
+ // TODO(kevinb): update parser-generator.ts to call nthrooth
4268
+ // without using `new`.
4205
4269
  }
4206
- Log.prototype = new Expr();
4207
- _.extend(Log.prototype, {
4208
- func: Log,
4209
- args: function () {
4270
+
4271
+ /* logarithm */
4272
+ _class5 = Pow;
4273
+ Pow.nthroot = function (radicand, degree) {
4274
+ var exp = Mul.fold(Mul.handleDivide(new Int(1), degree));
4275
+
4276
+ // FIXME(johnsullivan): If oneOverDegree ends up being a pow object,
4277
+ // this "root" hint is lost between here and when tex() is called.
4278
+ return new _class5(radicand, exp.addHint("root"));
4279
+ };
4280
+ class Log extends Expr {
4281
+ constructor(base, power) {
4282
+ super();
4283
+ this.base = void 0;
4284
+ this.power = void 0;
4285
+ this.func = Log;
4286
+ this.base = base;
4287
+ this.power = power;
4288
+ this.hints = _extends({}, this.hints, {
4289
+ open: false
4290
+ });
4291
+ }
4292
+ args() {
4210
4293
  return [this.base, this.power];
4211
- },
4212
- eval: function (vars, options) {
4294
+ }
4295
+ eval(vars = {}, options) {
4213
4296
  return Math.log(this.power.eval(vars, options)) / Math.log(this.base.eval(vars, options));
4214
- },
4215
- codegen: function () {
4297
+ }
4298
+ codegen() {
4216
4299
  return "(Math.log(" + this.power.codegen() + ") / Math.log(" + this.base.codegen() + "))";
4217
- },
4218
- print: function () {
4300
+ }
4301
+ print() {
4219
4302
  var power = "(" + this.power.print() + ")";
4220
4303
  if (this.isNatural()) {
4221
4304
  return "ln" + power;
4222
4305
  } else {
4223
4306
  return "log_(" + this.base.print() + ") " + power;
4224
4307
  }
4225
- },
4226
- tex: function () {
4308
+ }
4309
+ tex() {
4227
4310
  var power = "(" + this.power.tex() + ")";
4228
4311
  if (this.isNatural()) {
4229
4312
  return "\\ln" + power;
4230
4313
  } else {
4231
4314
  return "\\log_{" + this.base.tex() + "}" + power;
4232
4315
  }
4233
- },
4234
- collect: function (options) {
4316
+ }
4317
+ collect(options) {
4235
4318
  var log = this.recurse("collect", options);
4236
4319
  if (log.power instanceof Num && log.power.eval() === 1) {
4237
4320
  // e.g. ln(1) -> 0
4238
- return Num.Zero;
4321
+ return NumZero;
4239
4322
  } else if (log.base.equals(log.power)) {
4240
4323
  // e.g. log_b(b) -> 1
4241
- return Num.One;
4324
+ return NumOne;
4242
4325
  } else if (log.power instanceof Pow && log.power.base.equals(log.base)) {
4243
4326
  // e.g. log_b(b^x) -> x
4244
4327
  return log.power.exp;
4245
4328
  } else {
4246
4329
  return log;
4247
4330
  }
4248
- },
4249
- expand: function () {
4331
+ }
4332
+ expand() {
4250
4333
  var log = this.recurse("expand");
4251
4334
  if (log.power instanceof Mul) {
4252
4335
  // might want behind super-simplify() flag
4253
4336
  // e.g. ln(xy) -> ln(x) + ln(y)
4254
4337
 
4255
- var terms = _.map(log.power.terms, function (term) {
4338
+ var terms = _.map(log.power.terms, term => {
4256
4339
  // need to expand again in case new log powers are Pows
4257
4340
  return new Log(log.base, term).expand();
4258
4341
  });
@@ -4268,231 +4351,209 @@ _.extend(Log.prototype, {
4268
4351
  } else {
4269
4352
  return log;
4270
4353
  }
4271
- },
4272
- hints: _.extend(Log.prototype.hints, {
4273
- open: false
4274
- }),
4275
- isPositive: function () {
4354
+ }
4355
+ isPositive() {
4276
4356
  var log = this.collect();
4277
- if (log.base instanceof Num && log.power instanceof Num) {
4357
+ if (log instanceof Log && log.base instanceof Num && log.power instanceof Num) {
4278
4358
  return this.eval() > 0;
4279
4359
  } else {
4280
4360
  return false;
4281
4361
  }
4282
- },
4283
- needsExplicitMul: function () {
4362
+ }
4363
+ needsExplicitMul() {
4284
4364
  return false;
4285
- },
4286
- isNatural: function () {
4365
+ }
4366
+ isNatural() {
4287
4367
  return this.base.equals(Const.e);
4288
4368
  }
4289
- });
4290
- _.extend(Log, {
4291
- natural: function () {
4369
+ static natural() {
4292
4370
  return Const.e;
4293
- },
4294
- common: function () {
4295
- return Num.Ten;
4296
- },
4297
- create: function (base, power) {
4371
+ }
4372
+ static common() {
4373
+ return NumTen;
4374
+ }
4375
+ static create(base, power) {
4298
4376
  var log = new Log(base, power);
4299
4377
  if (!power.hints.parens) {
4300
4378
  log = log.addHint("open");
4301
4379
  }
4302
4380
  return log;
4303
4381
  }
4304
- });
4305
-
4306
- /* trigonometric functions */
4307
- function Trig(type, arg) {
4308
- this.type = type;
4309
- this.arg = arg;
4310
4382
  }
4311
- Trig.prototype = new Expr();
4312
- _.extend(Trig.prototype, {
4313
- func: Trig,
4314
- args: function () {
4315
- return [this.type, this.arg];
4316
- },
4317
- functions: {
4318
- sin: {
4319
- eval: Math.sin,
4320
- codegen: "Math.sin((",
4321
- tex: "\\sin",
4322
- expand: function () {
4323
- return this;
4324
- }
4325
- },
4326
- cos: {
4327
- eval: Math.cos,
4328
- codegen: "Math.cos((",
4329
- tex: "\\cos",
4330
- expand: function () {
4331
- return this;
4332
- }
4333
- },
4334
- tan: {
4335
- eval: Math.tan,
4336
- codegen: "Math.tan((",
4337
- tex: "\\tan",
4338
- expand: function () {
4339
- return Mul.handleDivide(Trig.sin(this.arg), Trig.cos(this.arg));
4340
- }
4341
- },
4342
- csc: {
4343
- eval: function (arg) {
4344
- return 1 / Math.sin(arg);
4383
+ /* trigonometric functions */
4384
+ class Trig extends Expr {
4385
+ constructor(type, _arg) {
4386
+ super();
4387
+ this.type = void 0;
4388
+ // TODO(kevinb): Use a union type for this
4389
+ this.arg = void 0;
4390
+ this.exp = void 0;
4391
+ this.func = Trig;
4392
+ // TODO(kevinb): Use union type for the function names.
4393
+ this.functions = {
4394
+ sin: {
4395
+ eval: Math.sin,
4396
+ codegen: "Math.sin((",
4397
+ tex: "\\sin",
4398
+ expand: () => this
4345
4399
  },
4346
- codegen: "(1/Math.sin(",
4347
- tex: "\\csc",
4348
- expand: function () {
4349
- return Mul.handleDivide(Num.One, Trig.sin(this.arg));
4350
- }
4351
- },
4352
- sec: {
4353
- eval: function (arg) {
4354
- return 1 / Math.cos(arg);
4400
+ cos: {
4401
+ eval: Math.cos,
4402
+ codegen: "Math.cos((",
4403
+ tex: "\\cos",
4404
+ expand: () => this
4355
4405
  },
4356
- codegen: "(1/Math.cos(",
4357
- tex: "\\sec",
4358
- expand: function () {
4359
- return Mul.handleDivide(Num.One, Trig.cos(this.arg));
4360
- }
4361
- },
4362
- cot: {
4363
- eval: function (arg) {
4364
- return 1 / Math.tan(arg);
4365
- },
4366
- codegen: "(1/Math.tan(",
4367
- tex: "\\cot",
4368
- expand: function () {
4369
- return Mul.handleDivide(Trig.cos(this.arg), Trig.sin(this.arg));
4370
- }
4371
- },
4372
- arcsin: {
4373
- eval: Math.asin,
4374
- codegen: "Math.asin((",
4375
- tex: "\\arcsin"
4376
- },
4377
- arccos: {
4378
- eval: Math.acos,
4379
- codegen: "Math.acos((",
4380
- tex: "\\arccos"
4381
- },
4382
- arctan: {
4383
- eval: Math.atan,
4384
- codegen: "Math.atan((",
4385
- tex: "\\arctan"
4386
- },
4387
- arccsc: {
4388
- eval: function (arg) {
4389
- return Math.asin(1 / arg);
4406
+ tan: {
4407
+ eval: Math.tan,
4408
+ codegen: "Math.tan((",
4409
+ tex: "\\tan",
4410
+ expand: () => Mul.handleDivide(Trig.sin(this.arg), Trig.cos(this.arg))
4390
4411
  },
4391
- codegen: "Math.asin(1/(",
4392
- tex: "\\operatorname{arccsc}"
4393
- },
4394
- arcsec: {
4395
- eval: function (arg) {
4396
- return Math.acos(1 / arg);
4412
+ csc: {
4413
+ eval: arg => {
4414
+ return 1 / Math.sin(arg);
4415
+ },
4416
+ codegen: "(1/Math.sin(",
4417
+ tex: "\\csc",
4418
+ expand: () => Mul.handleDivide(NumOne, Trig.sin(this.arg))
4397
4419
  },
4398
- codegen: "Math.acos(1/(",
4399
- tex: "\\operatorname{arcsec}"
4400
- },
4401
- arccot: {
4402
- eval: function (arg) {
4403
- return Math.atan(1 / arg);
4420
+ sec: {
4421
+ eval: arg => {
4422
+ return 1 / Math.cos(arg);
4423
+ },
4424
+ codegen: "(1/Math.cos(",
4425
+ tex: "\\sec",
4426
+ expand: () => Mul.handleDivide(NumOne, Trig.cos(this.arg))
4404
4427
  },
4405
- codegen: "Math.atan(1/(",
4406
- tex: "\\operatorname{arccot}"
4407
- },
4408
- sinh: {
4409
- eval: function (arg) {
4410
- return (Math.exp(arg) - Math.exp(-arg)) / 2;
4428
+ cot: {
4429
+ eval: arg => {
4430
+ return 1 / Math.tan(arg);
4431
+ },
4432
+ codegen: "(1/Math.tan(",
4433
+ tex: "\\cot",
4434
+ expand: () => Mul.handleDivide(Trig.cos(this.arg), Trig.sin(this.arg))
4411
4435
  },
4412
- codegen: function (argStr) {
4413
- return "((Math.exp(" + argStr + ") - Math.exp(-(" + argStr + "))) / 2)";
4436
+ arcsin: {
4437
+ eval: Math.asin,
4438
+ codegen: "Math.asin((",
4439
+ tex: "\\arcsin"
4414
4440
  },
4415
- tex: "\\sinh",
4416
- expand: function () {
4417
- return this;
4418
- }
4419
- },
4420
- cosh: {
4421
- eval: function (arg) {
4422
- return (Math.exp(arg) + Math.exp(-arg)) / 2;
4441
+ arccos: {
4442
+ eval: Math.acos,
4443
+ codegen: "Math.acos((",
4444
+ tex: "\\arccos"
4423
4445
  },
4424
- codegen: function (argStr) {
4425
- return "((Math.exp(" + argStr + ") + Math.exp(-(" + argStr + "))) / 2)";
4446
+ arctan: {
4447
+ eval: Math.atan,
4448
+ codegen: "Math.atan((",
4449
+ tex: "\\arctan"
4426
4450
  },
4427
- tex: "\\cosh",
4428
- expand: function () {
4429
- return this;
4430
- }
4431
- },
4432
- tanh: {
4433
- eval: function (arg) {
4434
- return (Math.exp(arg) - Math.exp(-arg)) / (Math.exp(arg) + Math.exp(-arg));
4451
+ arccsc: {
4452
+ eval: arg => {
4453
+ return Math.asin(1 / arg);
4454
+ },
4455
+ codegen: "Math.asin(1/(",
4456
+ tex: "\\operatorname{arccsc}"
4435
4457
  },
4436
- codegen: function (argStr) {
4437
- return "(" + "(Math.exp(" + argStr + ") - Math.exp(-(" + argStr + ")))" + " / " + "(Math.exp(" + argStr + ") + Math.exp(-(" + argStr + ")))" + ")";
4458
+ arcsec: {
4459
+ eval: arg => {
4460
+ return Math.acos(1 / arg);
4461
+ },
4462
+ codegen: "Math.acos(1/(",
4463
+ tex: "\\operatorname{arcsec}"
4438
4464
  },
4439
- tex: "\\tanh",
4440
- expand: function () {
4441
- return Mul.handleDivide(Trig.sinh(this.arg), Trig.cosh(this.arg));
4442
- }
4443
- },
4444
- csch: {
4445
- eval: function (arg) {
4446
- return 2 / (Math.exp(arg) - Math.exp(-arg));
4465
+ arccot: {
4466
+ eval: arg => {
4467
+ return Math.atan(1 / arg);
4468
+ },
4469
+ codegen: "Math.atan(1/(",
4470
+ tex: "\\operatorname{arccot}"
4447
4471
  },
4448
- codegen: function (argStr) {
4449
- return "(2 / (Math.exp(" + argStr + ") - Math.exp(-(" + argStr + "))))";
4472
+ sinh: {
4473
+ eval: arg => {
4474
+ return (Math.exp(arg) - Math.exp(-arg)) / 2;
4475
+ },
4476
+ codegen: argStr => {
4477
+ return "((Math.exp(" + argStr + ") - Math.exp(-(" + argStr + "))) / 2)";
4478
+ },
4479
+ tex: "\\sinh",
4480
+ expand: () => this
4450
4481
  },
4451
- tex: "\\csch",
4452
- expand: function () {
4453
- return Mul.handleDivide(Num.One, Trig.sinh(this.arg));
4454
- }
4455
- },
4456
- sech: {
4457
- eval: function (arg) {
4458
- return 2 / (Math.exp(arg) + Math.exp(-arg));
4482
+ cosh: {
4483
+ eval: arg => {
4484
+ return (Math.exp(arg) + Math.exp(-arg)) / 2;
4485
+ },
4486
+ codegen: argStr => {
4487
+ return "((Math.exp(" + argStr + ") + Math.exp(-(" + argStr + "))) / 2)";
4488
+ },
4489
+ tex: "\\cosh",
4490
+ expand: () => this
4459
4491
  },
4460
- codegen: function (argStr) {
4461
- return "(2 / (Math.exp(" + argStr + ") + Math.exp(-(" + argStr + "))))";
4492
+ tanh: {
4493
+ eval: arg => {
4494
+ return (Math.exp(arg) - Math.exp(-arg)) / (Math.exp(arg) + Math.exp(-arg));
4495
+ },
4496
+ codegen: argStr => {
4497
+ return "(" + "(Math.exp(" + argStr + ") - Math.exp(-(" + argStr + ")))" + " / " + "(Math.exp(" + argStr + ") + Math.exp(-(" + argStr + ")))" + ")";
4498
+ },
4499
+ tex: "\\tanh",
4500
+ expand: () => Mul.handleDivide(Trig.sinh(this.arg), Trig.cosh(this.arg))
4462
4501
  },
4463
- tex: "\\sech",
4464
- expand: function () {
4465
- return Mul.handleDivide(Num.One, Trig.cosh(this.arg));
4466
- }
4467
- },
4468
- coth: {
4469
- eval: function (arg) {
4470
- return (Math.exp(arg) + Math.exp(-arg)) / (Math.exp(arg) - Math.exp(-arg));
4502
+ csch: {
4503
+ eval: arg => {
4504
+ return 2 / (Math.exp(arg) - Math.exp(-arg));
4505
+ },
4506
+ codegen: argStr => {
4507
+ return "(2 / (Math.exp(" + argStr + ") - Math.exp(-(" + argStr + "))))";
4508
+ },
4509
+ tex: "\\csch",
4510
+ expand: () => Mul.handleDivide(NumOne, Trig.sinh(this.arg))
4471
4511
  },
4472
- codegen: function (argStr) {
4473
- return "(" + "(Math.exp(" + argStr + ") + Math.exp(-(" + argStr + ")))" + " / " + "(Math.exp(" + argStr + ") - Math.exp(-(" + argStr + ")))" + ")";
4512
+ sech: {
4513
+ eval: arg => {
4514
+ return 2 / (Math.exp(arg) + Math.exp(-arg));
4515
+ },
4516
+ codegen: argStr => {
4517
+ return "(2 / (Math.exp(" + argStr + ") + Math.exp(-(" + argStr + "))))";
4518
+ },
4519
+ tex: "\\sech",
4520
+ expand: () => Mul.handleDivide(NumOne, Trig.cosh(this.arg))
4474
4521
  },
4475
- tex: "\\coth",
4476
- expand: function () {
4477
- return Mul.handleDivide(Trig.cosh(this.arg), Trig.sinh(this.arg));
4522
+ coth: {
4523
+ eval: arg => {
4524
+ return (Math.exp(arg) + Math.exp(-arg)) / (Math.exp(arg) - Math.exp(-arg));
4525
+ },
4526
+ codegen: argStr => {
4527
+ return "(" + "(Math.exp(" + argStr + ") + Math.exp(-(" + argStr + ")))" + " / " + "(Math.exp(" + argStr + ") - Math.exp(-(" + argStr + ")))" + ")";
4528
+ },
4529
+ tex: "\\coth",
4530
+ expand: () => Mul.handleDivide(Trig.cosh(this.arg), Trig.sinh(this.arg))
4478
4531
  }
4479
- }
4480
- },
4481
- isEven: function () {
4532
+ };
4533
+ this.type = type;
4534
+ this.arg = _arg;
4535
+ this.hints = _extends({}, this.hints, {
4536
+ open: false
4537
+ });
4538
+ }
4539
+ args() {
4540
+ return [this.type, this.arg];
4541
+ }
4542
+ isEven() {
4482
4543
  return _.contains(["cos", "sec"], this.type);
4483
- },
4484
- isInverse: function () {
4544
+ }
4545
+ isInverse() {
4485
4546
  return this.type.indexOf("arc") === 0;
4486
- },
4487
- isBasic: function () {
4547
+ }
4548
+ isBasic() {
4488
4549
  return _.contains(["sin", "cos"], this.type);
4489
- },
4490
- eval: function (vars, options) {
4550
+ }
4551
+ eval(vars = {}, options) {
4491
4552
  var func = this.functions[this.type].eval;
4492
4553
  var arg = this.arg.eval(vars, options);
4493
4554
  return func(arg);
4494
- },
4495
- codegen: function () {
4555
+ }
4556
+ codegen() {
4496
4557
  var func = this.functions[this.type].codegen;
4497
4558
  if (typeof func === "function") {
4498
4559
  return func(this.arg.codegen());
@@ -4501,27 +4562,29 @@ _.extend(Trig.prototype, {
4501
4562
  } else {
4502
4563
  throw new Error("codegen not implemented for " + this.type);
4503
4564
  }
4504
- },
4505
- print: function () {
4565
+ }
4566
+ print() {
4506
4567
  return this.type + "(" + this.arg.print() + ")";
4507
- },
4508
- tex: function (options) {
4568
+ }
4569
+ tex() {
4570
+ var func = this.functions[this.type].tex;
4571
+ var arg = "(" + this.arg.tex() + ")";
4572
+ return func + arg;
4573
+ }
4574
+ texSplit() {
4509
4575
  var func = this.functions[this.type].tex;
4510
4576
  var arg = "(" + this.arg.tex() + ")";
4511
- return options && options.split ? [func, arg] : func + arg;
4512
- },
4513
- hints: _.extend(Trig.prototype.hints, {
4514
- open: false
4515
- }),
4516
- isPositive: function () {
4577
+ return [func, arg];
4578
+ }
4579
+ isPositive() {
4517
4580
  var trig = this.collect();
4518
- if (trig.arg instanceof Num) {
4581
+ if (trig instanceof Trig && trig.arg instanceof Num) {
4519
4582
  return this.eval() > 0;
4520
4583
  } else {
4521
4584
  return false;
4522
4585
  }
4523
- },
4524
- completeParse: function () {
4586
+ }
4587
+ completeParse() {
4525
4588
  if (this.exp) {
4526
4589
  var pow = new Pow(this, this.exp);
4527
4590
  this.exp = undefined;
@@ -4529,47 +4592,42 @@ _.extend(Trig.prototype, {
4529
4592
  } else {
4530
4593
  return this;
4531
4594
  }
4532
- },
4595
+ }
4596
+
4533
4597
  // TODO(alex): does every new node type need to redefine these?
4534
- needsExplicitMul: function () {
4598
+ needsExplicitMul() {
4535
4599
  return false;
4536
- },
4537
- expand: function () {
4600
+ }
4601
+ expand() {
4538
4602
  var trig = this.recurse("expand");
4539
4603
  if (!trig.isInverse()) {
4540
4604
  // e.g. tan(x) -> sin(x)/cos(x)
4605
+ // NOTE(kevinb): All non-inverse trig functions have an expand property.
4541
4606
  var expand = trig.functions[trig.type].expand;
4542
4607
  return _.bind(expand, trig)();
4543
4608
  } else {
4544
4609
  return trig;
4545
4610
  }
4546
- },
4547
- collect: function (options) {
4611
+ }
4612
+ collect(options) {
4548
4613
  var trig = this.recurse("collect", options);
4549
4614
  if (!trig.isInverse() && trig.arg.isNegative()) {
4550
- var arg;
4551
- if (trig.arg instanceof Num) {
4552
- arg = trig.arg.abs();
4553
- } else {
4554
- arg = Mul.handleDivide(trig.arg, Num.Neg).collect(options);
4555
- }
4615
+ const arg = trig.arg instanceof Num ? trig.arg.abs() : Mul.handleDivide(trig.arg, NumNeg).collect(options);
4556
4616
  if (trig.isEven()) {
4557
4617
  // e.g. cos(-x) -> cos(x)
4558
4618
  return new Trig(trig.type, arg);
4559
4619
  } else {
4560
4620
  // e.g. sin(-x) -> -sin(x)
4561
- return new Mul(Num.Neg, new Trig(trig.type, arg));
4621
+ return new Mul(NumNeg, new Trig(trig.type, arg));
4562
4622
  }
4563
4623
  } else {
4564
4624
  return trig;
4565
4625
  }
4566
4626
  }
4567
- });
4568
- _.extend(Trig, {
4569
- create: function (pair, arg) {
4627
+ static create(pair, arg) {
4570
4628
  var type = pair[0];
4571
4629
  var exp = pair[1];
4572
- if (exp && exp.equals(Num.Neg)) {
4630
+ if (exp && exp.equals(NumNeg)) {
4573
4631
  // e.g. sin^-1(x) -> arcsin(x)
4574
4632
  type = "arc" + type;
4575
4633
  exp = undefined;
@@ -4582,42 +4640,43 @@ _.extend(Trig, {
4582
4640
  trig.exp = exp;
4583
4641
  }
4584
4642
  return trig;
4585
- },
4586
- sin: function (arg) {
4643
+ }
4644
+ static sin(arg) {
4587
4645
  return new Trig("sin", arg);
4588
- },
4589
- cos: function (arg) {
4646
+ }
4647
+ static cos(arg) {
4590
4648
  return new Trig("cos", arg);
4591
- },
4592
- sinh: function (arg) {
4649
+ }
4650
+ static sinh(arg) {
4593
4651
  return new Trig("sinh", arg);
4594
- },
4595
- cosh: function (arg) {
4652
+ }
4653
+ static cosh(arg) {
4596
4654
  return new Trig("cosh", arg);
4597
4655
  }
4598
- });
4599
- function Abs(arg) {
4600
- this.arg = arg;
4601
4656
  }
4602
- Abs.prototype = new Expr();
4603
- _.extend(Abs.prototype, {
4604
- func: Abs,
4605
- args: function () {
4657
+ class Abs extends Expr {
4658
+ constructor(arg) {
4659
+ super();
4660
+ this.arg = void 0;
4661
+ this.func = Abs;
4662
+ this.arg = arg;
4663
+ }
4664
+ args() {
4606
4665
  return [this.arg];
4607
- },
4608
- eval: function (vars, options) {
4666
+ }
4667
+ eval(vars = {}, options) {
4609
4668
  return Math.abs(this.arg.eval(vars, options));
4610
- },
4611
- codegen: function () {
4669
+ }
4670
+ codegen() {
4612
4671
  return "Math.abs(" + this.arg.codegen() + ")";
4613
- },
4614
- print: function () {
4672
+ }
4673
+ print() {
4615
4674
  return "abs(" + this.arg.print() + ")";
4616
- },
4617
- tex: function () {
4675
+ }
4676
+ tex() {
4618
4677
  return "\\left|" + this.arg.tex() + "\\right|";
4619
- },
4620
- collect: function (options) {
4678
+ }
4679
+ collect(options) {
4621
4680
  var abs = this.recurse("collect", options);
4622
4681
  if (abs.arg.isPositive()) {
4623
4682
  // e.g. |2^x| -> 2^x
@@ -4627,7 +4686,7 @@ _.extend(Abs.prototype, {
4627
4686
  return abs.arg.abs();
4628
4687
  } else if (abs.arg instanceof Mul) {
4629
4688
  // e.g. |-2*pi*x| -> 2*pi*|x|
4630
- var terms = _.groupBy(abs.arg.terms, function (term) {
4689
+ var terms = _.groupBy(abs.arg.terms, term => {
4631
4690
  if (term.isPositive()) {
4632
4691
  return "positive";
4633
4692
  } else if (term instanceof Num) {
@@ -4644,55 +4703,60 @@ _.extend(Abs.prototype, {
4644
4703
  } else {
4645
4704
  return abs;
4646
4705
  }
4647
- },
4706
+ }
4707
+
4648
4708
  // this should definitely be behind a super-simplify flag
4649
- expand: function () {
4709
+ expand() {
4650
4710
  var abs = this.recurse("expand");
4651
4711
  if (abs.arg instanceof Mul) {
4652
4712
  // e.g. |xyz| -> |x|*|y|*|z|
4653
- var terms = _.map(abs.arg.terms, function (term) {
4713
+ var terms = _.map(abs.arg.terms, term => {
4654
4714
  return new Abs(term);
4655
4715
  });
4656
4716
  return new Mul(terms);
4657
4717
  } else {
4658
4718
  return abs;
4659
4719
  }
4660
- },
4661
- isPositive: function () {
4720
+ }
4721
+ isPositive() {
4662
4722
  return true;
4663
4723
  }
4664
- });
4724
+ }
4665
4725
 
4666
4726
  /* equation */
4667
- function Eq(left, type, right) {
4668
- this.left = left;
4669
- this.type = type;
4670
- this.right = right;
4671
- }
4672
- Eq.prototype = new Expr();
4673
- _.extend(Eq.prototype, {
4674
- func: Eq,
4675
- args: function () {
4727
+ class Eq extends Expr {
4728
+ constructor(left, type, right) {
4729
+ super();
4730
+ this.left = void 0;
4731
+ this.type = void 0;
4732
+ // TODO(kevinb): use an enum for this
4733
+ this.right = void 0;
4734
+ this.func = Eq;
4735
+ this.signs = {
4736
+ "=": " = ",
4737
+ "<": " < ",
4738
+ ">": " > ",
4739
+ "<>": " \\ne ",
4740
+ "<=": " \\le ",
4741
+ ">=": " \\ge "
4742
+ };
4743
+ this.left = left;
4744
+ this.type = type;
4745
+ this.right = right;
4746
+ }
4747
+ args() {
4676
4748
  return [this.left, this.type, this.right];
4677
- },
4678
- needsExplicitMul: function () {
4749
+ }
4750
+ needsExplicitMul() {
4679
4751
  return false;
4680
- },
4681
- print: function () {
4752
+ }
4753
+ print() {
4682
4754
  return this.left.print() + this.type + this.right.print();
4683
- },
4684
- signs: {
4685
- "=": " = ",
4686
- "<": " < ",
4687
- ">": " > ",
4688
- "<>": " \\ne ",
4689
- "<=": " \\le ",
4690
- ">=": " \\ge "
4691
- },
4692
- tex: function () {
4755
+ }
4756
+ tex() {
4693
4757
  return this.left.tex() + this.signs[this.type] + this.right.tex();
4694
- },
4695
- normalize: function () {
4758
+ }
4759
+ normalize() {
4696
4760
  var eq = this.recurse("normalize");
4697
4761
  if (_.contains([">", ">="], eq.type)) {
4698
4762
  // inequalities should have the smaller side on the left
@@ -4700,18 +4764,19 @@ _.extend(Eq.prototype, {
4700
4764
  } else {
4701
4765
  return eq;
4702
4766
  }
4703
- },
4767
+ }
4768
+
4704
4769
  // convert this equation to an expression set to zero
4705
4770
  // the expression is normalized to a canonical form
4706
4771
  // e.g. y/2=x/4 -> y/2-x/4(=0) -> 2y-x(=0)
4707
4772
  // unless unfactored is specified, will then divide through
4708
- asExpr: function (unfactored) {
4709
- var isZero = function isZero(expr) {
4773
+ asExpr(unfactored = false) {
4774
+ var isZero = expr => {
4710
4775
  return expr instanceof Num && expr.isSimple() && expr.eval() === 0;
4711
4776
  };
4712
4777
 
4713
4778
  // first convert to a sequence of additive terms
4714
- var terms = [];
4779
+ let terms = [];
4715
4780
  if (this.left instanceof Add) {
4716
4781
  terms = _.clone(this.left.terms);
4717
4782
  } else if (!isZero(this.left)) {
@@ -4741,8 +4806,8 @@ _.extend(Eq.prototype, {
4741
4806
  if (isInequality && !denominator.isPositive()) {
4742
4807
  denominator = denominator.asPositiveFactor();
4743
4808
  }
4744
- if (!denominator.equals(Num.One)) {
4745
- terms = _.map(terms, function (term) {
4809
+ if (!denominator.equals(NumOne)) {
4810
+ terms = _.map(terms, term => {
4746
4811
  return Mul.createOrAppend(term, denominator).simplify({
4747
4812
  once: true,
4748
4813
  preciseFloats: true
@@ -4752,41 +4817,33 @@ _.extend(Eq.prototype, {
4752
4817
  }
4753
4818
  var add = new Add(terms).flatten();
4754
4819
  return unfactored ? add : this.divideThrough(add);
4755
- },
4820
+ }
4821
+
4756
4822
  // divide through by every common factor in the expression
4757
4823
  // e.g. 2y-4x(=0) -> y-2x(=0)
4758
4824
  // TODO(alex): Make it an option to only divide by variables/expressions
4759
4825
  // guaranteed to be nonzero
4760
- divideThrough: function (expr) {
4761
- var isInequality = !this.isEquality();
4762
- var simplified = expr.simplify({
4826
+ divideThrough(expr) {
4827
+ const isInequality = !this.isEquality();
4828
+ const simplified = expr.simplify({
4763
4829
  once: true
4764
4830
  });
4765
- var factored = simplified.factor({
4831
+ const factored = simplified.factor({
4766
4832
  keepNegative: isInequality
4767
4833
  });
4768
4834
  if (!(factored instanceof Mul)) {
4769
4835
  return expr;
4770
4836
  }
4771
- var terms = factored.terms;
4772
- var isAdd = function isAdd(term) {
4773
- return term instanceof Add;
4774
- };
4775
- var hasVar = function hasVar(term) {
4776
- return !!term.getVars().length;
4777
- };
4778
- var isOne = function isOne(term) {
4779
- return term.equals(Num.One);
4780
- };
4781
- var grouped = _.groupBy(terms, isAdd);
4782
- var adds = grouped[true] || [];
4783
- var others = grouped[false] || [];
4837
+ const terms = factored.terms;
4838
+ const hasVar = term => !!term.getVars().length;
4839
+ const isOne = term => term.equals(NumOne);
4840
+ const [adds, others] = partition(terms, isAdd);
4784
4841
  if (adds.length && this.isEquality()) {
4785
4842
  // keep only Adds
4786
4843
  // e.g. 2xy(z+1)(=0) -> z+1(=0)
4787
4844
  return new Mul(adds).flatten();
4788
4845
  }
4789
- var denominator = others;
4846
+ let denominator = others;
4790
4847
  if (!adds.length) {
4791
4848
  // if no Adds, keep all variable terms to preserve meaning
4792
4849
  // e.g. 42xyz(=0) -> xyz(=0)
@@ -4800,10 +4857,10 @@ _.extend(Eq.prototype, {
4800
4857
 
4801
4858
  // don't need to divide by one
4802
4859
  denominator = _.reject(denominator, isOne);
4803
- denominator = _.map(denominator, function (term) {
4804
- return new Pow(term, Num.Div);
4860
+ denominator = _.map(denominator, term => {
4861
+ return new Pow(term, NumDiv);
4805
4862
  });
4806
- var dividedResult = new Mul(terms.concat(denominator)).collect();
4863
+ const dividedResult = new Mul(terms.concat(denominator)).collect();
4807
4864
 
4808
4865
  // If the end result is the same as the original factoring,
4809
4866
  // rollback the factoring and discard all intermediate steps.
@@ -4812,11 +4869,11 @@ _.extend(Eq.prototype, {
4812
4869
  } else {
4813
4870
  return dividedResult;
4814
4871
  }
4815
- },
4816
- isEquality: function () {
4872
+ }
4873
+ isEquality() {
4817
4874
  return _.contains(["=", "<>"], this.type);
4818
- },
4819
- compare: function (other) {
4875
+ }
4876
+ compare(other) {
4820
4877
  // expression comparisons are handled by Expr.compare()
4821
4878
  if (!(other instanceof Eq)) {
4822
4879
  return false;
@@ -4837,9 +4894,10 @@ _.extend(Eq.prototype, {
4837
4894
  } else {
4838
4895
  return expr1.compare(expr2);
4839
4896
  }
4840
- },
4897
+ }
4898
+
4841
4899
  // should only be done after compare() returns true to avoid false positives
4842
- sameForm: function (other) {
4900
+ sameForm(other) {
4843
4901
  var eq1 = this.normalize();
4844
4902
  var eq2 = other.normalize();
4845
4903
  var same = eq1.left.sameForm(eq2.left) && eq1.right.sameForm(eq2.right);
@@ -4849,71 +4907,66 @@ _.extend(Eq.prototype, {
4849
4907
  } else {
4850
4908
  return same;
4851
4909
  }
4852
- },
4910
+ }
4911
+
4853
4912
  // we don't want to override collect because it would turn y=x into y-x(=0)
4854
4913
  // instead, we ask if the equation was in that form, would it be simplified?
4855
- isSimplified: function () {
4914
+ isSimplified() {
4856
4915
  var expr = this.asExpr( /* unfactored */true);
4857
4916
  var simplified = this.divideThrough(expr).simplify();
4858
4917
  return expr.equals(simplified) && this.left.isSimplified() && this.right.isSimplified();
4859
4918
  }
4860
- });
4861
- _.extend(Eq.prototype, {
4919
+
4862
4920
  // Assumptions: Expression is of the form a+bx, and we solve for x
4863
- solveLinearEquationForVariable: function (variable) {
4921
+ solveLinearEquationForVariable(variable) {
4864
4922
  var expr = this.asExpr();
4865
- if (!expr.is(Add) || expr.terms.length !== 2) {
4923
+ if (!(expr instanceof Add) || expr.terms.length !== 2) {
4866
4924
  throw new Error("Can only handle linear equations of the form " + "a + bx (= 0)");
4867
4925
  }
4868
- var hasVar = function hasVar(term) {
4926
+ var hasVar = term => {
4869
4927
  return term.has(Var) && _.contains(term.getVars(), variable.symbol);
4870
4928
  };
4871
- var a, b;
4872
- if (hasVar(expr.terms[0])) {
4873
- a = Mul.handleNegative(expr.terms[1]);
4874
- b = Mul.handleDivide(expr.terms[0], variable);
4875
- } else {
4876
- a = Mul.handleNegative(expr.terms[0]);
4877
- b = Mul.handleDivide(expr.terms[1], variable);
4878
- }
4929
+ const termHasVar = hasVar(expr.terms[0]);
4930
+ const a = termHasVar ? Mul.handleNegative(expr.terms[1]) : Mul.handleNegative(expr.terms[0]);
4931
+ const b = termHasVar ? Mul.handleDivide(expr.terms[0], variable) : Mul.handleDivide(expr.terms[1], variable);
4879
4932
  return Mul.handleDivide(a, b).simplify();
4880
4933
  }
4881
- });
4934
+ }
4882
4935
 
4883
4936
  /* abstract symbol node */
4884
- function Symbol() {}
4885
- Symbol.prototype = new Expr();
4886
- _.extend(Symbol.prototype, {
4887
- needsExplicitMul: function () {
4937
+ class Sym extends Expr {
4938
+ needsExplicitMul() {
4888
4939
  return false;
4889
- },
4890
- findGCD: function (factor) {
4891
- if (factor instanceof Symbol || factor instanceof Num) {
4892
- return this.equals(factor) ? this : Num.One;
4940
+ }
4941
+ findGCD(factor) {
4942
+ if (factor instanceof Sym || factor instanceof Num) {
4943
+ return this.equals(factor) ? this : NumOne;
4893
4944
  } else {
4894
4945
  return factor.findGCD(this);
4895
4946
  }
4896
4947
  }
4897
- });
4948
+ }
4898
4949
 
4899
4950
  /* function variable */
4900
- function Func(symbol, arg) {
4901
- this.symbol = symbol;
4902
- this.arg = arg;
4903
- }
4904
- Func.prototype = new Symbol();
4905
- _.extend(Func.prototype, {
4906
- func: Func,
4907
- args: function () {
4951
+ class Func extends Sym {
4952
+ constructor(symbol, arg) {
4953
+ super();
4954
+ this.symbol = void 0;
4955
+ this.arg = void 0;
4956
+ this.func = Func;
4957
+ this.symbol = symbol;
4958
+ this.arg = arg;
4959
+ }
4960
+ args() {
4908
4961
  return [this.symbol, this.arg];
4909
- },
4910
- print: function () {
4962
+ }
4963
+ print() {
4911
4964
  return this.symbol + "(" + this.arg.print() + ")";
4912
- },
4913
- tex: function () {
4965
+ }
4966
+ tex() {
4914
4967
  return this.symbol + "(" + this.arg.tex() + ")";
4915
- },
4916
- eval: function (vars, options) {
4968
+ }
4969
+ eval(vars = {}, options) {
4917
4970
  var arg = this.arg;
4918
4971
  var func = vars[this.symbol];
4919
4972
  var newVars = _.extend(_.clone(vars), {
@@ -4925,191 +4978,211 @@ _.extend(Func.prototype, {
4925
4978
  }
4926
4979
  // If parsedFunc isn't actually parsed, return its error
4927
4980
  return parsedFunc;
4928
- },
4929
- codegen: function () {
4981
+ }
4982
+ codegen() {
4930
4983
  return 'vars["' + this.symbol + '"](' + this.arg.codegen() + ")";
4931
- },
4932
- getUnits: function () {
4984
+ }
4985
+ getUnits() {
4933
4986
  return this.arg.getUnits();
4934
- },
4935
- getVars: function (excludeFunc) {
4987
+ }
4988
+ getVars(excludeFunc) {
4936
4989
  if (excludeFunc) {
4937
4990
  return this.arg.getVars();
4938
4991
  } else {
4939
4992
  return _.union(this.arg.getVars(), [this.symbol]).sort();
4940
4993
  }
4941
- },
4942
- getConsts: function () {
4994
+ }
4995
+ getConsts() {
4943
4996
  return this.arg.getConsts();
4944
4997
  }
4945
- });
4998
+ }
4946
4999
 
4947
5000
  /* variable */
4948
- function Var(symbol, subscript) {
4949
- this.symbol = symbol;
4950
- this.subscript = subscript;
4951
- }
4952
- Var.prototype = new Symbol();
4953
- _.extend(Var.prototype, {
4954
- func: Var,
4955
- args: function () {
5001
+ class Var extends Sym {
5002
+ constructor(symbol, subscript) {
5003
+ super();
5004
+ this.symbol = void 0;
5005
+ this.subscript = void 0;
5006
+ this.func = Var;
5007
+ this.symbol = symbol;
5008
+ this.subscript = subscript;
5009
+ }
5010
+ args() {
4956
5011
  return [this.symbol, this.subscript];
4957
- },
4958
- exprArgs: function () {
5012
+ }
5013
+ exprArgs() {
4959
5014
  return [];
4960
- },
4961
- recurse: function () {
5015
+ }
5016
+ recurse() {
4962
5017
  return this;
4963
- },
4964
- print: function () {
5018
+ }
5019
+ print() {
4965
5020
  var sub = "";
4966
5021
  if (this.subscript) {
4967
5022
  sub = "_(" + this.subscript.print() + ")";
4968
5023
  }
4969
5024
  return this.symbol + sub;
4970
- },
5025
+ }
5026
+
4971
5027
  // Provide a way to easily evalate expressions with the common case,
4972
5028
  // subscripts that consist of a single number or symbol e.g. x_a or x_42
4973
- prettyPrint: function () {
5029
+ prettyPrint() {
4974
5030
  var sub = this.subscript;
4975
- if (sub && (sub instanceof Num || sub instanceof Symbol)) {
5031
+ if (sub && (sub instanceof Num || sub instanceof Sym)) {
4976
5032
  return this.symbol + "_" + sub.print();
4977
5033
  } else {
4978
5034
  return this.print();
4979
5035
  }
4980
- },
4981
- tex: function () {
5036
+ }
5037
+ tex() {
4982
5038
  var sub = "";
4983
5039
  if (this.subscript) {
4984
5040
  sub = "_{" + this.subscript.tex() + "}";
4985
5041
  }
4986
5042
  var prefix = this.symbol.length > 1 ? "\\" : "";
4987
5043
  return prefix + this.symbol + sub;
4988
- },
4989
- repr: function () {
5044
+ }
5045
+ repr() {
4990
5046
  return "Var(" + this.print() + ")";
4991
- },
4992
- eval: function (vars, options) {
5047
+ }
5048
+ eval(vars = {}, options) {
5049
+ // @ts-expect-error: values is Vars are strings, but here
5050
+ // we expect them to be numbers. We should probably store
5051
+ // Var and Func entries separately in the Vars type so that
5052
+ // they can be typed correctly.
4993
5053
  return vars[this.prettyPrint()];
4994
- },
4995
- codegen: function () {
5054
+ }
5055
+ codegen() {
4996
5056
  return 'vars["' + this.prettyPrint() + '"]';
4997
- },
4998
- getVars: function () {
5057
+ }
5058
+ getVars() {
4999
5059
  return [this.prettyPrint()];
5000
- },
5001
- isPositive: function () {
5060
+ }
5061
+ isPositive() {
5002
5062
  return false;
5003
5063
  }
5004
- });
5064
+ }
5005
5065
 
5006
5066
  /* constant */
5007
- function Const(symbol) {
5008
- this.symbol = symbol;
5009
- }
5010
- Const.prototype = new Symbol();
5011
- _.extend(Const.prototype, {
5012
- func: Const,
5013
- args: function () {
5067
+ class Const extends Sym {
5068
+ constructor(symbol) {
5069
+ super();
5070
+ this.symbol = void 0;
5071
+ this.func = Const;
5072
+ this.symbol = symbol;
5073
+ }
5074
+ args() {
5014
5075
  return [this.symbol];
5015
- },
5016
- recurse: function () {
5076
+ }
5077
+ recurse() {
5017
5078
  return this;
5018
- },
5019
- eval: function (vars, options) {
5079
+ }
5080
+ eval(vars = {}, options) {
5020
5081
  if (this.symbol === "pi") {
5021
5082
  return Math.PI;
5022
5083
  } else if (this.symbol === "e") {
5023
5084
  return Math.E;
5085
+ } else {
5086
+ // @ts-expect-error: should we throw an error here?
5087
+ return undefined;
5024
5088
  }
5025
- },
5026
- codegen: function () {
5089
+ }
5090
+ codegen() {
5027
5091
  if (this.symbol === "pi") {
5028
5092
  return "Math.PI";
5029
5093
  } else if (this.symbol === "e") {
5030
5094
  return "Math.E";
5095
+ } else {
5096
+ // @ts-expect-error: should we throw an error here?
5097
+ return undefined;
5031
5098
  }
5032
- },
5033
- print: function () {
5099
+ }
5100
+ print() {
5034
5101
  return this.symbol;
5035
- },
5036
- tex: function () {
5102
+ }
5103
+ tex() {
5037
5104
  if (this.symbol === "pi") {
5038
5105
  return "\\pi ";
5039
5106
  } else if (this.symbol === "e") {
5040
5107
  return "e";
5108
+ } else {
5109
+ // @ts-expect-error: should we return this.symbol here?
5110
+ return undefined;
5041
5111
  }
5042
- },
5043
- isPositive: function () {
5112
+ }
5113
+ isPositive() {
5044
5114
  return this.eval() > 0;
5045
- },
5046
- abs: function () {
5115
+ }
5116
+ abs() {
5047
5117
  if (this.eval() > 0) {
5048
5118
  return this;
5049
5119
  } else {
5050
5120
  return Mul.handleNegative(this);
5051
5121
  }
5052
- },
5053
- getConsts: function () {
5122
+ }
5123
+ getConsts() {
5054
5124
  return [this.print()];
5055
5125
  }
5056
- });
5057
- Const.e = new Const("e");
5058
- Const.pi = new Const("pi");
5126
+ }
5059
5127
 
5060
5128
  /* abstract number node */
5061
- function Num() {}
5062
- Num.prototype = new Expr();
5063
- _.extend(Num.prototype, {
5064
- repr: function () {
5129
+ _class12 = Const;
5130
+ Const.e = new _class12("e");
5131
+ Const.pi = new _class12("pi");
5132
+ class Num extends Expr {
5133
+ constructor() {
5134
+ super();
5135
+ // hints for interpreting and rendering user input
5136
+ this.n = 0;
5137
+ this.hints = _extends({}, this.hints, {
5138
+ negate: false,
5139
+ subtract: false,
5140
+ divide: false,
5141
+ root: false,
5142
+ fraction: false,
5143
+ entered: false
5144
+ });
5145
+ }
5146
+ repr() {
5065
5147
  return this.print();
5066
- },
5067
- strip: function () {
5148
+ }
5149
+ strip() {
5068
5150
  return this.abs();
5069
- },
5070
- recurse: function () {
5151
+ }
5152
+ recurse() {
5071
5153
  return this;
5072
- },
5073
- codegen: function () {
5154
+ }
5155
+ codegen() {
5074
5156
  return this.print();
5075
- },
5157
+ }
5158
+
5076
5159
  // takes another Num and returns a new Num
5077
- add: abstract,
5078
- mul: abstract,
5160
+
5079
5161
  // returns this Num's additive inverse
5080
- negate: abstract,
5081
- isSubtract: function () {
5082
- return this.hints.subtract;
5083
- },
5162
+
5163
+ isSubtract() {
5164
+ return Boolean(this.hints.subtract);
5165
+ }
5166
+
5084
5167
  // return the absolute value of the number
5085
- abs: abstract,
5086
- needsExplicitMul: function () {
5168
+
5169
+ needsExplicitMul() {
5087
5170
  return true;
5088
- },
5089
- findGCD: abstract,
5090
- isPositive: function () {
5171
+ }
5172
+ isPositive() {
5091
5173
  return this.eval() > 0;
5092
- },
5093
- isNegative: function () {
5174
+ }
5175
+ isNegative() {
5094
5176
  return this.eval() < 0;
5095
- },
5096
- asPositiveFactor: function () {
5177
+ }
5178
+ asPositiveFactor() {
5097
5179
  return this.isPositive() ? this : this.abs();
5098
- },
5099
- // hints for interpreting and rendering user input
5100
- hints: _.extend(Num.prototype.hints, {
5101
- negate: false,
5102
- subtract: false,
5103
- divide: false,
5104
- root: false,
5105
- fraction: false,
5106
- entered: false
5107
- }),
5180
+ }
5181
+
5108
5182
  // whether a number is considered simple (one term)
5109
5183
  // e.g. for reals, ints and floats are simple
5110
- isSimple: abstract,
5111
5184
  // Based on http://stackoverflow.com/a/10454560/2571482
5112
- getDecimalPlaces: function () {
5185
+ getDecimalPlaces() {
5113
5186
  var match = ("" + this.n).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
5114
5187
  if (match) {
5115
5188
  return Math.max(0,
@@ -5120,52 +5193,88 @@ _.extend(Num.prototype, {
5120
5193
  } else {
5121
5194
  return 0;
5122
5195
  }
5123
- },
5124
- asRational: abstract
5125
- });
5196
+ }
5197
+ static negativeOne(hint) {
5198
+ if (hint === "subtract") {
5199
+ return NumSub;
5200
+ } else if (hint === "divide") {
5201
+ return NumDiv;
5202
+ } else {
5203
+ return NumNeg;
5204
+ }
5205
+ }
5126
5206
 
5127
- /* rational number (n: numerator, d: denominator) */
5128
- function Rational(numerator, denominator) {
5129
- var n = numerator;
5130
- var d = denominator;
5131
- if (d < 0) {
5132
- n = -n;
5133
- d = -d;
5134
- }
5135
- this.n = n;
5136
- this.d = d;
5207
+ // find the greatest common denominator
5208
+ static findGCD(a, b) {
5209
+ var mod;
5210
+ a = Math.abs(a);
5211
+ b = Math.abs(b);
5212
+
5213
+ // Euclid's method doesn't handle non-integers very well. For now
5214
+ // we just say we can't pull out a common factor. It might be
5215
+ // reasonable to do better than this in the future.
5216
+ if (a !== Math.floor(a) || b !== Math.floor(b)) {
5217
+ return 1;
5218
+ }
5219
+ while (b) {
5220
+ mod = a % b;
5221
+ a = b;
5222
+ b = mod;
5223
+ }
5224
+ return a;
5225
+ }
5226
+ static min(...args) {
5227
+ return _.min(args, num => num.eval());
5228
+ }
5229
+ static max(...args) {
5230
+ return _.max(args, num => num.eval());
5231
+ }
5137
5232
  }
5138
- Rational.prototype = new Num();
5139
- _.extend(Rational.prototype, {
5140
- func: Rational,
5141
- args: function () {
5233
+
5234
+ /* rational number (n: numerator, d: denominator) */
5235
+ class Rational extends Num {
5236
+ constructor(numerator, denominator) {
5237
+ super();
5238
+ this.n = void 0;
5239
+ this.d = void 0;
5240
+ this.func = Rational;
5241
+ var n = numerator;
5242
+ var d = denominator;
5243
+ if (d < 0) {
5244
+ n = -n;
5245
+ d = -d;
5246
+ }
5247
+ this.n = n;
5248
+ this.d = d;
5249
+ }
5250
+ args() {
5142
5251
  return [this.n, this.d];
5143
- },
5144
- eval: function () {
5252
+ }
5253
+ eval() {
5145
5254
  return this.n / this.d;
5146
- },
5147
- print: function () {
5255
+ }
5256
+ print() {
5148
5257
  return this.n.toString() + "/" + this.d.toString();
5149
- },
5150
- tex: function () {
5258
+ }
5259
+ tex() {
5151
5260
  var tex = "\\frac{" + Math.abs(this.n).toString() + "}{" + this.d.toString() + "}";
5152
5261
  return this.n < 0 ? "-" + tex : tex;
5153
- },
5154
- add: function (num, options) {
5262
+ }
5263
+ add(num, options) {
5155
5264
  if (num instanceof Rational) {
5156
5265
  return new Rational(this.n * num.d + this.d * num.n, this.d * num.d).collect();
5157
5266
  } else {
5158
5267
  return num.add(this, options);
5159
5268
  }
5160
- },
5161
- mul: function (num, options) {
5269
+ }
5270
+ mul(num, options) {
5162
5271
  if (num instanceof Rational) {
5163
5272
  return new Rational(this.n * num.n, this.d * num.d).collect();
5164
5273
  } else {
5165
5274
  return num.mul(this, options);
5166
5275
  }
5167
- },
5168
- collect: function () {
5276
+ }
5277
+ collect() {
5169
5278
  var gcd = Num.findGCD(this.n, this.d);
5170
5279
  var n = this.n / gcd;
5171
5280
  var d = this.d / gcd;
@@ -5174,14 +5283,14 @@ _.extend(Rational.prototype, {
5174
5283
  } else {
5175
5284
  return new Rational(n, d);
5176
5285
  }
5177
- },
5178
- negate: function () {
5286
+ }
5287
+ negate() {
5179
5288
  return new Rational(-this.n, this.d);
5180
- },
5181
- abs: function () {
5289
+ }
5290
+ abs() {
5182
5291
  return new Rational(Math.abs(this.n), this.d);
5183
- },
5184
- findGCD: function (factor) {
5292
+ }
5293
+ findGCD(factor) {
5185
5294
  // Attempt to factor out common numerators and denominators to return
5186
5295
  // a Rational instead of a Float
5187
5296
  if (factor instanceof Rational) {
@@ -5196,9 +5305,10 @@ _.extend(Rational.prototype, {
5196
5305
  } else {
5197
5306
  return factor.findGCD(this);
5198
5307
  }
5199
- },
5308
+ }
5309
+
5200
5310
  // for now, assuming that exp is a Num
5201
- raiseToThe: function (exp) {
5311
+ raiseToThe(exp) {
5202
5312
  if (exp instanceof Int) {
5203
5313
  var positive = exp.eval() > 0;
5204
5314
  var abs = exp.abs().eval();
@@ -5212,120 +5322,121 @@ _.extend(Rational.prototype, {
5212
5322
  } else {
5213
5323
  return new Float(this.eval()).raiseToThe(exp);
5214
5324
  }
5215
- },
5216
- getDenominator: function () {
5325
+ }
5326
+ getDenominator() {
5217
5327
  return new Int(this.d);
5218
- },
5219
- isSimple: function () {
5328
+ }
5329
+ isSimple() {
5220
5330
  return false;
5221
- },
5222
- asRational: function () {
5331
+ }
5332
+ asRational() {
5223
5333
  return this;
5224
5334
  }
5225
- });
5335
+ }
5226
5336
 
5227
5337
  /* integer (n: numerator/number) */
5228
- function Int(number) {
5229
- this.n = number;
5230
- }
5231
- Int.prototype = new Rational(0, 1);
5232
- _.extend(Int.prototype, {
5233
- func: Int,
5234
- args: function () {
5338
+ class Int extends Rational {
5339
+ constructor(number) {
5340
+ super(number, 1);
5341
+ this.func = Int;
5342
+ }
5343
+ args() {
5235
5344
  return [this.n];
5236
- },
5237
- print: function () {
5345
+ }
5346
+ print() {
5238
5347
  return this.n.toString();
5239
- },
5240
- tex: function () {
5348
+ }
5349
+ tex() {
5241
5350
  return this.n.toString();
5242
- },
5243
- negate: function () {
5351
+ }
5352
+ negate() {
5244
5353
  return new Int(-this.n);
5245
- },
5246
- abs: function () {
5354
+ }
5355
+ abs() {
5247
5356
  return new Int(Math.abs(this.n));
5248
- },
5249
- isSimple: function () {
5357
+ }
5358
+ isSimple() {
5250
5359
  return true;
5251
- },
5252
- findGCD: function (factor) {
5360
+ }
5361
+ findGCD(factor) {
5253
5362
  if (factor instanceof Int) {
5254
5363
  return new Int(Num.findGCD(this.n, factor.n));
5255
5364
  } else {
5256
5365
  return factor.findGCD(this);
5257
5366
  }
5258
5367
  }
5259
- });
5260
- _.extend(Int, {
5261
- create: function (n) {
5368
+ static create(n) {
5262
5369
  return new Int(n).addHint("entered");
5263
5370
  }
5264
- });
5371
+ }
5265
5372
 
5266
5373
  /* float (n: number) */
5267
- function Float(number) {
5268
- this.n = number;
5269
- }
5270
- Float.prototype = new Num();
5271
- _.extend(Float.prototype, {
5272
- func: Float,
5273
- args: function () {
5374
+ class Float extends Num {
5375
+ constructor(number) {
5376
+ super();
5377
+ this.n = void 0;
5378
+ this.func = Float;
5379
+ this.n = number;
5380
+ }
5381
+ args() {
5274
5382
  return [this.n];
5275
- },
5276
- eval: function () {
5383
+ }
5384
+ eval() {
5277
5385
  return this.n;
5278
- },
5386
+ }
5387
+
5279
5388
  // TODO(alex): when we internationalize number parsing/display
5280
5389
  // we should make sure to use the appropriate decimal mark here
5281
- print: function () {
5390
+ print() {
5282
5391
  return this.n.toString();
5283
- },
5284
- tex: function () {
5392
+ }
5393
+ tex() {
5285
5394
  return this.n.toString();
5286
- },
5287
- add: function (num, options) {
5395
+ }
5396
+ add(num, options) {
5288
5397
  if (options && options.preciseFloats) {
5289
5398
  return Float.toDecimalPlaces(this.n + num.eval(), Math.max(this.getDecimalPlaces(), num.getDecimalPlaces()));
5290
5399
  } else {
5291
5400
  return new Float(this.n + num.eval()).collect();
5292
5401
  }
5293
- },
5294
- mul: function (num, options) {
5402
+ }
5403
+ mul(num, options) {
5295
5404
  if (options && options.preciseFloats) {
5296
5405
  return Float.toDecimalPlaces(this.n * num.eval(), this.getDecimalPlaces() + num.getDecimalPlaces());
5297
5406
  } else {
5298
5407
  return new Float(this.n * num.eval()).collect();
5299
5408
  }
5300
- },
5301
- collect: function () {
5409
+ }
5410
+ collect(options) {
5302
5411
  // We used to simplify Floats to Ints here whenever possible, but no
5303
5412
  // longer do so in order to preserve significant figures.
5304
5413
  return this;
5305
- },
5306
- negate: function () {
5414
+ }
5415
+ negate() {
5307
5416
  return new Float(-this.n);
5308
- },
5309
- abs: function () {
5417
+ }
5418
+ abs() {
5310
5419
  return new Float(Math.abs(this.n));
5311
- },
5312
- findGCD: function (factor) {
5420
+ }
5421
+ findGCD(factor) {
5313
5422
  if (factor instanceof Num) {
5314
5423
  return new Float(Num.findGCD(this.eval(), factor.eval())).collect();
5315
5424
  } else {
5316
5425
  return factor.findGCD(this);
5317
5426
  }
5318
- },
5427
+ }
5428
+
5319
5429
  // for now, assuming that exp is a Num
5320
- raiseToThe: function (exp, options) {
5430
+ raiseToThe(exp, options) {
5321
5431
  if (options && options.preciseFloats && exp instanceof Int && exp.n > 1) {
5322
5432
  return Float.toDecimalPlaces(new Pow(this, exp).eval(), this.getDecimalPlaces() * exp.n);
5323
5433
  } else {
5324
5434
  return new Float(new Pow(this, exp).eval()).collect();
5325
5435
  }
5326
- },
5436
+ }
5437
+
5327
5438
  // only to be used on non-repeating decimals (e.g. user-provided)
5328
- asRational: function () {
5439
+ asRational() {
5329
5440
  var parts = this.n.toString().split(".");
5330
5441
  if (parts.length === 1) {
5331
5442
  return new Rational(this.n, 1);
@@ -5334,77 +5445,30 @@ _.extend(Float.prototype, {
5334
5445
  var denominator = Math.pow(10, parts[1].length);
5335
5446
  return new Rational(numerator, denominator).collect();
5336
5447
  }
5337
- },
5338
- getDenominator: function () {
5448
+ }
5449
+ getDenominator() {
5339
5450
  return this.asRational().getDenominator();
5340
- },
5341
- isSimple: function () {
5451
+ }
5452
+ isSimple() {
5342
5453
  return true;
5343
5454
  }
5344
- });
5345
- _.extend(Float, {
5346
- create: function (n) {
5455
+ static create(n) {
5347
5456
  return new Float(n).addHint("entered");
5348
- },
5457
+ }
5458
+
5349
5459
  // Account for floating point imprecision by explicitly controlling the
5350
5460
  // number of decimal places in common operations (e.g. +, *, ^)
5351
- toDecimalPlaces: function (n, places) {
5461
+ static toDecimalPlaces(n, places) {
5352
5462
  return new Float(+n.toFixed(Math.min(places, 20))).collect();
5353
5463
  }
5354
- });
5355
-
5356
- // static methods and fields that are best defined on Num
5357
- _.extend(Num, {
5358
- negativeOne: function (hint) {
5359
- if (hint === "subtract") {
5360
- return Num.Sub;
5361
- } else if (hint === "divide") {
5362
- return Num.Div;
5363
- } else {
5364
- return Num.Neg;
5365
- }
5366
- },
5367
- // find the greatest common denominator
5368
- findGCD: function (a, b) {
5369
- var mod;
5370
- a = Math.abs(a);
5371
- b = Math.abs(b);
5372
-
5373
- // Euclid's method doesn't handle non-integers very well. For now
5374
- // we just say we can't pull out a common factor. It might be
5375
- // reasonable to do better than this in the future.
5376
- if (a !== Math.floor(a) || b !== Math.floor(b)) {
5377
- return 1;
5378
- }
5379
- while (b) {
5380
- mod = a % b;
5381
- a = b;
5382
- b = mod;
5383
- }
5384
- return a;
5385
- },
5386
- min: function () {
5387
- return _.min(_.toArray(arguments), function (num) {
5388
- return num.eval();
5389
- });
5390
- },
5391
- max: function () {
5392
- return _.max(_.toArray(arguments), function (num) {
5393
- return num.eval();
5394
- });
5395
- }
5396
- });
5397
- Num.Neg = new Int(-1).addHint("negate");
5398
- Num.Sub = new Int(-1).addHint("subtract");
5399
- Num.Div = new Int(-1).addHint("divide");
5400
- Num.Sqrt = new Rational(1, 2).addHint("root");
5401
- Num.Zero = new Int(0);
5402
- Num.One = new Int(1);
5403
- Num.Ten = new Int(10);
5404
-
5405
- // set identities here
5406
- Add.prototype.identity = Num.Zero;
5407
- Mul.prototype.identity = Num.One;
5464
+ }
5465
+ const NumNeg = new Int(-1).addHint("negate");
5466
+ const NumSub = new Int(-1).addHint("subtract");
5467
+ const NumDiv = new Int(-1).addHint("divide");
5468
+ const NumSqrt = new Rational(1, 2).addHint("root");
5469
+ const NumZero = new Int(0);
5470
+ const NumOne = new Int(1);
5471
+ const NumTen = new Int(10);
5408
5472
  var parseError = function parseError(str, hash) {
5409
5473
  // return int location of parsing error
5410
5474
  throw new Error(hash.loc.first_column);
@@ -5467,10 +5531,56 @@ const parse = function parse(input, options) {
5467
5531
  };
5468
5532
 
5469
5533
  /* unit */
5470
- function Unit(symbol) {
5471
- this.symbol = symbol;
5534
+ class Unit extends Sym {
5535
+ constructor(symbol) {
5536
+ super();
5537
+ this.symbol = void 0;
5538
+ this.func = Unit;
5539
+ this.symbol = symbol;
5540
+ }
5541
+ args() {
5542
+ return [this.symbol];
5543
+ }
5544
+ recurse() {
5545
+ return this;
5546
+ }
5547
+ eval(vars = {}, options) {
5548
+ // This is called when comparing units. A unit doesn't affect the
5549
+ // numerical value of its coefficient, so this needs to be 1.
5550
+ //
5551
+ // On the other hand, things must not evaluate to the same thing if
5552
+ // they don't have the same type. I believe that's also true - form is
5553
+ // checked before numerical equivalence. I do not know where, though.
5554
+ // However, there are a couple tests checking this.
5555
+ return 1;
5556
+ }
5557
+ getUnits() {
5558
+ return [{
5559
+ unit: this.symbol,
5560
+ pow: 1
5561
+ }];
5562
+ }
5563
+ codegen() {
5564
+ return "1";
5565
+ }
5566
+ print() {
5567
+ return this.symbol;
5568
+ }
5569
+ tex() {
5570
+ return this.symbol;
5571
+ }
5572
+
5573
+ // Simplify units by replacing prefixes with multiplication
5574
+ collect(options) {
5575
+ if (_(baseUnits).has(this.symbol)) {
5576
+ return this;
5577
+ } else if (_(derivedUnits).has(this.symbol)) {
5578
+ return derivedUnits[this.symbol].conversion;
5579
+ } else {
5580
+ throw new Error("could not understand unit: " + this.symbol);
5581
+ }
5582
+ }
5472
5583
  }
5473
- Unit.prototype = new Symbol();
5474
5584
 
5475
5585
  // If possible, replace unit prefixes with a multiplication.
5476
5586
  //
@@ -5482,7 +5592,7 @@ var unprefixify = function unprefixify(symbol) {
5482
5592
  }
5483
5593
 
5484
5594
  // check for prefix
5485
- var prefix = _(_(siPrefixes).keys()).find(function (testPrefix) {
5595
+ var prefix = _(_(siPrefixes).keys()).find(testPrefix => {
5486
5596
  return new RegExp("^" + testPrefix).test(symbol);
5487
5597
  });
5488
5598
  if (prefix) {
@@ -5522,18 +5632,18 @@ const unitParse = function unitParse(input) {
5522
5632
  //
5523
5633
  // denom is optionally null
5524
5634
 
5525
- var unitArray = [];
5526
- _(parseResult.unit.num).each(function (unitSpec) {
5635
+ const unitArray = [];
5636
+ _(parseResult.unit.num).each(unitSpec => {
5527
5637
  unitArray.push(new Pow(unprefixify(unitSpec.name), new Int(unitSpec.pow)));
5528
5638
  });
5529
- _(parseResult.unit.denom).each(function (unitSpec) {
5639
+ _(parseResult.unit.denom).each(unitSpec => {
5530
5640
  unitArray.push(new Pow(unprefixify(unitSpec.name), new Int(-1 * unitSpec.pow)));
5531
5641
  });
5532
5642
  var unit = new Mul(unitArray).flatten();
5533
5643
  if (parseResult.type === "unitMagnitude") {
5534
5644
  // in the first case we have a magnitude coefficient as well as the
5535
5645
  // unit itself.
5536
- var coefArray = [new Float(+parseResult.magnitude)].concat(unitArray);
5646
+ var coefArray = [new Float(+parseResult.magnitude), ...unitArray];
5537
5647
  var expr = new Mul(coefArray);
5538
5648
  return {
5539
5649
  parsed: true,
@@ -5557,50 +5667,6 @@ const unitParse = function unitParse(input) {
5557
5667
  };
5558
5668
  }
5559
5669
  };
5560
- _.extend(Unit.prototype, {
5561
- func: Unit,
5562
- args: function () {
5563
- return [this.symbol];
5564
- },
5565
- recurse: function () {
5566
- return this;
5567
- },
5568
- eval: function (vars, options) {
5569
- // This is called when comparing units. A unit doesn't affect the
5570
- // numerical value of its coefficient, so this needs to be 1.
5571
- //
5572
- // On the other hand, things must not evaluate to the same thing if
5573
- // they don't have the same type. I believe that's also true - form is
5574
- // checked before numerical equivalence. I do not know where, though.
5575
- // However, there are a couple tests checking this.
5576
- return 1;
5577
- },
5578
- getUnits: function () {
5579
- return [{
5580
- unit: this.symbol,
5581
- pow: 1
5582
- }];
5583
- },
5584
- codegen: function () {
5585
- return "1";
5586
- },
5587
- print: function () {
5588
- return this.symbol;
5589
- },
5590
- tex: function () {
5591
- return this.symbol;
5592
- },
5593
- // Simplify units by replacing prefixes with multiplication
5594
- collect: function (options) {
5595
- if (_(baseUnits).has(this.symbol)) {
5596
- return this;
5597
- } else if (_(derivedUnits).has(this.symbol)) {
5598
- return derivedUnits[this.symbol].conversion;
5599
- } else {
5600
- throw new Error("could not understand unit: " + this.symbol);
5601
- }
5602
- }
5603
- });
5604
5670
  var baseUnits = {
5605
5671
  m: new Unit("m"),
5606
5672
  // Note: kg is the SI base unit but we use g for consistency
@@ -5633,31 +5699,23 @@ var siPrefixes = {
5633
5699
  };
5634
5700
 
5635
5701
  // Use these two values to mark a unit as either SI-prefixable or not.
5636
- var hasPrefixes = {};
5637
- var hasntPrefixes = {};
5638
- var makeAlias = function makeAlias(str, prefixes) {
5702
+ const hasPrefixes = "hasPrefixes";
5703
+ const hasntPrefixes = "hasntPrefixes";
5704
+ const makeAlias = function makeAlias(str, prefixes) {
5639
5705
  var splits = str.split("|");
5640
5706
  var coefficientStr = splits[0].trim();
5641
5707
  var unitsStr = splits[1].trim();
5642
- var coefficient = Num.One;
5708
+ var coefficient = NumOne;
5643
5709
  if (coefficientStr !== "") {
5644
5710
  coefficient = parse(coefficientStr).expr;
5645
5711
  }
5646
5712
  var numdenomStr = unitsStr.split("/");
5647
5713
  var numdenom = [coefficient];
5648
5714
  if (numdenomStr[0]) {
5649
- numdenomStr[0].split(" ").filter(function (x) {
5650
- return x !== "";
5651
- }).map(function (x) {
5652
- numdenom.push(new Unit(x));
5653
- });
5715
+ numdenomStr[0].split(" ").filter(x => x !== "").forEach(x => numdenom.push(new Unit(x)));
5654
5716
  }
5655
5717
  if (numdenomStr[1]) {
5656
- numdenomStr[1].split(" ").filter(function (x) {
5657
- return x !== "";
5658
- }).map(function (x) {
5659
- numdenom.push(new Pow(new Unit(x), Num.Div));
5660
- });
5718
+ numdenomStr[1].split(" ").filter(x => x !== "").forEach(x => numdenom.push(new Pow(new Unit(x), NumDiv)));
5661
5719
  }
5662
5720
  return {
5663
5721
  conversion: new Mul(numdenom),
@@ -5814,18 +5872,8 @@ var derivedUnits = {
5814
5872
  // other
5815
5873
  Hz: makeAlias("| / s", hasPrefixes)
5816
5874
  };
5817
- const Zero = Num.Zero;
5818
- const One = Num.One;
5819
-
5820
- function _extends() {
5821
- return _extends = Object.assign ? Object.assign.bind() : function (n) {
5822
- for (var e = 1; e < arguments.length; e++) {
5823
- var t = arguments[e];
5824
- for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
5825
- }
5826
- return n;
5827
- }, _extends.apply(null, arguments);
5828
- }
5875
+ const Zero = NumZero;
5876
+ const One = NumOne;
5829
5877
 
5830
5878
  // Assumes that both expressions have already been parsed
5831
5879
  // TODO(alex): be able to pass a random() function to compare()