diff --git a/src/game/formulas/formulas.ts b/src/game/formulas/formulas.ts index d9a6bed..7f88d0c 100644 --- a/src/game/formulas/formulas.ts +++ b/src/game/formulas/formulas.ts @@ -345,19 +345,35 @@ export abstract class InternalFormula(value: T, other: FormulaSource): T; @@ -459,7 +475,7 @@ export abstract class InternalFormula + invert: ops.invertPassthrough }); } diff --git a/src/game/formulas/operations.ts b/src/game/formulas/operations.ts index e8bd04d..586319e 100644 --- a/src/game/formulas/operations.ts +++ b/src/game/formulas/operations.ts @@ -1,6 +1,12 @@ import Decimal, { DecimalSource } from "util/bignum"; import Formula, { hasVariable, unrefFormulaSource } from "./formulas"; -import { FormulaSource, GenericFormula, InvertFunction, SubstitutionStack } from "./types"; +import { + FormulaSource, + GenericFormula, + InvertFunction, + InvertibleFormula, + SubstitutionStack +} from "./types"; const ln10 = Decimal.ln(10); @@ -8,6 +14,15 @@ export function passthrough(value: T): return value; } +export function invertPassthrough(value: DecimalSource, ...inputs: FormulaSource[]) { + const variable = inputs.find(input => hasVariable(input)) as InvertibleFormula | undefined; + if (variable == null) { + console.error("Could not invert due to no input being a variable"); + return 0; + } + return variable.invert(value); +} + export function invertNeg(value: DecimalSource, lhs: FormulaSource) { if (hasVariable(lhs)) { return lhs.invert(Decimal.neg(value)); diff --git a/tests/game/formulas.test.ts b/tests/game/formulas.test.ts index 5209ff8..98bb82d 100644 --- a/tests/game/formulas.test.ts +++ b/tests/game/formulas.test.ts @@ -16,6 +16,10 @@ type FormulaFunctions = keyof GenericFormula & keyof typeof Formula & keyof type const testValues = [-2, "0", new Decimal(10.5)] as const; const invertibleZeroParamFunctionNames = [ + "round", + "floor", + "ceil", + "trunc", "neg", "recip", "log10", @@ -48,10 +52,6 @@ const invertibleZeroParamFunctionNames = [ const nonInvertibleZeroParamFunctionNames = [ "abs", "sign", - "round", - "floor", - "ceil", - "trunc", "pLog10", "absLog10", "factorial", @@ -85,6 +85,10 @@ const integrableZeroParamFunctionNames = [ ] as const; const nonIntegrableZeroParamFunctionNames = [ ...nonInvertibleZeroParamFunctionNames, + "round", + "floor", + "ceil", + "trunc", "lambertw", "ssqrt" ] as const; @@ -151,7 +155,7 @@ describe("Formula Equality Checking", () => { describe("Formula aliases", () => { function testAliases( aliases: T[], - args: Parameters<(typeof Formula)[T]> + args: Parameters ) { describe(aliases[0], () => { let formula: GenericFormula; @@ -246,7 +250,7 @@ describe("Creating Formulas", () => { function checkFormula( functionName: T, - args: Readonly> + args: Readonly> ) { let formula: GenericFormula; beforeAll(() => { @@ -270,7 +274,7 @@ describe("Creating Formulas", () => { // It's a lot of tests, but I'd rather be exhaustive function testFormulaCall( functionName: T, - args: Readonly> + args: Readonly> ) { if ((functionName === "slog" || functionName === "layeradd") && args[0] === -1) { // These cases in particular take a long time, so skip them @@ -488,18 +492,18 @@ describe("Inverting", () => { }); test("Inverting nested formulas", () => { - const formula = Formula.add(variable, constant).times(constant); + const formula = Formula.add(variable, constant).times(constant).floor(); expect(formula.invert(100)).compare_tolerance(0); }); describe("Inverting with non-invertible sections", () => { test("Non-invertible constant", () => { - const formula = Formula.add(variable, constant.ceil()); + const formula = Formula.add(variable, constant.sign()); expect(formula.isInvertible()).toBe(true); expect(() => formula.invert(10)).not.toLogError(); }); test("Non-invertible variable", () => { - const formula = Formula.add(variable.ceil(), constant); + const formula = Formula.add(variable.sign(), constant); expect(formula.isInvertible()).toBe(false); expect(() => formula.invert(10)).toLogError(); });