From fd925071e59bc0fc25f66c7f6b9df1b462091290 Mon Sep 17 00:00:00 2001
From: thepaperpilot <thepaperpilot@gmail.com>
Date: Tue, 14 Feb 2023 01:12:11 -0600
Subject: [PATCH] Fix some tests

---
 src/game/formulas.ts        | 55 ++++++++++++++++++++++++++-----------
 tests/game/formulas.test.ts | 32 ++++++++++++++-------
 2 files changed, 61 insertions(+), 26 deletions(-)

diff --git a/src/game/formulas.ts b/src/game/formulas.ts
index 10b3fcd..253e0db 100644
--- a/src/game/formulas.ts
+++ b/src/game/formulas.ts
@@ -829,7 +829,9 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
         // eslint-disable-next-line @typescript-eslint/ban-ts-comment
         // @ts-ignore
         this.internalInvertIntegral =
-            this.internalHasVariable && variable?.isIntegralInvertible() ? invert : undefined;
+            this.internalHasVariable && variable?.isIntegralInvertible()
+                ? invertIntegral
+                : undefined;
     }
 
     /** Type predicate that this formula can be inverted. */
@@ -847,7 +849,10 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
 
     /** Type predicate that this formula has an integral function that can be inverted. */
     isIntegralInvertible(): this is InvertibleIntegralFormula {
-        return this.internalHasVariable && this.internalInvertIntegral != null;
+        return (
+            this.internalHasVariable &&
+            (this.internalInvertIntegral != null || this.internalEvaluate == null)
+        );
     }
 
     /** Whether or not this formula has a singular variable inside it, which can be accessed via {@link innermostVariable}. */
@@ -892,11 +897,12 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
      * @see {@link isIntegrable}
      */
     evaluateIntegral(variable?: DecimalSource): DecimalSource {
-        return (
-            this.internalIntegrate?.call(this, variable, ...this.inputs) ??
-            variable ??
-            unrefFormulaSource(this.inputs[0])
-        );
+        if (this.internalIntegrate) {
+            return this.internalIntegrate.call(this, variable, ...this.inputs);
+        } else if (this.inputs.length === 1 && this.internalHasVariable) {
+            return variable ?? unrefFormulaSource(this.inputs[0]);
+        }
+        throw "Cannot integrate formula without variable";
     }
 
     /**
@@ -907,7 +913,12 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
     invertIntegral(value: DecimalSource): DecimalSource {
         // This is nearly completely non-functional
         // Proper nesting will require somehow using integration by substitution or integration by parts
-        return this.internalInvertIntegral?.call(this, value, ...this.inputs) ?? value;
+        if (this.internalInvertIntegral) {
+            return this.internalInvertIntegral.call(this, value, ...this.inputs);
+        } else if (this.inputs.length === 1 && this.internalHasVariable) {
+            return value;
+        }
+        throw "Cannot invert integral of formula without invertible integral";
     }
 
     /**
@@ -962,10 +973,12 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
     public static step(
         value: FormulaSource,
         start: Computable<DecimalSource>,
-        formulaModifier: (value: Ref<DecimalSource>) => GenericFormula
+        formulaModifier: (
+            value: InvertibleFormula & IntegrableFormula & InvertibleIntegralFormula
+        ) => GenericFormula
     ): GenericFormula {
         const lhsRef = ref<DecimalSource>(0);
-        const formula = formulaModifier(lhsRef);
+        const formula = formulaModifier(Formula.variable(lhsRef));
         const processedStart = convertComputable(start);
         function evalStep(lhs: DecimalSource) {
             if (Decimal.lt(lhs, unref(processedStart))) {
@@ -1002,10 +1015,12 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
     public static if(
         value: FormulaSource,
         condition: Computable<boolean>,
-        formulaModifier: (value: Ref<DecimalSource>) => GenericFormula
+        formulaModifier: (
+            value: InvertibleFormula & IntegrableFormula & InvertibleIntegralFormula
+        ) => GenericFormula
     ): GenericFormula {
         const lhsRef = ref<DecimalSource>(0);
-        const formula = formulaModifier(lhsRef);
+        const formula = formulaModifier(Formula.variable(lhsRef));
         const processedCondition = convertComputable(condition);
         function evalStep(lhs: DecimalSource) {
             if (unref(processedCondition)) {
@@ -1035,7 +1050,9 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
     public static conditional(
         value: FormulaSource,
         condition: Computable<boolean>,
-        formulaModifier: (value: Ref<DecimalSource>) => GenericFormula
+        formulaModifier: (
+            value: InvertibleFormula & IntegrableFormula & InvertibleIntegralFormula
+        ) => GenericFormula
     ) {
         return Formula.if(value, condition, formulaModifier);
     }
@@ -1632,20 +1649,26 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
 
     public step(
         start: Computable<DecimalSource>,
-        formulaModifier: (value: Ref<DecimalSource>) => GenericFormula
+        formulaModifier: (
+            value: InvertibleFormula & IntegrableFormula & InvertibleIntegralFormula
+        ) => GenericFormula
     ) {
         return Formula.step(this, start, formulaModifier);
     }
 
     public if(
         condition: Computable<boolean>,
-        formulaModifier: (value: Ref<DecimalSource>) => GenericFormula
+        formulaModifier: (
+            value: InvertibleFormula & IntegrableFormula & InvertibleIntegralFormula
+        ) => GenericFormula
     ) {
         return Formula.if(this, condition, formulaModifier);
     }
     public conditional(
         condition: Computable<boolean>,
-        formulaModifier: (value: Ref<DecimalSource>) => GenericFormula
+        formulaModifier: (
+            value: InvertibleFormula & IntegrableFormula & InvertibleIntegralFormula
+        ) => GenericFormula
     ) {
         return Formula.if(this, condition, formulaModifier);
     }
diff --git a/tests/game/formulas.test.ts b/tests/game/formulas.test.ts
index 47f8d26..c00d43b 100644
--- a/tests/game/formulas.test.ts
+++ b/tests/game/formulas.test.ts
@@ -569,8 +569,8 @@ describe("Integrating", () => {
                     checkFormula(Formula[name](variable, constant)));
                 test(`${name}(const, var) is marked as integrable`, () =>
                     checkFormula(Formula[name](constant, variable)));
-                test(`${name}(var, var) is marked as integrable`, () =>
-                    checkFormula(Formula[name](variable, variable)));
+                test(`${name}(var, var) is marked as not integrable`, () =>
+                    expect(Formula[name](variable, variable).isIntegrable()).toBe(false));
             });
         });
     });
@@ -872,20 +872,29 @@ describe("Custom Formulas", () => {
     describe("Formula with invert", () => {
         test("Zero input inverts correctly", () =>
             expect(
-                new Formula({ inputs: [], evaluate: () => 6, invert: value => value }).invert(10)
+                new Formula({
+                    inputs: [],
+                    evaluate: () => 6,
+                    invert: value => value,
+                    variable: ref(10)
+                }).invert(10)
             ).compare_tolerance(10));
         test("One input inverts correctly", () =>
             expect(
-                new Formula({ inputs: [1], evaluate: () => 10, invert: (value, v1) => v1 }).invert(
-                    10
-                )
+                new Formula({
+                    inputs: [1],
+                    evaluate: () => 10,
+                    invert: (value, v1) => v1,
+                    variable: ref(10)
+                }).invert(10)
             ).compare_tolerance(1));
         test("Two inputs inverts correctly", () =>
             expect(
                 new Formula({
                     inputs: [1, 2],
                     evaluate: () => 10,
-                    invert: (value, v1, v2) => v2
+                    invert: (value, v1, v2) => v2,
+                    variable: ref(10)
                 }).invert(10)
             ).compare_tolerance(2));
     });
@@ -923,7 +932,8 @@ describe("Custom Formulas", () => {
                 new Formula({
                     inputs: [],
                     evaluate: () => 10,
-                    invertIntegral: () => 1
+                    invertIntegral: () => 1,
+                    variable: ref(10)
                 }).invertIntegral(8)
             ).compare_tolerance(1));
         test("One input inverts integral correctly", () =>
@@ -931,7 +941,8 @@ describe("Custom Formulas", () => {
                 new Formula({
                     inputs: [1],
                     evaluate: () => 10,
-                    invertIntegral: val => 1
+                    invertIntegral: val => 1,
+                    variable: ref(10)
                 }).invertIntegral(8)
             ).compare_tolerance(1));
         test("Two inputs inverts integral correctly", () =>
@@ -939,7 +950,8 @@ describe("Custom Formulas", () => {
                 new Formula({
                     inputs: [1, 2],
                     evaluate: (v1, v2) => 10,
-                    invertIntegral: (v1, v2) => 1
+                    invertIntegral: (v1, v2) => 1,
+                    variable: ref(10)
                 }).invertIntegral(8)
             ).compare_tolerance(1));
     });