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 sgn = InternalFormula.sign;
|
||||||
|
|
||||||
public static round(value: FormulaSource) {
|
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) {
|
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) {
|
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) {
|
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;
|
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({
|
return new Formula({
|
||||||
inputs: [value, min, max],
|
inputs: [value, min, max],
|
||||||
evaluate: Decimal.clamp,
|
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 Decimal, { DecimalSource } from "util/bignum";
|
||||||
import Formula, { hasVariable, unrefFormulaSource } from "./formulas";
|
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);
|
const ln10 = Decimal.ln(10);
|
||||||
|
|
||||||
|
@ -8,6 +14,15 @@ export function passthrough<T extends GenericFormula | DecimalSource>(value: T):
|
||||||
return value;
|
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) {
|
export function invertNeg(value: DecimalSource, lhs: FormulaSource) {
|
||||||
if (hasVariable(lhs)) {
|
if (hasVariable(lhs)) {
|
||||||
return lhs.invert(Decimal.neg(value));
|
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 testValues = [-2, "0", new Decimal(10.5)] as const;
|
||||||
|
|
||||||
const invertibleZeroParamFunctionNames = [
|
const invertibleZeroParamFunctionNames = [
|
||||||
|
"round",
|
||||||
|
"floor",
|
||||||
|
"ceil",
|
||||||
|
"trunc",
|
||||||
"neg",
|
"neg",
|
||||||
"recip",
|
"recip",
|
||||||
"log10",
|
"log10",
|
||||||
|
@ -48,10 +52,6 @@ const invertibleZeroParamFunctionNames = [
|
||||||
const nonInvertibleZeroParamFunctionNames = [
|
const nonInvertibleZeroParamFunctionNames = [
|
||||||
"abs",
|
"abs",
|
||||||
"sign",
|
"sign",
|
||||||
"round",
|
|
||||||
"floor",
|
|
||||||
"ceil",
|
|
||||||
"trunc",
|
|
||||||
"pLog10",
|
"pLog10",
|
||||||
"absLog10",
|
"absLog10",
|
||||||
"factorial",
|
"factorial",
|
||||||
|
@ -85,6 +85,10 @@ const integrableZeroParamFunctionNames = [
|
||||||
] as const;
|
] as const;
|
||||||
const nonIntegrableZeroParamFunctionNames = [
|
const nonIntegrableZeroParamFunctionNames = [
|
||||||
...nonInvertibleZeroParamFunctionNames,
|
...nonInvertibleZeroParamFunctionNames,
|
||||||
|
"round",
|
||||||
|
"floor",
|
||||||
|
"ceil",
|
||||||
|
"trunc",
|
||||||
"lambertw",
|
"lambertw",
|
||||||
"ssqrt"
|
"ssqrt"
|
||||||
] as const;
|
] as const;
|
||||||
|
@ -151,7 +155,7 @@ describe("Formula Equality Checking", () => {
|
||||||
describe("Formula aliases", () => {
|
describe("Formula aliases", () => {
|
||||||
function testAliases<T extends FormulaFunctions>(
|
function testAliases<T extends FormulaFunctions>(
|
||||||
aliases: T[],
|
aliases: T[],
|
||||||
args: Parameters<(typeof Formula)[T]>
|
args: Parameters<typeof Formula[T]>
|
||||||
) {
|
) {
|
||||||
describe(aliases[0], () => {
|
describe(aliases[0], () => {
|
||||||
let formula: GenericFormula;
|
let formula: GenericFormula;
|
||||||
|
@ -246,7 +250,7 @@ describe("Creating Formulas", () => {
|
||||||
|
|
||||||
function checkFormula<T extends FormulaFunctions>(
|
function checkFormula<T extends FormulaFunctions>(
|
||||||
functionName: T,
|
functionName: T,
|
||||||
args: Readonly<Parameters<(typeof Formula)[T]>>
|
args: Readonly<Parameters<typeof Formula[T]>>
|
||||||
) {
|
) {
|
||||||
let formula: GenericFormula;
|
let formula: GenericFormula;
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
|
@ -270,7 +274,7 @@ describe("Creating Formulas", () => {
|
||||||
// It's a lot of tests, but I'd rather be exhaustive
|
// It's a lot of tests, but I'd rather be exhaustive
|
||||||
function testFormulaCall<T extends FormulaFunctions>(
|
function testFormulaCall<T extends FormulaFunctions>(
|
||||||
functionName: T,
|
functionName: T,
|
||||||
args: Readonly<Parameters<(typeof Formula)[T]>>
|
args: Readonly<Parameters<typeof Formula[T]>>
|
||||||
) {
|
) {
|
||||||
if ((functionName === "slog" || functionName === "layeradd") && args[0] === -1) {
|
if ((functionName === "slog" || functionName === "layeradd") && args[0] === -1) {
|
||||||
// These cases in particular take a long time, so skip them
|
// These cases in particular take a long time, so skip them
|
||||||
|
@ -488,18 +492,18 @@ describe("Inverting", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Inverting nested formulas", () => {
|
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);
|
expect(formula.invert(100)).compare_tolerance(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Inverting with non-invertible sections", () => {
|
describe("Inverting with non-invertible sections", () => {
|
||||||
test("Non-invertible constant", () => {
|
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.isInvertible()).toBe(true);
|
||||||
expect(() => formula.invert(10)).not.toLogError();
|
expect(() => formula.invert(10)).not.toLogError();
|
||||||
});
|
});
|
||||||
test("Non-invertible variable", () => {
|
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.isInvertible()).toBe(false);
|
||||||
expect(() => formula.invert(10)).toLogError();
|
expect(() => formula.invert(10)).toLogError();
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue