forked from profectus/Profectus
Fix passthroughs for inversions and make more operations invertible
This commit is contained in:
parent
9edda4d957
commit
eee5ac3e2d
3 changed files with 51 additions and 16 deletions
|
@ -345,19 +345,35 @@ export abstract class InternalFormula<T extends [FormulaSource] | FormulaSource[
|
|||
public static sgn = InternalFormula.sign;
|
||||
|
||||
public static round(value: FormulaSource) {
|
||||
return new Formula({ inputs: [value], evaluate: Decimal.round });
|
||||
return new Formula({
|
||||
inputs: [value],
|
||||
evaluate: Decimal.round,
|
||||
invert: ops.invertPassthrough
|
||||
});
|
||||
}
|
||||
|
||||
public static floor(value: FormulaSource) {
|
||||
return new Formula({ inputs: [value], evaluate: Decimal.floor });
|
||||
return new Formula({
|
||||
inputs: [value],
|
||||
evaluate: Decimal.floor,
|
||||
invert: ops.invertPassthrough
|
||||
});
|
||||
}
|
||||
|
||||
public static ceil(value: FormulaSource) {
|
||||
return new Formula({ inputs: [value], evaluate: Decimal.ceil });
|
||||
return new Formula({
|
||||
inputs: [value],
|
||||
evaluate: Decimal.ceil,
|
||||
invert: ops.invertPassthrough
|
||||
});
|
||||
}
|
||||
|
||||
public static trunc(value: FormulaSource) {
|
||||
return new Formula({ inputs: [value], evaluate: Decimal.trunc });
|
||||
return new Formula({
|
||||
inputs: [value],
|
||||
evaluate: Decimal.trunc,
|
||||
invert: ops.invertPassthrough
|
||||
});
|
||||
}
|
||||
|
||||
public static add<T extends GenericFormula>(value: T, other: FormulaSource): T;
|
||||
|
@ -459,7 +475,7 @@ export abstract class InternalFormula<T extends [FormulaSource] | FormulaSource[
|
|||
return new Formula({
|
||||
inputs: [value, min, max],
|
||||
evaluate: Decimal.clamp,
|
||||
invert: ops.passthrough as InvertFunction<[FormulaSource, FormulaSource, FormulaSource]>
|
||||
invert: ops.invertPassthrough
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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<T extends GenericFormula | DecimalSource>(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));
|
||||
|
|
|
@ -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<T extends FormulaFunctions>(
|
||||
aliases: T[],
|
||||
args: Parameters<(typeof Formula)[T]>
|
||||
args: Parameters<typeof Formula[T]>
|
||||
) {
|
||||
describe(aliases[0], () => {
|
||||
let formula: GenericFormula;
|
||||
|
@ -246,7 +250,7 @@ describe("Creating Formulas", () => {
|
|||
|
||||
function checkFormula<T extends FormulaFunctions>(
|
||||
functionName: T,
|
||||
args: Readonly<Parameters<(typeof Formula)[T]>>
|
||||
args: Readonly<Parameters<typeof Formula[T]>>
|
||||
) {
|
||||
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<T extends FormulaFunctions>(
|
||||
functionName: T,
|
||||
args: Readonly<Parameters<(typeof Formula)[T]>>
|
||||
args: Readonly<Parameters<typeof Formula[T]>>
|
||||
) {
|
||||
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();
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue