Skip to content

Commit 87dcce8

Browse files
authored
Parenthesize LaTeX limit target for one-sided limits, parenthesize Provided by Priority, support negative integral and derivative iterations (#651)
* Parenthesize LaTeX limit target for one-sided limits * Parenthesize Provided by Priority * Fix typo * Consistently use "for" for provided * Convert integral and derivative negative iterations * Derivative fixes * Latexise inverse and zeroth integral
1 parent 2d2060a commit 87dcce8

7 files changed

Lines changed: 112 additions & 63 deletions

File tree

Sources/AngouriMath/Functions/Continuous/Differentiation.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,8 @@ protected override Entity InnerDifferentiate(Variable variable)
331331
{
332332
(Variable v, var args) when args.All(arg => arg.Nodes.Contains(variable) is false)
333333
=> 0,
334+
// special case f(x) to return a raw Derivativef avoid stack overflowing InnerSimplify
335+
(Variable v, { Head: Variable variable_, Tail : LEmpty<Entity> }) when variable_ == variable => MathS.Derivative(this, variable),
334336

335337
// d/dx_i f(g_1(x_1, x_2, ..., x_n), g_2(x_1, x_2, ..., x_n), ..., g_n(x_1, x_2, ..., x_n))
336338
// becomes

Sources/AngouriMath/Functions/Evaluation/Evaluation.Continuous/Evaluation.Continuous.Calculus.Classes.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ protected override Entity InnerEval() =>
2323
(a, b, c) => (a, b, c) switch
2424
{
2525
(var expr, _, 0) => expr,
26-
// TODO: consider Integral for negative cases
26+
(_, _, int.MinValue) => null,
27+
(_, _, < 0) => new Integralf(a, b, -c).Evaled,
2728
// TODO: should we call InnerSimplified here?
2829
(var expr, Variable var, int asInt)
2930
when expr.Differentiate(var, asInt) is var res and not Derivativef
@@ -46,7 +47,8 @@ protected override Entity InnerSimplify() =>
4647
(a, b, c) => (a, b, c) switch
4748
{
4849
(var expr, _, 0) => expr,
49-
// TODO: consider Integral for negative cases
50+
(_, _, int.MinValue) => null,
51+
(_, _, < 0) => new Integralf(a, b, -c).InnerSimplified,
5052
// TODO: should we call InnerSimlified here?
5153
(var expr, Variable var, int asInt)
5254
when expr.Differentiate(var, asInt) is var res and not Derivativef
@@ -86,7 +88,8 @@ protected override Entity InnerEval() =>
8688
(a, b, c) => (a, b, c) switch
8789
{
8890
(var expr, _, 0) => expr,
89-
// TODO: consider Derivative for negative cases
91+
(_, _, int.MinValue) => null,
92+
(_, _, < 0) => new Derivativef(a, b, -c).Evaled,
9093
(var expr, Variable var, int asInt)
9194
when SequentialIntegrating(expr, var, asInt) is var res and not Integralf
9295
&& !res.Nodes.Any(n => n is Integralf)
@@ -102,7 +105,8 @@ protected override Entity InnerSimplify() =>
102105
(a, b, c) => (a, b, c) switch
103106
{
104107
(var expr, _, 0) => expr,
105-
// TODO: consider Derivative for negative cases
108+
(_, _, int.MinValue) => null,
109+
(_, _, < 0 and not int.MinValue) => new Derivativef(a, b, -c).InnerSimplified,
106110
// TODO: should we apply InnerSimplified?
107111
(var expr, Variable var, int asInt)
108112
when SequentialIntegrating(expr, var, asInt) is var res and not Integralf

Sources/AngouriMath/Functions/Output/Latex/Latex.Calculus.Classes.cs

Lines changed: 26 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,41 +13,34 @@ partial record Entity
1313
{
1414
public partial record Derivativef
1515
{
16-
/// <inheritdoc/>
17-
public override string Latexise()
16+
internal static string LatexiseDerivative(Entity expression, Entity var, int iterations)
1817
{
19-
var powerIfNeeded = Iterations == 1 ? "" : "^{" + Iterations + "}";
20-
21-
var varOverDeriv =
22-
Var is Variable { Name: { Length: 1 } name }
23-
? name
24-
: @"\left[" + Var.Latexise(false) + @"\right]";
25-
18+
var powerIfNeeded = iterations == 1 ? "" : "^{" + iterations + "}";
19+
var varOverDeriv = var is Variable { IsLatexUprightFormatted: false } ? var.Latexise() : @"\left(" + var.Latexise() + @"\right)";
20+
string ParenIfNeeded(string paren) => expression.Priority < Priority.Pow ? paren : "";
2621
// NOTE: \mathrm{d} is used for upright 'd' following ISO 80000-2 standard.
2722
// The differential operator should be upright (roman) to distinguish it from variables, similar to sin, cos, log, etc.
28-
return @"\frac{\mathrm{d}" + powerIfNeeded +
29-
@"\left[" + Expression.Latexise(false) + @"\right]}{\mathrm{d}" + varOverDeriv + powerIfNeeded + "}";
23+
return $$"""\frac{\mathrm{d}{{powerIfNeeded}}}{\mathrm{d}{{varOverDeriv}}{{powerIfNeeded}}}{{ParenIfNeeded(@"\left[")}}{{expression.Latexise()}}{{ParenIfNeeded(@"\right]")}}""";
3024
}
25+
/// <inheritdoc/>
26+
public override string Latexise() => LatexiseDerivative(Expression, Var, Iterations);
3127
}
3228

3329
public partial record Integralf
3430
{
3531
/// <inheritdoc/>
3632
public override string Latexise()
3733
{
38-
// Unlike derivatives, integrals do not have "power" that would be equal
39-
// to sequential applying integration to a function
40-
41-
if (Iterations < 0)
42-
return "Error";
43-
44-
if (Iterations == 0)
45-
return Expression.Latexise(false);
34+
// Unlike derivatives, integrals do not have "power" that would be equal to sequentially applying integration to a function.
35+
// So for non-positive iterations, we just latexise the derivative. Since we treat integrals as functions for parenthesization,
36+
// we still need to latexize the 0th derivative explicitly and not just return the inner expression's latex form.
37+
if (Iterations <= 0)
38+
return Derivativef.LatexiseDerivative(Expression, Var, -Iterations);
4639

4740
var sb = new StringBuilder();
4841
for (int i = 0; i < Iterations; i++)
49-
sb.Append(@"\int ");
50-
sb.Append(@"\left[");
42+
sb.Append(@"\int");
43+
sb.Append(@" \left[");
5144
sb.Append(Expression.Latexise(false));
5245
sb.Append(@"\right]");
5346

@@ -59,18 +52,15 @@ public override string Latexise()
5952
// Thin spaces (\,) are added between differentials following standard practice.
6053
for (int i = 0; i < Iterations; i++)
6154
{
62-
if (i > 0)
63-
sb.Append(@"\,"); // Add thin space between repeated differentials
64-
else
65-
sb.Append(' '); // Leading space before first differential
55+
sb.Append(@"\,");// Leading space before first differential and between differentials
6656
sb.Append(@"\mathrm{d}");
67-
if (Var is Variable { Name: { Length: 1 } name })
68-
sb.Append(name);
57+
if (Var is Variable { IsLatexUprightFormatted: false })
58+
sb.Append(Var.Latexise());
6959
else
7060
{
71-
sb.Append(@"\left[");
72-
sb.Append(Var.Latexise(false));
73-
sb.Append(@"\right]");
61+
sb.Append(@"\left(");
62+
sb.Append(Var.Latexise());
63+
sb.Append(@"\right)");
7464
}
7565
}
7666
return sb.ToString();
@@ -84,15 +74,18 @@ public override string Latexise()
8474
{
8575
var sb = new StringBuilder();
8676
sb.Append(@"\lim_{").Append(Var.Latexise())
87-
.Append(@"\to ").Append(Destination.Latexise());
77+
.Append(@"\to ");
8878

8979
switch (ApproachFrom)
9080
{
9181
case ApproachFrom.Left:
92-
sb.Append("^-");
82+
sb.Append(Destination.Latexise(Destination.Priority <= Priority.Pow)).Append("^-");
9383
break;
9484
case ApproachFrom.Right:
95-
sb.Append("^+");
85+
sb.Append(Destination.Latexise(Destination.Priority <= Priority.Pow)).Append("^+");
86+
break;
87+
case ApproachFrom.BothSides:
88+
sb.Append(Destination.Latexise());
9689
break;
9790
}
9891

Sources/AngouriMath/Functions/Output/Latex/Latex.Classes.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ public partial record Variable
2424

2525
"Gamma", "Delta", "Theta", "Lambda", "Xi", "Pi", "Sigma", "Upsilon", "Phi", "Psi", "Omega",
2626
];
27-
internal bool IsLatexUprightFormatted => IsNameLatexUprightFormatted(Name);
27+
/// <summary>An upright (multi-character or constant) variable needs separation from other upright variables for clarity.
28+
/// Non-upright (only includes italic for now) variables can be written together with all other variables.</summary>
29+
internal bool IsLatexUprightFormatted => SplitIndex() is var (prefix, _) ? IsNameLatexUprightFormatted(prefix) : IsNameLatexUprightFormatted(Name);
2830
internal static bool IsNameLatexUprightFormatted(string varName) =>
2931
// NOTE: Mathematical constants like "pi" and "e" are rendered upright following ISO 80000-2.
3032
// This applies everywhere: as main variables or as subscripts.

Sources/AngouriMath/Functions/Output/Latex/Latex.Omni.Classes.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public override string Latexise()
7979
partial record Providedf
8080
{
8181
/// <inheritdoc/>
82-
public override string Latexise() => $@"\left({Expression.Latexise()} \quad \text{{for}} \quad {Predicate.Latexise()}\right)";
82+
public override string Latexise() => $@"{Expression.Latexise(Expression.Priority < Priority)} \quad \text{{for}} \quad {Predicate.Latexise(Predicate.Priority <= Priority)}";
8383
}
8484

8585
partial record Piecewise
@@ -90,8 +90,8 @@ public override string Latexise() => $@"\begin{{cases}}" +
9090
Cases.Select(c =>
9191
{
9292
if (c.Predicate == Boolean.True)
93-
return $@"{c.Expression.Latexise()} & \text{{otherwise}}";
94-
return $@"{c.Expression.Latexise()} & \text{{if }} {c.Predicate.Latexise()}";
93+
return $@"{c.Expression.Latexise(c.Expression.Priority < Priority.Provided)} & \text{{otherwise}}";
94+
return $@"{c.Expression.Latexise(c.Expression.Priority < Priority.Provided)} & \text{{for }} {c.Predicate.Latexise(c.Predicate.Priority <= Priority.Provided)}"; // "for" used in https://mathworld.wolfram.com/Derivative.html
9595
}
9696
))
9797
+

Sources/Tests/UnitTests/Common/InnerSimplifyTest.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,30 @@ public void PiecewiseAndPiecewise(string before, string inBetween)
141141
[InlineData("(|[2, 3, 6]|)", "7")]
142142
public void TestMatrices(string before, string after)
143143
=> ShouldChangeTo(before, after);
144+
[Fact] public void IntegralToDerivativeSimplify() =>
145+
"integral(apply(f, x), x, -1)".ToEntity().InnerSimplified.ShouldBe("derivative(apply(f, x), x)");
146+
[Fact] public void IntegralToDerivativeSimplify2() =>
147+
"integral(apply(f, x), x, -2)".ToEntity().InnerSimplified.ShouldBe("derivative(apply(f, x), x, 2)");
148+
[Fact] public void IntegralToDerivativeSimplifyMinValue() =>
149+
$"integral(apply(f, x), x, {int.MinValue})".ToEntity().InnerSimplified.ShouldBe($"integral(apply(f, x), x, {int.MinValue})");
150+
[Fact] public void IntegralToDerivativeEval() =>
151+
"integral(apply(f, x) + (sin(3)^2 + cos(3)^2), x, -1)".ToEntity().Evaled.ShouldBe("derivative(apply(f, x), x) + 0");
152+
[Fact] public void IntegralToDerivativeEval2() =>
153+
"integral(apply(f, x), x, -2)".ToEntity().Evaled.ShouldBe("derivative(apply(f, x), x, 2)");
154+
[Fact] public void IntegralToDerivativeEvalMinValue() =>
155+
$"integral(apply(f, x) + (sin(3)^2 + cos(3)^2), x, {int.MinValue})".ToEntity().Evaled.ShouldBe($"integral(apply(f, x) + 1, x, {int.MinValue})");
156+
[Fact] public void DerivativeToIntegralSimplify() =>
157+
"derivative(apply(f, x), x, -1)".ToEntity().InnerSimplified.ShouldBe("integral(apply(f, x), x)");
158+
[Fact] public void DerivativeToIntegralSimplify2() =>
159+
"derivative(apply(f, x), x, -2)".ToEntity().InnerSimplified.ShouldBe("integral(apply(f, x), x, 2)");
160+
[Fact] public void DerivativeToIntegralSimplifyMinValue() =>
161+
$"derivative(apply(f, x), x, {int.MinValue})".ToEntity().InnerSimplified.ShouldBe($"derivative(apply(f, x), x, {int.MinValue})");
162+
[Fact] public void DerivativeToIntegralEval() =>
163+
"derivative(apply(f, x) + (sin(3)^2 + cos(3)^2), x, -1)".ToEntity().Evaled.ShouldBe("integral(apply(f, x) + 1, x)");
164+
[Fact] public void DerivativeToIntegralEval2() =>
165+
"derivative(apply(f, x), x, -2)".ToEntity().Evaled.ShouldBe("integral(apply(f, x), x, 2)");
166+
[Fact] public void DerivativeToIntegralEvalMinValue() =>
167+
$"derivative(apply(f, x) + (sin(3)^2 + cos(3)^2), x, {int.MinValue})".ToEntity().Evaled.ShouldBe($"derivative(apply(f, x) + 1, x, {int.MinValue})");
144168

145169
[Fact] public void PiecewiseIntegrate1() =>
146170
"piecewise(2 provided a, 3)".Integrate("x")

0 commit comments

Comments
 (0)