WIP integral rework

This commit is contained in:
thepaperpilot 2023-04-01 15:55:17 -05:00
parent d7e2658304
commit a91efffd5c
4 changed files with 283 additions and 431 deletions

View file

@ -1,7 +1,7 @@
import { Resource } from "features/resources/resource"; import { Resource } from "features/resources/resource";
import Decimal, { DecimalSource } from "util/bignum"; import Decimal, { DecimalSource } from "util/bignum";
import { Computable, convertComputable, ProcessedComputable } from "util/computed"; import { Computable, convertComputable, ProcessedComputable } from "util/computed";
import { computed, ComputedRef, ref, unref } from "vue"; import { computed, ComputedRef, Ref, ref, unref } from "vue";
import type { import type {
EvaluateFunction, EvaluateFunction,
FormulaOptions, FormulaOptions,
@ -15,7 +15,6 @@ import type {
InvertFunction, InvertFunction,
InvertibleFormula, InvertibleFormula,
InvertibleIntegralFormula, InvertibleIntegralFormula,
InvertIntegralFunction,
SubstitutionFunction, SubstitutionFunction,
SubstitutionStack SubstitutionStack
} from "./types"; } from "./types";
@ -29,8 +28,8 @@ export function unrefFormulaSource(value: FormulaSource, variable?: DecimalSourc
return value instanceof Formula ? value.evaluate(variable) : unref(value); return value instanceof Formula ? value.evaluate(variable) : unref(value);
} }
function integrateVariable(variable: DecimalSource) { function integrateVariable(this: GenericFormula) {
return Decimal.pow(variable, 2).div(2); return Formula.pow(this, 2).div(2);
} }
function integrateVariableInner(this: GenericFormula, variable?: DecimalSource) { function integrateVariableInner(this: GenericFormula, variable?: DecimalSource) {
@ -54,11 +53,12 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
private readonly internalIntegrate: IntegrateFunction<T> | undefined; private readonly internalIntegrate: IntegrateFunction<T> | undefined;
private readonly internalIntegrateInner: IntegrateFunction<T> | undefined; private readonly internalIntegrateInner: IntegrateFunction<T> | undefined;
private readonly applySubstitution: SubstitutionFunction<T> | undefined; private readonly applySubstitution: SubstitutionFunction<T> | undefined;
private readonly internalInvertIntegral: InvertIntegralFunction<T> | undefined;
private readonly internalHasVariable: boolean; private readonly internalHasVariable: boolean;
public readonly innermostVariable: ProcessedComputable<DecimalSource> | undefined; public readonly innermostVariable: ProcessedComputable<DecimalSource> | undefined;
private integralFormula: GenericFormula | undefined;
constructor(options: FormulaOptions<T>) { constructor(options: FormulaOptions<T>) {
let readonlyProperties; let readonlyProperties;
if ("variable" in options) { if ("variable" in options) {
@ -75,7 +75,6 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
this.internalInvert = readonlyProperties.internalInvert; this.internalInvert = readonlyProperties.internalInvert;
this.internalIntegrate = readonlyProperties.internalIntegrate; this.internalIntegrate = readonlyProperties.internalIntegrate;
this.internalIntegrateInner = readonlyProperties.internalIntegrateInner; this.internalIntegrateInner = readonlyProperties.internalIntegrateInner;
this.internalInvertIntegral = readonlyProperties.internalInvertIntegral;
this.applySubstitution = readonlyProperties.applySubstitution; this.applySubstitution = readonlyProperties.applySubstitution;
} }
@ -112,10 +111,9 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
integrate, integrate,
integrateInner, integrateInner,
applySubstitution, applySubstitution,
invertIntegral,
hasVariable hasVariable
} = options; } = options;
if (invert == null && invertIntegral == null && hasVariable) { if (invert == null && hasVariable) {
throw new Error( throw new Error(
"A formula cannot be marked as having a variable if it is not invertible" "A formula cannot be marked as having a variable if it is not invertible"
); );
@ -132,8 +130,6 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
numVariables === 1 || (numVariables === 0 && hasVariable === true); numVariables === 1 || (numVariables === 0 && hasVariable === true);
const innermostVariable = internalHasVariable ? variable?.innermostVariable : undefined; const innermostVariable = internalHasVariable ? variable?.innermostVariable : undefined;
const internalInvert = internalHasVariable && variable?.isInvertible() ? invert : undefined; const internalInvert = internalHasVariable && variable?.isInvertible() ? invert : undefined;
const internalInvertIntegral =
internalHasVariable && variable?.isIntegralInvertible() ? invertIntegral : undefined;
return { return {
inputs, inputs,
@ -141,13 +137,19 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
internalInvert, internalInvert,
internalIntegrate: integrate, internalIntegrate: integrate,
internalIntegrateInner: integrateInner, internalIntegrateInner: integrateInner,
internalInvertIntegral,
applySubstitution, applySubstitution,
innermostVariable, innermostVariable,
internalHasVariable internalHasVariable
}; };
} }
private calculateConstantOfIntegration() {
// Calculate C based on the knowledge that at 1 purchase, the total sum would be the cost of that one purchase
const integral = this.getIntegralFormula().evaluate(1);
const actualCost = this.evaluate(0);
return Decimal.sub(actualCost, integral);
}
/** Type predicate that this formula can be inverted. */ /** Type predicate that this formula can be inverted. */
isInvertible(): this is InvertibleFormula { isInvertible(): this is InvertibleFormula {
return ( return (
@ -163,10 +165,10 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
/** Type predicate that this formula has an integral function that can be inverted. */ /** Type predicate that this formula has an integral function that can be inverted. */
isIntegralInvertible(): this is InvertibleIntegralFormula { isIntegralInvertible(): this is InvertibleIntegralFormula {
return ( if (!this.isIntegrable()) {
this.internalHasVariable && return false;
(this.internalInvertIntegral != null || this.internalEvaluate == null) }
); return this.getIntegralFormula().isInvertible();
} }
/** Whether or not this formula has a singular variable inside it, which can be accessed via {@link innermostVariable}. */ /** Whether or not this formula has a singular variable inside it, which can be accessed via {@link innermostVariable}. */
@ -208,71 +210,101 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
/** /**
* Evaluate the result of the indefinite integral (sans the constant of integration). Only works if there's a single variable and the formula is integrable. The formula can only have one "complex" operation (anything besides +,-,*,/). * Evaluate the result of the indefinite integral (sans the constant of integration). Only works if there's a single variable and the formula is integrable. The formula can only have one "complex" operation (anything besides +,-,*,/).
* @param variable Optionally override the value of the variable while evaluating * @param variable Optionally override the value of the variable while evaluating
* @param stack The list of callbacks to run to handle simple operations inside the complex operation. Used in nested formulas
* @see {@link isIntegrable} * @see {@link isIntegrable}
*/ */
evaluateIntegral(variable?: DecimalSource, stack?: SubstitutionStack): DecimalSource { evaluateIntegral(variable?: DecimalSource): DecimalSource {
if (!this.isIntegrable()) {
throw new Error("Cannot evaluate integral of formula without integral");
}
return Decimal.add(
this.getIntegralFormula().evaluate(variable),
this.calculateConstantOfIntegration()
);
}
/**
* Given the potential result of the formula's integral (and the constant of integration), calculate what value the variable inside the formula would have to be for that result to occur. Only works if there's a single variable and if the formula's integral is invertible.
* @param value The result of the integral.
* @see {@link isIntegralInvertible}
*/
invertIntegral(value: DecimalSource): DecimalSource {
if (this.integralFormula?.isInvertible()) {
throw new Error("Cannot invert integral of formula without invertible integral");
}
return this.getIntegralFormula().invert(value);
}
/**
* Get a formula that will evaluate to the integral of this formula. May also be invertible.
* @param variable The variable that will be used to evaluate this integral at a given x value
* @param stack For nested formulas, a stack of operations that occur outside the complex operation
*/
getIntegralFormula(
variable?: ProcessedComputable<DecimalSource>,
stack?: SubstitutionStack
): GenericFormula {
if (variable == null && this.integralFormula != null) {
return this.integralFormula;
}
let formula;
const variablePresent = variable != null;
if (variable == null) {
variable = this.innermostVariable;
if (variable == null) {
throw new Error("Cannot integrate formula without variable");
}
}
if (stack == null) { if (stack == null) {
// "Outer" part of the formula // "Outer" part of the formula
if (this.applySubstitution == null) { if (this.applySubstitution == null) {
// We're the complex operation of this formula // We're the complex operation of this formula
stack = []; stack = [];
if (this.internalIntegrate == null) { if (this.internalIntegrate == null) {
throw new Error("Cannot integrate formula with non-existent operation"); throw new Error("Cannot integrate formula with non-integrable operation");
} }
let value = this.internalIntegrate.call(this, variable, stack, ...this.inputs); let value = this.internalIntegrate.call(this, variable, stack, ...this.inputs);
stack.forEach(func => (value = func(value))); stack.forEach(func => (value = func(value)));
return value; formula = value;
} else { } else {
// Continue digging into the formula // Continue digging into the formula
if (this.internalIntegrate) { if (this.internalIntegrate) {
return this.internalIntegrate.call(this, variable, undefined, ...this.inputs); formula = this.internalIntegrate.call(
this,
variable,
undefined,
...this.inputs
);
} else if (this.inputs.length === 1 && this.internalHasVariable) { } else if (this.inputs.length === 1 && this.internalHasVariable) {
return integrateVariable(variable ?? unrefFormulaSource(this.inputs[0])); // eslint-disable-next-line @typescript-eslint/no-this-alias
formula = this;
} else {
throw new Error("Cannot integrate formula without variable");
} }
throw new Error("Cannot integrate formula without variable");
} }
} else { } else {
// "Inner" part of the formula // "Inner" part of the formula
if (this.applySubstitution == null) { if (this.applySubstitution == null) {
throw new Error("Cannot have two complex operations in an integrable formula"); throw new Error("Cannot have two complex operations in an integrable formula");
} }
stack.push((variable: DecimalSource) => stack.push((variable: GenericFormula) =>
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.applySubstitution!.call(this, variable, ...this.inputs) this.applySubstitution!.call(this, variable, ...this.inputs)
); );
if (this.internalIntegrateInner) { if (this.internalIntegrateInner) {
return this.internalIntegrateInner.call(this, variable, stack, ...this.inputs); formula = this.internalIntegrateInner.call(this, variable, stack, ...this.inputs);
} else if (this.internalIntegrate) { } else if (this.internalIntegrate) {
return this.internalIntegrate.call(this, variable, stack, ...this.inputs); formula = this.internalIntegrate.call(this, variable, stack, ...this.inputs);
} else if (this.inputs.length === 1 && this.internalHasVariable) { } else if (this.inputs.length === 1 && this.internalHasVariable) {
return variable ?? unrefFormulaSource(this.inputs[0]); // eslint-disable-next-line @typescript-eslint/no-this-alias
formula = this;
} else {
throw new Error("Cannot integrate formula without variable");
} }
throw new Error("Cannot integrate formula without variable");
} }
} if (!variablePresent) {
this.integralFormula = formula;
calculateConstantOfIntegration() {
// Calculate C based on the knowledge that at 1 purchase, the total sum would be the cost of that one purchase
const integral = this.evaluateIntegral(1);
const actualCost = this.evaluate(0);
return Decimal.sub(actualCost, integral);
}
/**
* Given the potential result of the formula's integral (sand the constant of integration), calculate what value the variable inside the formula would have to be for that result to occur. Only works if there's a single variable and if the formula's integral is invertible.
* @param value The result of the integral.
* @see {@link isIntegralInvertible}
*/
invertIntegral(value: DecimalSource): DecimalSource {
// This is nearly completely non-functional
// Proper nesting will require somehow using integration by substitution or integration by parts
if (this.internalInvertIntegral) {
return this.internalInvertIntegral.call(this, value, ...this.inputs);
} else if (this.inputs.length === 1 && this.internalHasVariable) {
return value;
} }
throw new Error("Cannot invert integral of formula without invertible integral"); return formula;
} }
/** /**
@ -292,7 +324,6 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
this.internalEvaluate === other.internalEvaluate && this.internalEvaluate === other.internalEvaluate &&
this.internalInvert === other.internalInvert && this.internalInvert === other.internalInvert &&
this.internalIntegrate === other.internalIntegrate && this.internalIntegrate === other.internalIntegrate &&
this.internalInvertIntegral === other.internalInvertIntegral &&
this.internalHasVariable === other.internalHasVariable this.internalHasVariable === other.internalHasVariable
); );
} }
@ -415,7 +446,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
return new Formula({ inputs: [value], evaluate: Decimal.abs }); return new Formula({ inputs: [value], evaluate: Decimal.abs });
} }
public static neg<T extends GenericFormula>(value: T): Omit<T, "invertIntegral">; public static neg<T extends GenericFormula>(value: T): T;
public static neg(value: FormulaSource): GenericFormula; public static neg(value: FormulaSource): GenericFormula;
public static neg(value: FormulaSource) { public static neg(value: FormulaSource) {
return new Formula({ return new Formula({
@ -460,8 +491,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
invert: ops.invertAdd, invert: ops.invertAdd,
integrate: ops.integrateAdd, integrate: ops.integrateAdd,
integrateInner: ops.integrateInnerAdd, integrateInner: ops.integrateInnerAdd,
applySubstitution: ops.passthrough, applySubstitution: ops.passthrough
invertIntegral: ops.invertIntegrateAdd
}); });
} }
public static plus = Formula.add; public static plus = Formula.add;
@ -476,8 +506,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
invert: ops.invertSub, invert: ops.invertSub,
integrate: ops.integrateSub, integrate: ops.integrateSub,
integrateInner: ops.integrateInnerSub, integrateInner: ops.integrateInnerSub,
applySubstitution: ops.passthrough, applySubstitution: ops.passthrough
invertIntegral: ops.invertIntegrateSub
}); });
} }
public static subtract = Formula.sub; public static subtract = Formula.sub;
@ -492,8 +521,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
evaluate: Decimal.mul, evaluate: Decimal.mul,
invert: ops.invertMul, invert: ops.invertMul,
integrate: ops.integrateMul, integrate: ops.integrateMul,
applySubstitution: ops.applySubstitutionMul, applySubstitution: ops.applySubstitutionMul
invertIntegral: ops.invertIntegrateMul
}); });
} }
public static multiply = Formula.mul; public static multiply = Formula.mul;
@ -508,8 +536,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
evaluate: Decimal.div, evaluate: Decimal.div,
invert: ops.invertDiv, invert: ops.invertDiv,
integrate: ops.integrateDiv, integrate: ops.integrateDiv,
applySubstitution: ops.applySubstitutionDiv, applySubstitution: ops.applySubstitutionDiv
invertIntegral: ops.invertIntegrateDiv
}); });
} }
public static divide = Formula.div; public static divide = Formula.div;
@ -523,8 +550,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
inputs: [value], inputs: [value],
evaluate: Decimal.recip, evaluate: Decimal.recip,
invert: ops.invertRecip, invert: ops.invertRecip,
integrate: ops.integrateRecip, integrate: ops.integrateRecip
invertIntegral: ops.invertIntegrateRecip
}); });
} }
public static reciprocal = Formula.recip; public static reciprocal = Formula.recip;
@ -537,10 +563,6 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
invert: ops.passthrough as ( invert: ops.passthrough as (
value: DecimalSource, value: DecimalSource,
...inputs: [FormulaSource, FormulaSource] ...inputs: [FormulaSource, FormulaSource]
) => DecimalSource,
invertIntegral: ops.passthrough as (
value: DecimalSource,
...inputs: [FormulaSource, FormulaSource]
) => DecimalSource ) => DecimalSource
}); });
} }
@ -559,12 +581,7 @@ export default class Formula<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< invert: ops.passthrough as InvertFunction<[FormulaSource, FormulaSource, FormulaSource]>
[FormulaSource, FormulaSource, FormulaSource]
>,
invertIntegral: ops.passthrough as InvertFunction<
[FormulaSource, FormulaSource, FormulaSource]
>
}); });
} }
@ -583,8 +600,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
inputs: [value], inputs: [value],
evaluate: Decimal.log10, evaluate: Decimal.log10,
invert: ops.invertLog10, invert: ops.invertLog10,
integrate: ops.integrateLog10, integrate: ops.integrateLog10
invertIntegral: ops.invertIntegrateLog10
}); });
} }
@ -596,8 +612,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
inputs: [value, base], inputs: [value, base],
evaluate: Decimal.log, evaluate: Decimal.log,
invert: ops.invertLog, invert: ops.invertLog,
integrate: ops.integrateLog, integrate: ops.integrateLog
invertIntegral: ops.invertIntegrateLog
}); });
} }
public static logarithm = Formula.log; public static logarithm = Formula.log;
@ -609,8 +624,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
inputs: [value], inputs: [value],
evaluate: Decimal.log2, evaluate: Decimal.log2,
invert: ops.invertLog2, invert: ops.invertLog2,
integrate: ops.integrateLog2, integrate: ops.integrateLog2
invertIntegral: ops.invertIntegrateLog2
}); });
} }
@ -621,8 +635,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
inputs: [value], inputs: [value],
evaluate: Decimal.ln, evaluate: Decimal.ln,
invert: ops.invertLn, invert: ops.invertLn,
integrate: ops.integrateLn, integrate: ops.integrateLn
invertIntegral: ops.invertIntegrateLn
}); });
} }
@ -634,8 +647,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
inputs: [value, other], inputs: [value, other],
evaluate: Decimal.pow, evaluate: Decimal.pow,
invert: ops.invertPow, invert: ops.invertPow,
integrate: ops.integratePow, integrate: ops.integratePow
invertIntegral: ops.invertIntegratePow
}); });
} }
@ -646,8 +658,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
inputs: [value], inputs: [value],
evaluate: Decimal.pow10, evaluate: Decimal.pow10,
invert: ops.invertPow10, invert: ops.invertPow10,
integrate: ops.integratePow10, integrate: ops.integratePow10
invertIntegral: ops.invertIntegratePow10
}); });
} }
@ -659,8 +670,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
inputs: [value, other], inputs: [value, other],
evaluate: Decimal.pow_base, evaluate: Decimal.pow_base,
invert: ops.invertPowBase, invert: ops.invertPowBase,
integrate: ops.integratePowBase, integrate: ops.integratePowBase
invertIntegral: ops.invertIntegratePowBase
}); });
} }
@ -672,8 +682,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
inputs: [value, other], inputs: [value, other],
evaluate: Decimal.root, evaluate: Decimal.root,
invert: ops.invertRoot, invert: ops.invertRoot,
integrate: ops.integrateRoot, integrate: ops.integrateRoot
invertIntegral: ops.invertIntegrateRoot
}); });
} }
@ -689,7 +698,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
return new Formula({ inputs: [value], evaluate: Decimal.lngamma }); return new Formula({ inputs: [value], evaluate: Decimal.lngamma });
} }
public static exp<T extends GenericFormula>(value: T): Omit<T, "invertsIntegral">; public static exp<T extends GenericFormula>(value: T): T;
public static exp(value: FormulaSource): GenericFormula; public static exp(value: FormulaSource): GenericFormula;
public static exp(value: FormulaSource) { public static exp(value: FormulaSource) {
return new Formula({ return new Formula({
@ -728,7 +737,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
value: T, value: T,
height?: FormulaSource, height?: FormulaSource,
payload?: FormulaSource payload?: FormulaSource
): Omit<T, "integrate" | "invertIntegral">; ): Omit<T, "integrate">;
public static tetrate( public static tetrate(
value: FormulaSource, value: FormulaSource,
height?: FormulaSource, height?: FormulaSource,
@ -750,7 +759,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
value: T, value: T,
height?: FormulaSource, height?: FormulaSource,
payload?: FormulaSource payload?: FormulaSource
): Omit<T, "integrate" | "invertIntegral">; ): Omit<T, "integrate">;
public static iteratedexp( public static iteratedexp(
value: FormulaSource, value: FormulaSource,
height?: FormulaSource, height?: FormulaSource,
@ -779,7 +788,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
public static slog<T extends GenericFormula>( public static slog<T extends GenericFormula>(
value: T, value: T,
base?: FormulaSource base?: FormulaSource
): Omit<T, "integrate" | "invertIntegral">; ): Omit<T, "integrate">;
public static slog(value: FormulaSource, base?: FormulaSource): GenericFormula; public static slog(value: FormulaSource, base?: FormulaSource): GenericFormula;
public static slog(value: FormulaSource, base: FormulaSource = 10) { public static slog(value: FormulaSource, base: FormulaSource = 10) {
return new Formula({ inputs: [value, base], evaluate: ops.slog, invert: ops.invertSlog }); return new Formula({ inputs: [value, base], evaluate: ops.slog, invert: ops.invertSlog });
@ -793,7 +802,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
value: T, value: T,
diff: FormulaSource, diff: FormulaSource,
base?: FormulaSource base?: FormulaSource
): Omit<T, "integrate" | "invertIntegral">; ): Omit<T, "integrate">;
public static layeradd( public static layeradd(
value: FormulaSource, value: FormulaSource,
diff: FormulaSource, diff: FormulaSource,
@ -807,9 +816,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
}); });
} }
public static lambertw<T extends GenericFormula>( public static lambertw<T extends GenericFormula>(value: T): Omit<T, "integrate">;
value: T
): Omit<T, "integrate" | "invertIntegral">;
public static lambertw(value: FormulaSource): GenericFormula; public static lambertw(value: FormulaSource): GenericFormula;
public static lambertw(value: FormulaSource) { public static lambertw(value: FormulaSource) {
return new Formula({ return new Formula({
@ -819,9 +826,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
}); });
} }
public static ssqrt<T extends GenericFormula>( public static ssqrt<T extends GenericFormula>(value: T): Omit<T, "integrate">;
value: T
): Omit<T, "integrate" | "invertIntegral">;
public static ssqrt(value: FormulaSource): GenericFormula; public static ssqrt(value: FormulaSource): GenericFormula;
public static ssqrt(value: FormulaSource) { public static ssqrt(value: FormulaSource) {
return new Formula({ inputs: [value], evaluate: Decimal.ssqrt, invert: ops.invertSsqrt }); return new Formula({ inputs: [value], evaluate: Decimal.ssqrt, invert: ops.invertSsqrt });
@ -835,7 +840,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
return new Formula({ inputs: [value, height, payload], evaluate: ops.pentate }); return new Formula({ inputs: [value, height, payload], evaluate: ops.pentate });
} }
public static sin<T extends GenericFormula>(value: T): Omit<T, "invertIntegral">; public static sin<T extends GenericFormula>(value: T): T;
public static sin(value: FormulaSource): GenericFormula; public static sin(value: FormulaSource): GenericFormula;
public static sin(value: FormulaSource) { public static sin(value: FormulaSource) {
return new Formula({ return new Formula({
@ -846,7 +851,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
}); });
} }
public static cos<T extends GenericFormula>(value: T): Omit<T, "invertIntegral">; public static cos<T extends GenericFormula>(value: T): T;
public static cos(value: FormulaSource): GenericFormula; public static cos(value: FormulaSource): GenericFormula;
public static cos(value: FormulaSource) { public static cos(value: FormulaSource) {
return new Formula({ return new Formula({
@ -857,7 +862,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
}); });
} }
public static tan<T extends GenericFormula>(value: T): Omit<T, "invertIntegral">; public static tan<T extends GenericFormula>(value: T): T;
public static tan(value: FormulaSource): GenericFormula; public static tan(value: FormulaSource): GenericFormula;
public static tan(value: FormulaSource) { public static tan(value: FormulaSource) {
return new Formula({ return new Formula({
@ -868,7 +873,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
}); });
} }
public static asin<T extends GenericFormula>(value: T): Omit<T, "invertIntegral">; public static asin<T extends GenericFormula>(value: T): T;
public static asin(value: FormulaSource): GenericFormula; public static asin(value: FormulaSource): GenericFormula;
public static asin(value: FormulaSource) { public static asin(value: FormulaSource) {
return new Formula({ return new Formula({
@ -879,7 +884,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
}); });
} }
public static acos<T extends GenericFormula>(value: T): Omit<T, "invertIntegral">; public static acos<T extends GenericFormula>(value: T): T;
public static acos(value: FormulaSource): GenericFormula; public static acos(value: FormulaSource): GenericFormula;
public static acos(value: FormulaSource) { public static acos(value: FormulaSource) {
return new Formula({ return new Formula({
@ -890,7 +895,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
}); });
} }
public static atan<T extends GenericFormula>(value: T): Omit<T, "invertIntegral">; public static atan<T extends GenericFormula>(value: T): T;
public static atan(value: FormulaSource): GenericFormula; public static atan(value: FormulaSource): GenericFormula;
public static atan(value: FormulaSource) { public static atan(value: FormulaSource) {
return new Formula({ return new Formula({
@ -901,7 +906,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
}); });
} }
public static sinh<T extends GenericFormula>(value: T): Omit<T, "invertIntegral">; public static sinh<T extends GenericFormula>(value: T): T;
public static sinh(value: FormulaSource): GenericFormula; public static sinh(value: FormulaSource): GenericFormula;
public static sinh(value: FormulaSource) { public static sinh(value: FormulaSource) {
return new Formula({ return new Formula({
@ -912,7 +917,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
}); });
} }
public static cosh<T extends GenericFormula>(value: T): Omit<T, "invertIntegral">; public static cosh<T extends GenericFormula>(value: T): T;
public static cosh(value: FormulaSource): GenericFormula; public static cosh(value: FormulaSource): GenericFormula;
public static cosh(value: FormulaSource) { public static cosh(value: FormulaSource) {
return new Formula({ return new Formula({
@ -923,7 +928,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
}); });
} }
public static tanh<T extends GenericFormula>(value: T): Omit<T, "invertIntegral">; public static tanh<T extends GenericFormula>(value: T): T;
public static tanh(value: FormulaSource): GenericFormula; public static tanh(value: FormulaSource): GenericFormula;
public static tanh(value: FormulaSource) { public static tanh(value: FormulaSource) {
return new Formula({ return new Formula({
@ -934,7 +939,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
}); });
} }
public static asinh<T extends GenericFormula>(value: T): Omit<T, "invertIntegral">; public static asinh<T extends GenericFormula>(value: T): T;
public static asinh(value: FormulaSource): GenericFormula; public static asinh(value: FormulaSource): GenericFormula;
public static asinh(value: FormulaSource) { public static asinh(value: FormulaSource) {
return new Formula({ return new Formula({
@ -945,7 +950,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
}); });
} }
public static acosh<T extends GenericFormula>(value: T): Omit<T, "invertIntegral">; public static acosh<T extends GenericFormula>(value: T): T;
public static acosh(value: FormulaSource): GenericFormula; public static acosh(value: FormulaSource): GenericFormula;
public static acosh(value: FormulaSource) { public static acosh(value: FormulaSource) {
return new Formula({ return new Formula({
@ -956,7 +961,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
}); });
} }
public static atanh<T extends GenericFormula>(value: T): Omit<T, "invertIntegral">; public static atanh<T extends GenericFormula>(value: T): T;
public static atanh(value: FormulaSource): GenericFormula; public static atanh(value: FormulaSource): GenericFormula;
public static atanh(value: FormulaSource) { public static atanh(value: FormulaSource) {
return new Formula({ return new Formula({
@ -997,7 +1002,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
return Formula.abs(this); return Formula.abs(this);
} }
public neg<T extends GenericFormula>(this: T): Omit<T, "invertIntegral">; public neg<T extends GenericFormula>(this: T): T;
public neg(this: GenericFormula): GenericFormula; public neg(this: GenericFormula): GenericFormula;
public neg(this: GenericFormula) { public neg(this: GenericFormula) {
return Formula.neg(this); return Formula.neg(this);
@ -1170,7 +1175,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
return Formula.lngamma(this); return Formula.lngamma(this);
} }
public exp<T extends GenericFormula>(this: T): Omit<T, "invertsIntegral">; public exp<T extends GenericFormula>(this: T): T;
public exp(this: FormulaSource): GenericFormula; public exp(this: FormulaSource): GenericFormula;
public exp(this: FormulaSource) { public exp(this: FormulaSource) {
return Formula.exp(this); return Formula.exp(this);
@ -1203,7 +1208,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
this: T, this: T,
height?: FormulaSource, height?: FormulaSource,
payload?: FormulaSource payload?: FormulaSource
): Omit<T, "integrate" | "invertIntegral">; ): Omit<T, "integrate">;
public tetrate( public tetrate(
this: FormulaSource, this: FormulaSource,
height?: FormulaSource, height?: FormulaSource,
@ -1221,7 +1226,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
this: T, this: T,
height?: FormulaSource, height?: FormulaSource,
payload?: FormulaSource payload?: FormulaSource
): Omit<T, "integrate" | "invertIntegral">; ): Omit<T, "integrate">;
public iteratedexp( public iteratedexp(
this: FormulaSource, this: FormulaSource,
height?: FormulaSource, height?: FormulaSource,
@ -1239,10 +1244,7 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
return Formula.iteratedlog(this, base, times); return Formula.iteratedlog(this, base, times);
} }
public slog<T extends GenericFormula>( public slog<T extends GenericFormula>(this: T, base?: FormulaSource): Omit<T, "integrate">;
this: T,
base?: FormulaSource
): Omit<T, "integrate" | "invertIntegral">;
public slog(this: FormulaSource, base?: FormulaSource): GenericFormula; public slog(this: FormulaSource, base?: FormulaSource): GenericFormula;
public slog(this: FormulaSource, base: FormulaSource = 10) { public slog(this: FormulaSource, base: FormulaSource = 10) {
return Formula.slog(this, base); return Formula.slog(this, base);
@ -1256,19 +1258,19 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
this: T, this: T,
diff: FormulaSource, diff: FormulaSource,
base?: FormulaSource base?: FormulaSource
): Omit<T, "integrate" | "invertIntegral">; ): Omit<T, "integrate">;
public layeradd(this: FormulaSource, diff: FormulaSource, base?: FormulaSource): GenericFormula; public layeradd(this: FormulaSource, diff: FormulaSource, base?: FormulaSource): GenericFormula;
public layeradd(this: FormulaSource, diff: FormulaSource, base: FormulaSource) { public layeradd(this: FormulaSource, diff: FormulaSource, base: FormulaSource) {
return Formula.layeradd(this, diff, base); return Formula.layeradd(this, diff, base);
} }
public lambertw<T extends GenericFormula>(this: T): Omit<T, "integrate" | "invertIntegral">; public lambertw<T extends GenericFormula>(this: T): Omit<T, "integrate">;
public lambertw(this: FormulaSource): GenericFormula; public lambertw(this: FormulaSource): GenericFormula;
public lambertw(this: FormulaSource) { public lambertw(this: FormulaSource) {
return Formula.lambertw(this); return Formula.lambertw(this);
} }
public ssqrt<T extends GenericFormula>(this: T): Omit<T, "integrate" | "invertIntegral">; public ssqrt<T extends GenericFormula>(this: T): Omit<T, "integrate">;
public ssqrt(this: FormulaSource): GenericFormula; public ssqrt(this: FormulaSource): GenericFormula;
public ssqrt(this: FormulaSource) { public ssqrt(this: FormulaSource) {
return Formula.ssqrt(this); return Formula.ssqrt(this);
@ -1281,73 +1283,73 @@ export default class Formula<T extends [FormulaSource] | FormulaSource[]> {
return Formula.pentate(this, height, payload); return Formula.pentate(this, height, payload);
} }
public sin<T extends GenericFormula>(this: T): Omit<T, "invertIntegral">; public sin<T extends GenericFormula>(this: T): T;
public sin(this: FormulaSource): GenericFormula; public sin(this: FormulaSource): GenericFormula;
public sin(this: FormulaSource) { public sin(this: FormulaSource) {
return Formula.sin(this); return Formula.sin(this);
} }
public cos<T extends GenericFormula>(this: T): Omit<T, "invertIntegral">; public cos<T extends GenericFormula>(this: T): T;
public cos(this: FormulaSource): GenericFormula; public cos(this: FormulaSource): GenericFormula;
public cos(this: FormulaSource) { public cos(this: FormulaSource) {
return Formula.cos(this); return Formula.cos(this);
} }
public tan<T extends GenericFormula>(this: T): Omit<T, "invertIntegral">; public tan<T extends GenericFormula>(this: T): T;
public tan(this: FormulaSource): GenericFormula; public tan(this: FormulaSource): GenericFormula;
public tan(this: FormulaSource) { public tan(this: FormulaSource) {
return Formula.tan(this); return Formula.tan(this);
} }
public asin<T extends GenericFormula>(this: T): Omit<T, "invertIntegral">; public asin<T extends GenericFormula>(this: T): T;
public asin(this: FormulaSource): GenericFormula; public asin(this: FormulaSource): GenericFormula;
public asin(this: FormulaSource) { public asin(this: FormulaSource) {
return Formula.asin(this); return Formula.asin(this);
} }
public acos<T extends GenericFormula>(this: T): Omit<T, "invertIntegral">; public acos<T extends GenericFormula>(this: T): T;
public acos(this: FormulaSource): GenericFormula; public acos(this: FormulaSource): GenericFormula;
public acos(this: FormulaSource) { public acos(this: FormulaSource) {
return Formula.acos(this); return Formula.acos(this);
} }
public atan<T extends GenericFormula>(this: T): Omit<T, "invertIntegral">; public atan<T extends GenericFormula>(this: T): T;
public atan(this: FormulaSource): GenericFormula; public atan(this: FormulaSource): GenericFormula;
public atan(this: FormulaSource) { public atan(this: FormulaSource) {
return Formula.atan(this); return Formula.atan(this);
} }
public sinh<T extends GenericFormula>(this: T): Omit<T, "invertIntegral">; public sinh<T extends GenericFormula>(this: T): T;
public sinh(this: FormulaSource): GenericFormula; public sinh(this: FormulaSource): GenericFormula;
public sinh(this: FormulaSource) { public sinh(this: FormulaSource) {
return Formula.sinh(this); return Formula.sinh(this);
} }
public cosh<T extends GenericFormula>(this: T): Omit<T, "invertIntegral">; public cosh<T extends GenericFormula>(this: T): T;
public cosh(this: FormulaSource): GenericFormula; public cosh(this: FormulaSource): GenericFormula;
public cosh(this: FormulaSource) { public cosh(this: FormulaSource) {
return Formula.cosh(this); return Formula.cosh(this);
} }
public tanh<T extends GenericFormula>(this: T): Omit<T, "invertIntegral">; public tanh<T extends GenericFormula>(this: T): T;
public tanh(this: FormulaSource): GenericFormula; public tanh(this: FormulaSource): GenericFormula;
public tanh(this: FormulaSource) { public tanh(this: FormulaSource) {
return Formula.tanh(this); return Formula.tanh(this);
} }
public asinh<T extends GenericFormula>(this: T): Omit<T, "invertIntegral">; public asinh<T extends GenericFormula>(this: T): T;
public asinh(this: FormulaSource): GenericFormula; public asinh(this: FormulaSource): GenericFormula;
public asinh(this: FormulaSource) { public asinh(this: FormulaSource) {
return Formula.asinh(this); return Formula.asinh(this);
} }
public acosh<T extends GenericFormula>(this: T): Omit<T, "invertIntegral">; public acosh<T extends GenericFormula>(this: T): T;
public acosh(this: FormulaSource): GenericFormula; public acosh(this: FormulaSource): GenericFormula;
public acosh(this: FormulaSource) { public acosh(this: FormulaSource) {
return Formula.acosh(this); return Formula.acosh(this);
} }
public atanh<T extends GenericFormula>(this: T): Omit<T, "invertIntegral">; public atanh<T extends GenericFormula>(this: T): T;
public atanh(this: FormulaSource): GenericFormula; public atanh(this: FormulaSource): GenericFormula;
public atanh(this: FormulaSource) { public atanh(this: FormulaSource) {
return Formula.atanh(this); return Formula.atanh(this);

View file

@ -1,9 +1,9 @@
import Decimal, { DecimalSource } from "util/bignum"; import Decimal, { DecimalSource } from "util/bignum";
import { unref } from "vue"; import { Ref } from "vue";
import Formula, { hasVariable, unrefFormulaSource } from "./formulas"; import Formula, { hasVariable, unrefFormulaSource } from "./formulas";
import { FormulaSource, InvertFunction, SubstitutionStack } from "./types"; import { FormulaSource, GenericFormula, InvertFunction, SubstitutionStack } from "./types";
export function passthrough(value: DecimalSource) { export function passthrough<T extends GenericFormula | DecimalSource>(value: T): T {
return value; return value;
} }
@ -15,18 +15,18 @@ export function invertNeg(value: DecimalSource, lhs: FormulaSource) {
} }
export function integrateNeg( export function integrateNeg(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource lhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
return Decimal.neg(lhs.evaluateIntegral(variable, stack)); return Formula.neg(lhs.getIntegralFormula(variable, stack));
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
export function applySubstitutionNeg(value: DecimalSource) { export function applySubstitutionNeg(value: GenericFormula) {
return Decimal.neg(value); return Formula.neg(value);
} }
export function invertAdd(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) { export function invertAdd(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) {
@ -39,54 +39,37 @@ export function invertAdd(value: DecimalSource, lhs: FormulaSource, rhs: Formula
} }
export function integrateAdd( export function integrateAdd(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource, lhs: FormulaSource,
rhs: FormulaSource rhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
return Decimal.times( return Formula.times(rhs, variable ?? lhs.innermostVariable ?? 0).add(x);
unrefFormulaSource(rhs),
variable ?? unref(lhs.innermostVariable) ?? 0
).add(x);
} else if (hasVariable(rhs)) { } else if (hasVariable(rhs)) {
const x = rhs.evaluateIntegral(variable, stack); const x = rhs.getIntegralFormula(variable, stack);
return Decimal.times( return Formula.times(lhs, variable ?? rhs.innermostVariable ?? 0).add(x);
unrefFormulaSource(lhs),
variable ?? unref(rhs.innermostVariable) ?? 0
).add(x);
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
export function integrateInnerAdd( export function integrateInnerAdd(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource, lhs: FormulaSource,
rhs: FormulaSource rhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
return Decimal.add(x, unrefFormulaSource(rhs)); return Formula.add(x, rhs);
} else if (hasVariable(rhs)) { } else if (hasVariable(rhs)) {
const x = rhs.evaluateIntegral(variable, stack); const x = rhs.getIntegralFormula(variable, stack);
return Decimal.add(x, unrefFormulaSource(lhs)); return Formula.add(x, lhs);
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
export function invertIntegrateAdd(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) {
if (hasVariable(lhs)) {
const b = unrefFormulaSource(rhs);
return lhs.invert(Decimal.pow(b, 2).add(Decimal.times(value, 2)).sub(b));
} else if (hasVariable(rhs)) {
const b = unrefFormulaSource(lhs);
return rhs.invert(Decimal.pow(b, 2).add(Decimal.times(value, 2)).sub(b));
}
throw new Error("Could not invert due to no input being a variable");
}
export function invertSub(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) { export function invertSub(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
return lhs.invert(Decimal.add(value, unrefFormulaSource(rhs))); return lhs.invert(Decimal.add(value, unrefFormulaSource(rhs)));
@ -97,54 +80,37 @@ export function invertSub(value: DecimalSource, lhs: FormulaSource, rhs: Formula
} }
export function integrateSub( export function integrateSub(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource, lhs: FormulaSource,
rhs: FormulaSource rhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
return Decimal.sub( return Formula.sub(x, Formula.times(rhs, variable ?? lhs.innermostVariable ?? 0));
x,
Decimal.times(unrefFormulaSource(rhs), variable ?? unref(lhs.innermostVariable) ?? 0)
);
} else if (hasVariable(rhs)) { } else if (hasVariable(rhs)) {
const x = rhs.evaluateIntegral(variable, stack); const x = rhs.getIntegralFormula(variable, stack);
return Decimal.times( return Formula.times(lhs, variable ?? rhs.innermostVariable ?? 0).sub(x);
unrefFormulaSource(lhs),
variable ?? unref(rhs.innermostVariable) ?? 0
).sub(x);
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
export function integrateInnerSub( export function integrateInnerSub(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource, lhs: FormulaSource,
rhs: FormulaSource rhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
return Decimal.sub(x, unrefFormulaSource(rhs)); return Formula.sub(x, rhs);
} else if (hasVariable(rhs)) { } else if (hasVariable(rhs)) {
const x = rhs.evaluateIntegral(variable, stack); const x = rhs.getIntegralFormula(variable, stack);
return Decimal.sub(x, unrefFormulaSource(lhs)); return Formula.sub(x, lhs);
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
export function invertIntegrateSub(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) {
if (hasVariable(lhs)) {
const b = unrefFormulaSource(rhs);
return lhs.invert(Decimal.pow(b, 2).add(Decimal.times(value, 2)).sqrt().sub(b));
} else if (hasVariable(rhs)) {
const b = unrefFormulaSource(lhs);
return rhs.invert(Decimal.pow(b, 2).add(Decimal.times(value, 2)).sqrt().sub(b));
}
throw new Error("Could not invert due to no input being a variable");
}
export function invertMul(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) { export function invertMul(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
return lhs.invert(Decimal.div(value, unrefFormulaSource(rhs))); return lhs.invert(Decimal.div(value, unrefFormulaSource(rhs)));
@ -155,41 +121,34 @@ export function invertMul(value: DecimalSource, lhs: FormulaSource, rhs: Formula
} }
export function integrateMul( export function integrateMul(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource, lhs: FormulaSource,
rhs: FormulaSource rhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
return Decimal.times(x, unrefFormulaSource(rhs)); return Formula.times(x, rhs);
} else if (hasVariable(rhs)) { } else if (hasVariable(rhs)) {
const x = rhs.evaluateIntegral(variable, stack); const x = rhs.getIntegralFormula(variable, stack);
return Decimal.times(x, unrefFormulaSource(lhs)); return Formula.times(x, lhs);
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
export function applySubstitutionMul(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) { export function applySubstitutionMul(
value: GenericFormula,
lhs: FormulaSource,
rhs: FormulaSource
) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
return Decimal.div(value, unrefFormulaSource(rhs)); return Formula.div(value, rhs);
} else if (hasVariable(rhs)) { } else if (hasVariable(rhs)) {
return Decimal.div(value, unrefFormulaSource(lhs)); return Formula.div(value, lhs);
} }
throw new Error("Could not apply substitution due to no input being a variable"); throw new Error("Could not apply substitution due to no input being a variable");
} }
export function invertIntegrateMul(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) {
if (hasVariable(lhs)) {
const b = unrefFormulaSource(rhs);
return lhs.invert(Decimal.sqrt(value).times(Decimal.sqrt(2)).div(Decimal.sqrt(b)));
} else if (hasVariable(rhs)) {
const b = unrefFormulaSource(lhs);
return rhs.invert(Decimal.sqrt(value).times(Decimal.sqrt(2)).div(Decimal.sqrt(b)));
}
throw new Error("Could not invert due to no input being a variable");
}
export function invertDiv(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) { export function invertDiv(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
return lhs.invert(Decimal.mul(value, unrefFormulaSource(rhs))); return lhs.invert(Decimal.mul(value, unrefFormulaSource(rhs)));
@ -200,41 +159,34 @@ export function invertDiv(value: DecimalSource, lhs: FormulaSource, rhs: Formula
} }
export function integrateDiv( export function integrateDiv(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource, lhs: FormulaSource,
rhs: FormulaSource rhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
return Decimal.div(x, unrefFormulaSource(rhs)); return Formula.div(x, rhs);
} else if (hasVariable(rhs)) { } else if (hasVariable(rhs)) {
const x = rhs.evaluateIntegral(variable, stack); const x = rhs.getIntegralFormula(variable, stack);
return Decimal.div(unrefFormulaSource(lhs), x); return Formula.div(lhs, x);
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
export function applySubstitutionDiv(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) { export function applySubstitutionDiv(
value: GenericFormula,
lhs: FormulaSource,
rhs: FormulaSource
) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
return Decimal.mul(value, unrefFormulaSource(rhs)); return Formula.mul(value, rhs);
} else if (hasVariable(rhs)) { } else if (hasVariable(rhs)) {
return Decimal.mul(value, unrefFormulaSource(lhs)); return Formula.mul(value, lhs);
} }
throw new Error("Could not apply substitution due to no input being a variable"); throw new Error("Could not apply substitution due to no input being a variable");
} }
export function invertIntegrateDiv(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) {
if (hasVariable(lhs)) {
const b = unrefFormulaSource(rhs);
return lhs.invert(Decimal.sqrt(value).times(Decimal.sqrt(2)).times(Decimal.sqrt(b)));
} else if (hasVariable(rhs)) {
const b = unrefFormulaSource(lhs);
return rhs.invert(Decimal.sqrt(value).times(Decimal.sqrt(2)).times(Decimal.sqrt(b)));
}
throw new Error("Could not invert due to no input being a variable");
}
export function invertRecip(value: DecimalSource, lhs: FormulaSource) { export function invertRecip(value: DecimalSource, lhs: FormulaSource) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
return lhs.invert(Decimal.recip(value)); return lhs.invert(Decimal.recip(value));
@ -243,24 +195,17 @@ export function invertRecip(value: DecimalSource, lhs: FormulaSource) {
} }
export function integrateRecip( export function integrateRecip(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource lhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
return Decimal.ln(x); return Formula.ln(x);
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
export function invertIntegrateRecip(value: DecimalSource, lhs: FormulaSource) {
if (hasVariable(lhs)) {
return lhs.invert(Decimal.exp(value));
}
throw new Error("Could not invert due to no input being a variable");
}
export function invertLog10(value: DecimalSource, lhs: FormulaSource) { export function invertLog10(value: DecimalSource, lhs: FormulaSource) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
return lhs.invert(Decimal.pow10(value)); return lhs.invert(Decimal.pow10(value));
@ -269,26 +214,17 @@ export function invertLog10(value: DecimalSource, lhs: FormulaSource) {
} }
export function integrateLog10( export function integrateLog10(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource lhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
return Decimal.ln(x).sub(1).times(x).div(Decimal.ln(10)); return Formula.ln(x).sub(1).times(x).div(Formula.ln(10));
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
export function invertIntegrateLog10(value: DecimalSource, lhs: FormulaSource) {
if (hasVariable(lhs)) {
return lhs.invert(
Decimal.exp(Decimal.ln(2).add(Decimal.ln(5)).times(value).div(Math.E).lambertw().add(1))
);
}
throw new Error("Could not invert due to no input being a variable");
}
export function invertLog(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) { export function invertLog(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
return lhs.invert(Decimal.pow(unrefFormulaSource(rhs), value)); return lhs.invert(Decimal.pow(unrefFormulaSource(rhs), value));
@ -299,29 +235,18 @@ export function invertLog(value: DecimalSource, lhs: FormulaSource, rhs: Formula
} }
export function integrateLog( export function integrateLog(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource, lhs: FormulaSource,
rhs: FormulaSource rhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
return Decimal.ln(x) return Formula.ln(x).sub(1).times(x).div(Formula.ln(rhs));
.sub(1)
.times(x)
.div(Decimal.ln(unrefFormulaSource(rhs)));
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
export function invertIntegrateLog(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) {
if (hasVariable(lhs)) {
const numerator = Decimal.ln(unrefFormulaSource(rhs)).times(value);
return lhs.invert(numerator.div(numerator.div(Math.E).lambertw()));
}
throw new Error("Could not invert due to no input being a variable");
}
export function invertLog2(value: DecimalSource, lhs: FormulaSource) { export function invertLog2(value: DecimalSource, lhs: FormulaSource) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
return lhs.invert(Decimal.pow(2, value)); return lhs.invert(Decimal.pow(2, value));
@ -330,24 +255,17 @@ export function invertLog2(value: DecimalSource, lhs: FormulaSource) {
} }
export function integrateLog2( export function integrateLog2(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource lhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
return Decimal.ln(x).sub(1).times(x).div(Decimal.ln(2)); return Formula.ln(x).sub(1).times(x).div(Formula.ln(2));
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
export function invertIntegrateLog2(value: DecimalSource, lhs: FormulaSource) {
if (hasVariable(lhs)) {
return lhs.invert(Decimal.exp(Decimal.ln(2).times(value).div(Math.E).lambertw().add(1)));
}
throw new Error("Could not invert due to no input being a variable");
}
export function invertLn(value: DecimalSource, lhs: FormulaSource) { export function invertLn(value: DecimalSource, lhs: FormulaSource) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
return lhs.invert(Decimal.exp(value)); return lhs.invert(Decimal.exp(value));
@ -356,24 +274,17 @@ export function invertLn(value: DecimalSource, lhs: FormulaSource) {
} }
export function integrateLn( export function integrateLn(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource lhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
return Decimal.ln(x).sub(1).times(x); return Formula.ln(x).sub(1).times(x);
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
export function invertIntegrateLn(value: DecimalSource, lhs: FormulaSource) {
if (hasVariable(lhs)) {
return lhs.invert(Decimal.exp(Decimal.div(value, Math.E).lambertw().add(1)));
}
throw new Error("Could not invert due to no input being a variable");
}
export function invertPow(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) { export function invertPow(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
return lhs.invert(Decimal.root(value, unrefFormulaSource(rhs))); return lhs.invert(Decimal.root(value, unrefFormulaSource(rhs)));
@ -384,34 +295,22 @@ export function invertPow(value: DecimalSource, lhs: FormulaSource, rhs: Formula
} }
export function integratePow( export function integratePow(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource, lhs: FormulaSource,
rhs: FormulaSource rhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
const pow = Decimal.add(unrefFormulaSource(rhs), 1); const pow = Formula.add(rhs, 1);
return Decimal.pow(x, pow).div(pow); return Formula.pow(x, pow).div(pow);
} else if (hasVariable(rhs)) { } else if (hasVariable(rhs)) {
const x = rhs.evaluateIntegral(variable, stack); const x = rhs.getIntegralFormula(variable, stack);
const b = unrefFormulaSource(lhs); return Formula.pow(lhs, x).div(Formula.ln(lhs));
return Decimal.pow(b, x).div(Decimal.ln(b));
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
export function invertIntegratePow(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) {
if (hasVariable(lhs)) {
const b = unrefFormulaSource(rhs);
return lhs.invert(Decimal.negate(b).sub(1).negate().times(value).root(Decimal.add(b, 1)));
} else if (hasVariable(rhs)) {
const denominator = Decimal.ln(unrefFormulaSource(lhs));
return rhs.invert(Decimal.times(denominator, value).ln().div(denominator));
}
throw new Error("Could not invert due to no input being a variable");
}
export function invertPow10(value: DecimalSource, lhs: FormulaSource) { export function invertPow10(value: DecimalSource, lhs: FormulaSource) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
return lhs.invert(Decimal.root(value, 10)); return lhs.invert(Decimal.root(value, 10));
@ -420,26 +319,17 @@ export function invertPow10(value: DecimalSource, lhs: FormulaSource) {
} }
export function integratePow10( export function integratePow10(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource lhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
return Decimal.ln(x).sub(1).times(x).div(Decimal.ln(10)); return Formula.ln(x).sub(1).times(x).div(Decimal.ln(10));
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
export function invertIntegratePow10(value: DecimalSource, lhs: FormulaSource) {
if (hasVariable(lhs)) {
return lhs.invert(
Decimal.ln(2).add(Decimal.ln(5)).times(value).div(Math.E).lambertw().add(1).exp()
);
}
throw new Error("Could not invert due to no input being a variable");
}
export function invertPowBase(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) { export function invertPowBase(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
return lhs.invert(Decimal.ln(value).div(unrefFormulaSource(rhs))); return lhs.invert(Decimal.ln(value).div(unrefFormulaSource(rhs)));
@ -450,38 +340,22 @@ export function invertPowBase(value: DecimalSource, lhs: FormulaSource, rhs: For
} }
export function integratePowBase( export function integratePowBase(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource, lhs: FormulaSource,
rhs: FormulaSource rhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
const b = unrefFormulaSource(rhs); return Formula.pow(rhs, x).div(Formula.ln(rhs));
return Decimal.pow(b, x).div(Decimal.ln(b));
} else if (hasVariable(rhs)) { } else if (hasVariable(rhs)) {
const x = rhs.evaluateIntegral(variable, stack); const x = rhs.getIntegralFormula(variable, stack);
const denominator = Decimal.add(unrefFormulaSource(lhs), 1); const denominator = Formula.add(lhs, 1);
return Decimal.pow(x, denominator).div(denominator); return Formula.pow(x, denominator).div(denominator);
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
export function invertIntegratePowBase(
value: DecimalSource,
lhs: FormulaSource,
rhs: FormulaSource
) {
if (hasVariable(lhs)) {
const b = unrefFormulaSource(rhs);
return lhs.invert(Decimal.ln(b).times(value).ln().div(Decimal.ln(b)));
} else if (hasVariable(rhs)) {
const b = unrefFormulaSource(lhs);
return rhs.invert(Decimal.neg(b).sub(1).negate().times(value).root(Decimal.add(b, 1)));
}
throw new Error("Could not invert due to no input being a variable");
}
export function invertRoot(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) { export function invertRoot(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
return lhs.invert(Decimal.root(value, Decimal.recip(unrefFormulaSource(rhs)))); return lhs.invert(Decimal.root(value, Decimal.recip(unrefFormulaSource(rhs))));
@ -492,32 +366,18 @@ export function invertRoot(value: DecimalSource, lhs: FormulaSource, rhs: Formul
} }
export function integrateRoot( export function integrateRoot(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource, lhs: FormulaSource,
rhs: FormulaSource rhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
const a = unrefFormulaSource(rhs); return Formula.pow(x, Formula.recip(rhs).add(1)).times(rhs).div(Formula.add(rhs, 1));
return Decimal.pow(x, Decimal.recip(a).add(1)).times(a).div(Decimal.add(a, 1));
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
export function invertIntegrateRoot(value: DecimalSource, lhs: FormulaSource, rhs: FormulaSource) {
if (hasVariable(lhs)) {
const b = unrefFormulaSource(rhs);
return lhs.invert(
Decimal.add(b, 1)
.times(value)
.div(b)
.pow(Decimal.div(b, Decimal.add(b, 1)))
);
}
throw new Error("Could not invert due to no input being a variable");
}
export function invertExp(value: DecimalSource, lhs: FormulaSource) { export function invertExp(value: DecimalSource, lhs: FormulaSource) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
return lhs.invert(Decimal.ln(value)); return lhs.invert(Decimal.ln(value));
@ -526,13 +386,13 @@ export function invertExp(value: DecimalSource, lhs: FormulaSource) {
} }
export function integrateExp( export function integrateExp(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource lhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
return Decimal.exp(x); return Formula.exp(x);
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
@ -661,13 +521,13 @@ export function invertSin(value: DecimalSource, lhs: FormulaSource) {
} }
export function integrateSin( export function integrateSin(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource lhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
return Decimal.cos(x).neg(); return Formula.cos(x).neg();
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
@ -680,13 +540,13 @@ export function invertCos(value: DecimalSource, lhs: FormulaSource) {
} }
export function integrateCos( export function integrateCos(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource lhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
return Decimal.sin(x); return Formula.sin(x);
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
@ -699,13 +559,13 @@ export function invertTan(value: DecimalSource, lhs: FormulaSource) {
} }
export function integrateTan( export function integrateTan(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource lhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
return Decimal.cos(x).ln().neg(); return Formula.cos(x).ln().neg();
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
@ -718,15 +578,15 @@ export function invertAsin(value: DecimalSource, lhs: FormulaSource) {
} }
export function integrateAsin( export function integrateAsin(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource lhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
return Decimal.asin(x) return Formula.asin(x)
.times(x) .times(x)
.add(Decimal.sqrt(Decimal.sub(1, Decimal.pow(x, 2)))); .add(Formula.sqrt(Formula.sub(1, Formula.pow(x, 2))));
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
@ -739,15 +599,15 @@ export function invertAcos(value: DecimalSource, lhs: FormulaSource) {
} }
export function integrateAcos( export function integrateAcos(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource lhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
return Decimal.acos(x) return Formula.acos(x)
.times(x) .times(x)
.sub(Decimal.sqrt(Decimal.sub(1, Decimal.pow(x, 2)))); .sub(Formula.sqrt(Formula.sub(1, Formula.pow(x, 2))));
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
@ -760,15 +620,15 @@ export function invertAtan(value: DecimalSource, lhs: FormulaSource) {
} }
export function integrateAtan( export function integrateAtan(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource lhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
return Decimal.atan(x) return Formula.atan(x)
.times(x) .times(x)
.sub(Decimal.ln(Decimal.pow(x, 2).add(1)).div(2)); .sub(Formula.ln(Formula.pow(x, 2).add(1)).div(2));
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
@ -781,13 +641,13 @@ export function invertSinh(value: DecimalSource, lhs: FormulaSource) {
} }
export function integrateSinh( export function integrateSinh(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource lhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
return Decimal.cosh(x); return Formula.cosh(x);
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
@ -800,13 +660,13 @@ export function invertCosh(value: DecimalSource, lhs: FormulaSource) {
} }
export function integrateCosh( export function integrateCosh(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource lhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
return Decimal.sinh(x); return Formula.sinh(x);
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
@ -819,13 +679,13 @@ export function invertTanh(value: DecimalSource, lhs: FormulaSource) {
} }
export function integrateTanh( export function integrateTanh(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource lhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
return Decimal.cosh(x).ln(); return Formula.cosh(x).ln();
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
@ -838,13 +698,13 @@ export function invertAsinh(value: DecimalSource, lhs: FormulaSource) {
} }
export function integrateAsinh( export function integrateAsinh(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource lhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
return Decimal.asinh(x).times(x).sub(Decimal.pow(x, 2).add(1).sqrt()); return Formula.asinh(x).times(x).sub(Formula.pow(x, 2).add(1).sqrt());
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
@ -857,15 +717,15 @@ export function invertAcosh(value: DecimalSource, lhs: FormulaSource) {
} }
export function integrateAcosh( export function integrateAcosh(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource lhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
return Decimal.acosh(x) return Formula.acosh(x)
.times(x) .times(x)
.sub(Decimal.add(x, 1).sqrt().times(Decimal.sub(x, 1).sqrt())); .sub(Formula.add(x, 1).sqrt().times(Formula.sub(x, 1).sqrt()));
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
@ -878,15 +738,15 @@ export function invertAtanh(value: DecimalSource, lhs: FormulaSource) {
} }
export function integrateAtanh( export function integrateAtanh(
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack, stack: SubstitutionStack,
lhs: FormulaSource lhs: FormulaSource
) { ) {
if (hasVariable(lhs)) { if (hasVariable(lhs)) {
const x = lhs.evaluateIntegral(variable, stack); const x = lhs.getIntegralFormula(variable, stack);
return Decimal.atanh(x) return Formula.atanh(x)
.times(x) .times(x)
.add(Decimal.sub(1, Decimal.pow(x, 2)).ln().div(2)); .add(Formula.sub(1, Formula.pow(x, 2)).ln().div(2));
} }
throw new Error("Could not integrate due to no input being a variable"); throw new Error("Could not integrate due to no input being a variable");
} }
@ -898,7 +758,6 @@ export function createPassthroughBinaryFormula(
new Formula({ new Formula({
inputs: [value, other], inputs: [value, other],
evaluate: operation, evaluate: operation,
invert: passthrough as InvertFunction<[FormulaSource, FormulaSource]>, invert: passthrough as InvertFunction<[FormulaSource, FormulaSource]>
invertIntegral: passthrough as InvertFunction<[FormulaSource, FormulaSource]>
}); });
} }

View file

@ -22,20 +22,15 @@ type EvaluateFunction<T> = (
type InvertFunction<T> = (this: Formula<T>, value: DecimalSource, ...inputs: T) => DecimalSource; type InvertFunction<T> = (this: Formula<T>, value: DecimalSource, ...inputs: T) => DecimalSource;
type IntegrateFunction<T> = ( type IntegrateFunction<T> = (
this: Formula<T>, this: Formula<T>,
variable: DecimalSource | undefined, variable: Ref<DecimalSource>,
stack: SubstitutionStack | undefined, stack: SubstitutionStack | undefined,
...inputs: T ...inputs: T
) => DecimalSource; ) => GenericFormula;
type SubstitutionFunction<T> = ( type SubstitutionFunction<T> = (
this: Formula<T>, this: Formula<T>,
variable: DecimalSource, variable: GenericFormula,
...inputs: T ...inputs: T
) => DecimalSource; ) => GenericFormula;
type InvertIntegralFunction<T> = (
this: Formula<T>,
value: DecimalSource,
...inputs: T
) => DecimalSource;
type VariableFormulaOptions = { variable: ProcessedComputable<DecimalSource> }; type VariableFormulaOptions = { variable: ProcessedComputable<DecimalSource> };
type ConstantFormulaOptions = { type ConstantFormulaOptions = {
@ -48,7 +43,6 @@ type GeneralFormulaOptions<T extends [FormulaSource] | FormulaSource[]> = {
integrate?: IntegrateFunction<T>; integrate?: IntegrateFunction<T>;
integrateInner?: IntegrateFunction<T>; integrateInner?: IntegrateFunction<T>;
applySubstitution?: SubstitutionFunction<T>; applySubstitution?: SubstitutionFunction<T>;
invertIntegral?: InvertIntegralFunction<T>;
hasVariable?: boolean; hasVariable?: boolean;
}; };
type FormulaOptions<T extends [FormulaSource] | FormulaSource[]> = type FormulaOptions<T extends [FormulaSource] | FormulaSource[]> =
@ -63,12 +57,11 @@ type InternalFormulaProperties<T extends [FormulaSource] | FormulaSource[]> = {
internalInvert?: InvertFunction<T>; internalInvert?: InvertFunction<T>;
internalIntegrate?: IntegrateFunction<T>; internalIntegrate?: IntegrateFunction<T>;
internalIntegrateInner?: IntegrateFunction<T>; internalIntegrateInner?: IntegrateFunction<T>;
internalInvertIntegral?: InvertIntegralFunction<T>;
applySubstitution?: SubstitutionFunction<T>; applySubstitution?: SubstitutionFunction<T>;
innermostVariable?: ProcessedComputable<DecimalSource>; innermostVariable?: ProcessedComputable<DecimalSource>;
}; };
type SubstitutionStack = ((value: DecimalSource) => DecimalSource)[] | undefined; type SubstitutionStack = ((value: GenericFormula) => GenericFormula)[] | undefined;
// It's really hard to type mapped tuples, but these classes seem to manage // It's really hard to type mapped tuples, but these classes seem to manage
type FormulasToDecimals<T extends FormulaSource[]> = { type FormulasToDecimals<T extends FormulaSource[]> = {

View file

@ -491,8 +491,8 @@ describe("Integrating", () => {
constant = Formula.constant(10); constant = Formula.constant(10);
}); });
test("evaluateIntegral() returns variable's value", () => test("variable.evaluateIntegral() calculates correctly", () =>
expect(variable.evaluate()).compare_tolerance(10)); expect(variable.evaluateIntegral()).compare_tolerance(Decimal.pow(10, 2).div(2)));
test("evaluateIntegral(variable) overrides variable value", () => test("evaluateIntegral(variable) overrides variable value", () =>
expect(variable.add(10).evaluateIntegral(20)).compare_tolerance(400)); expect(variable.add(10).evaluateIntegral(20)).compare_tolerance(400));
@ -569,14 +569,10 @@ describe("Integrating", () => {
const actualCost = new Array(10) const actualCost = new Array(10)
.fill(null) .fill(null)
.reduce((acc, _, i) => acc.add(formula.evaluate(i)), new Decimal(0)); .reduce((acc, _, i) => acc.add(formula.evaluate(i)), new Decimal(0));
const calculatedCost = Decimal.add(
formula.evaluateIntegral(),
formula.calculateConstantOfIntegration()
);
// Check if the calculated cost is within 10% of the actual cost, // Check if the calculated cost is within 10% of the actual cost,
// because this is an approximation // because this is an approximation
expect( expect(
Decimal.sub(actualCost, calculatedCost).abs().div(actualCost).toNumber() Decimal.sub(actualCost, formula.evaluateIntegral()).abs().div(actualCost).toNumber()
).toBeLessThan(0.1); ).toBeLessThan(0.1);
}); });
@ -594,8 +590,10 @@ describe("Inverting integrals", () => {
constant = Formula.constant(10); constant = Formula.constant(10);
}); });
test("variable.invertIntegral() is pass-through", () => test("variable.invertIntegral() calculates correctly", () =>
expect(variable.invertIntegral(20)).compare_tolerance(20)); expect(variable.invertIntegral(20)).compare_tolerance(
Decimal.sqrt(20).times(Decimal.sqrt(2))
));
describe("Invertible Integral functions marked as such", () => { describe("Invertible Integral functions marked as such", () => {
function checkFormula(formula: GenericFormula) { function checkFormula(formula: GenericFormula) {
@ -670,7 +668,7 @@ describe("Inverting integrals", () => {
test("Inverting integral of nested formulas", () => { test("Inverting integral of nested formulas", () => {
const formula = Formula.add(variable, constant).times(constant).pow(2).times(30); const formula = Formula.add(variable, constant).times(constant).pow(2).times(30);
expect(formula.invertIntegral(7000000)).compare_tolerance(10); expect(formula.invertIntegral(formula.evaluateIntegral())).compare_tolerance(10);
}); });
test("Inverting integral of nested complex formulas", () => { test("Inverting integral of nested complex formulas", () => {
@ -946,7 +944,7 @@ describe("Custom Formulas", () => {
new Formula({ new Formula({
inputs: [], inputs: [],
evaluate: () => 10, evaluate: () => 10,
integrate: () => 20 integrate: variable => variable
}).evaluateIntegral() }).evaluateIntegral()
).compare_tolerance(20)); ).compare_tolerance(20));
test("One input integrates correctly", () => test("One input integrates correctly", () =>
@ -954,7 +952,7 @@ describe("Custom Formulas", () => {
new Formula({ new Formula({
inputs: [variable], inputs: [variable],
evaluate: () => 10, evaluate: () => 10,
integrate: (val, stack, v1) => val ?? 20 integrate: (variable, stack, v1) => Formula.add(variable, v1)
}).evaluateIntegral() }).evaluateIntegral()
).compare_tolerance(20)); ).compare_tolerance(20));
test("Two inputs integrates correctly", () => test("Two inputs integrates correctly", () =>
@ -962,7 +960,7 @@ describe("Custom Formulas", () => {
new Formula({ new Formula({
inputs: [variable, 2], inputs: [variable, 2],
evaluate: (v1, v2) => 10, evaluate: (v1, v2) => 10,
integrate: (v1, v2) => 3 integrate: (variable, v1, v2) => variable
}).evaluateIntegral() }).evaluateIntegral()
).compare_tolerance(3)); ).compare_tolerance(3));
}); });
@ -973,7 +971,7 @@ describe("Custom Formulas", () => {
new Formula({ new Formula({
inputs: [], inputs: [],
evaluate: () => 10, evaluate: () => 10,
invertIntegral: () => 1, integrate: variable => variable,
hasVariable: true hasVariable: true
}).invertIntegral(8) }).invertIntegral(8)
).toThrow()); ).toThrow());
@ -982,7 +980,7 @@ describe("Custom Formulas", () => {
new Formula({ new Formula({
inputs: [variable], inputs: [variable],
evaluate: () => 10, evaluate: () => 10,
invertIntegral: (val, v1) => 1, integrate: (variable, stack, v1) => variable,
hasVariable: true hasVariable: true
}).invertIntegral(8) }).invertIntegral(8)
).compare_tolerance(1)); ).compare_tolerance(1));
@ -991,7 +989,7 @@ describe("Custom Formulas", () => {
new Formula({ new Formula({
inputs: [variable, 2], inputs: [variable, 2],
evaluate: (v1, v2) => 10, evaluate: (v1, v2) => 10,
invertIntegral: (v1, v2) => 1, integrate: (variable, v1, v2) => variable,
hasVariable: true hasVariable: true
}).invertIntegral(8) }).invertIntegral(8)
).compare_tolerance(1)); ).compare_tolerance(1));