Skip to content

Commit b44972d

Browse files
authored
Place calculus operator priority between addition/subtraction and mutiplication/division in Latexise (#656)
1 parent 8e4eaf0 commit b44972d

File tree

9 files changed

+111
-101
lines changed

9 files changed

+111
-101
lines changed

Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Calculus.Classes.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ partial record Entity
1616
/// <summary>
1717
/// A node of derivative
1818
/// </summary>
19-
public sealed partial record Derivativef(Entity Expression, Entity Var, int Iterations) : Function
19+
public sealed partial record Derivativef(Entity Expression, Entity Var, int Iterations) : CalculusOperator(Expression, Var)
2020
{
2121
/// <summary>Reuse the cache by returning the same object if possible</summary>
2222
private Derivativef New(Entity expression, Entity var) =>
@@ -32,7 +32,7 @@ public override Entity Replace(Func<Entity, Entity> func) =>
3232
/// <summary>
3333
/// A node of integral
3434
/// </summary>
35-
public sealed partial record Integralf(Entity Expression, Entity Var, int Iterations) : Function
35+
public sealed partial record Integralf(Entity Expression, Entity Var, int Iterations) : CalculusOperator(Expression, Var)
3636
{
3737
/// <summary>Reuse the cache by returning the same object if possible</summary>
3838
private Integralf New(Entity expression, Entity var) =>
@@ -48,7 +48,7 @@ public override Entity Replace(Func<Entity, Entity> func) =>
4848
/// <summary>
4949
/// A node of limit
5050
/// </summary>
51-
public sealed partial record Limitf(Entity Expression, Entity Var, Entity Destination, ApproachFrom ApproachFrom) : Function
51+
public sealed partial record Limitf(Entity Expression, Entity Var, Entity Destination, ApproachFrom ApproachFrom) : CalculusOperator(Expression, Var)
5252
{
5353
/// <summary>Reuse the cache by returning the same object if possible</summary>
5454
private Limitf New(Entity expression, Entity var, Entity destination, ApproachFrom approachFrom) =>

Sources/AngouriMath/Core/Entity/Continuous/Entity.Continuous.Definition.cs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,27 @@ public abstract partial record ContinuousNode : Entity
1717

1818
}
1919

20+
/// <summary>
21+
/// Describes any node that is a function (e. g. sin, cos, etc.) or calculus operator (e. g. derivative, integral, limit)
22+
/// but not an arithmetic operator or leaf
23+
/// </summary>
24+
public abstract record Function : ContinuousNode
25+
{
26+
internal override Priority Priority => Priority.Func;
27+
}
28+
2029
/// <summary>
2130
/// Describes any function that is related to trigonometry
2231
/// </summary>
2332
public abstract record TrigonometricFunction : Function
2433
{
2534

35+
}
36+
/// <summary>
37+
/// Describes any calculus operator
38+
/// </summary>
39+
public abstract partial record CalculusOperator(Entity Expression, Entity Var) : Function
40+
{
2641
}
2742

2843
/// <summary>
@@ -100,14 +115,5 @@ public abstract record TrigonometricFunction : Function
100115
public Entity Signum() => new Signumf(this);
101116
/// <summary><see cref="MathS.Abs(Entity)"/></summary>
102117
public Entity Abs() => new Absf(this);
103-
104-
/// <summary>
105-
/// Describes any node that is a function (e. g. sin, cos, etc.)
106-
/// but not an operator or leaf
107-
/// </summary>
108-
public abstract record Function : ContinuousNode
109-
{
110-
internal override Priority Priority => Priority.Func;
111-
}
112118
}
113119
}

Sources/AngouriMath/Core/Entity/Entity.Definition.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,14 @@ internal enum Priority
4747

4848
Sum = 20 | NumericalOperation,
4949
Minus = 20 | NumericalOperation,
50+
/// <summary>For text formats, <see cref="Func"/> is used instead.</summary>
51+
LatexCalculusOperation = 30 | NumericalOperation,
5052
Mul = 40 | NumericalOperation,
5153
Div = 40 | NumericalOperation,
5254
Pow = 60 | NumericalOperation,
5355
Factorial = 70 | NumericalOperation,
5456
Func = 80 | NumericalOperation,
5557

56-
5758
Leaf = 100 | NumericalOperation,
5859
}
5960
#pragma warning restore CA1069 // Enums values should not be duplicated

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ partial record Entity : ILatexiseable
4545
/// </code>
4646
/// </example>
4747
public abstract string Latexise();
48+
/// <summary>
49+
/// Calculus operators, unlike other functions, have a <see cref="LatexPriority"/> between addition/subtraction
50+
/// and multiplication/division which is different from <see cref="Priority"/>.
51+
/// </summary>
52+
internal virtual Priority LatexPriority => Priority;
4853

4954
/// <summary>Returns the expression in LaTeX (for example, a / b -> \frac{a}{b})</summary>
5055
/// <param name="parenthesesRequired">Whether to wrap it with parentheses</param>

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

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,17 @@ public partial record Sumf
1515
{
1616
/// <inheritdoc/>
1717
public override string Latexise() =>
18-
Augend.Latexise(Augend.Priority < Priority)
19-
+ (Addend.Latexise(Addend.Priority < Priority) is var addend && addend.StartsWith("-")
18+
Augend.Latexise(Augend.LatexPriority < LatexPriority)
19+
+ (Addend.Latexise(Addend.LatexPriority < LatexPriority) is var addend && addend.StartsWith("-")
2020
? addend : "+" + addend);
2121
}
2222

2323
public partial record Minusf
2424
{
2525
/// <inheritdoc/>
2626
public override string Latexise() =>
27-
Subtrahend.Latexise(Subtrahend.Priority < Priority)
28-
+ "-" + Minuend.Latexise(Minuend.Priority <= Priority);
27+
Subtrahend.Latexise(Subtrahend.LatexPriority < LatexPriority)
28+
+ "-" + Minuend.Latexise(Minuend.LatexPriority <= LatexPriority);
2929
}
3030

3131
public partial record Mulf
@@ -43,16 +43,16 @@ public override string Latexise()
4343
return currIn switch
4444
{
4545
// -1, -2, 2i, i, -i, -2i etc. in the front and not (1+i) etc.
46-
Number { Priority: Priority.Sum } and not Complex { RealPart.IsZero: false, ImaginaryPart.IsZero: false } =>
46+
Number { LatexPriority: Priority.Sum } and not Complex { RealPart.IsZero: false, ImaginaryPart.IsZero: false } =>
4747
currIn.Latexise(false),
48-
_ => currIn.Latexise(currIn.Priority < Priority)
48+
_ => currIn.Latexise(currIn.LatexPriority < LatexPriority)
4949
};
5050
case 1:
5151
if (longArray[index - 1] is Integer(-1))
52-
return $"-{currIn.Latexise(currIn.Priority < Priority)}"; // display "-1 * x * y" as "-x \cdot y", only for the first -1
52+
return $"-{currIn.Latexise(currIn.LatexPriority < LatexPriority)}"; // display "-1 * x * y" as "-x \cdot y", only for the first -1
5353
break;
5454
}
55-
var currOut = currIn.Latexise(currIn.Priority < Priority);
55+
var currOut = currIn.Latexise(currIn.LatexPriority < LatexPriority);
5656

5757
return (longArray[index - 1], currIn) switch // whether we use juxtaposition and omit \cdot
5858
{
@@ -61,20 +61,20 @@ public override string Latexise()
6161

6262
// Don't juxtapose upright variables with numbers like displaying "var2" for "var*2" since "var2" may be interpreted as one variable.
6363
// Also, don't produce upright "ei" (one variable with two chars) for e*i, or "ei^2" for e*i^2.
64-
// but "e (2+i)" and "e (2+i)^2" are fine with the parentheses - so we have the priority check.
64+
// but "e (2+i)" and "e (2+i)^2" are fine with the parentheses - so we have the LatexPriority check.
6565
(Variable { IsLatexUprightFormatted: true }
66-
or Complex { ImaginaryPart.IsZero: false, Priority: >= Priority.Mul } /* don't combine upright "i" with an upright variable*/,
67-
Variable { IsLatexUprightFormatted: true } or Number { Priority: >= Priority.Mul }
68-
or Factorialf(Number { Priority: Priority.Leaf } or Variable { IsLatexUprightFormatted: true })
69-
or Powf(Number { Priority: Priority.Leaf } or Variable { IsLatexUprightFormatted: true }
70-
or Factorialf(Number { Priority: Priority.Leaf } or Variable { IsLatexUprightFormatted: true }), _)) => false,
66+
or Complex { ImaginaryPart.IsZero: false, LatexPriority: >= Priority.Mul } /* don't combine upright "i" with an upright variable*/,
67+
Variable { IsLatexUprightFormatted: true } or Number { LatexPriority: >= Priority.Mul }
68+
or Factorialf(Number { LatexPriority: Priority.Leaf } or Variable { IsLatexUprightFormatted: true })
69+
or Powf(Number { LatexPriority: Priority.Leaf } or Variable { IsLatexUprightFormatted: true }
70+
or Factorialf(Number { LatexPriority: Priority.Leaf } or Variable { IsLatexUprightFormatted: true }), _)) => false,
7171
// 2 * (3/4) instead of 2 (3/4) which is a mixed number (= 2 + 3/4)
72-
(Number { Priority: Priority.Leaf }, { Priority: Priority.Div }) => false,
73-
// 2 * 3 instead of 2 3 (= 23), 2 * 3^4 instead of 2 3^4 (= 23^4), but "(2+i) 2", "2 (2+i)" and "2 (2+i)^2" are fine with the parentheses - so we have the priority check.
74-
(_, Number { Priority: >= Priority.Mul } or Factorialf(Number { Priority: Priority.Leaf })
75-
or Powf(Number { Priority: Priority.Leaf } or Factorialf(Number { Priority: Priority.Leaf }), _)) => false, // Keep the \cdot in "f(x) \cdot -2" "f(x) \cdot 2i" "f(x) \cdot -2i"
76-
(var left, var right) => left.Priority >= right.Priority &&
77-
!(left.Priority == Priority.Div && right.Priority == Priority.Div) // Without \cdot, the fraction lines may appear too closely together.
72+
(Number { LatexPriority: Priority.Leaf }, { LatexPriority: Priority.Div }) => false,
73+
// 2 * 3 instead of 2 3 (= 23), 2 * 3^4 instead of 2 3^4 (= 23^4), but "(2+i) 2", "2 (2+i)" and "2 (2+i)^2" are fine with the parentheses - so we have the LatexPriority check.
74+
(_, Number { LatexPriority: >= Priority.Mul } or Factorialf(Number { LatexPriority: Priority.Leaf })
75+
or Powf(Number { LatexPriority: Priority.Leaf } or Factorialf(Number { LatexPriority: Priority.Leaf }), _)) => false, // Keep the \cdot in "f(x) \cdot -2" "f(x) \cdot 2i" "f(x) \cdot -2i"
76+
(var left, var right) => left.LatexPriority >= right.LatexPriority &&
77+
!(left.LatexPriority == Priority.Div && right.LatexPriority == Priority.Div) // Without \cdot, the fraction lines may appear too closely together.
7878
} ? $@"{prevOut} {currOut}" : $@"{prevOut} \cdot {currOut}";
7979
});
8080

@@ -125,7 +125,7 @@ public override string Latexise()
125125
}
126126
else
127127
{
128-
return "{" + Base.Latexise(Base.Priority <= Priority) + "}^{" + Exponent.Latexise() + "}";
128+
return "{" + Base.Latexise(Base.LatexPriority <= LatexPriority) + "}^{" + Exponent.Latexise() + "}";
129129
}
130130
}
131131
}
@@ -134,7 +134,7 @@ public partial record Factorialf
134134
{
135135
/// <inheritdoc/>
136136
public override string Latexise() =>
137-
Argument.Latexise(Argument.Priority <= Priority) + "!";
137+
Argument.Latexise(Argument.LatexPriority <= LatexPriority) + "!";
138138
}
139139

140140
partial record Signumf

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

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,20 @@ namespace AngouriMath
1111
{
1212
partial record Entity
1313
{
14+
partial record CalculusOperator
15+
{
16+
/// <inheritdoc/>
17+
internal override Priority LatexPriority => Priority.LatexCalculusOperation;
18+
}
1419
public partial record Derivativef
1520
{
1621
internal static string LatexiseDerivative(Entity expression, Entity var, int iterations)
1722
{
1823
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 : "";
2124
// NOTE: \mathrm{d} is used for upright 'd' following ISO 80000-2 standard.
2225
// The differential operator should be upright (roman) to distinguish it from variables, similar to sin, cos, log, etc.
23-
return $$"""\frac{\mathrm{d}{{powerIfNeeded}}}{\mathrm{d}{{varOverDeriv}}{{powerIfNeeded}}}{{ParenIfNeeded(@"\left[")}}{{expression.Latexise()}}{{ParenIfNeeded(@"\right]")}}""";
26+
return $$"""\frac{\mathrm{d}{{powerIfNeeded}}}{\mathrm{d}{{var.Latexise(var is not Variable { IsLatexUprightFormatted: false })
27+
}}{{powerIfNeeded}}}{{expression.Latexise(expression.LatexPriority < Priority.LatexCalculusOperation)}}""";
2428
}
2529
/// <inheritdoc/>
2630
public override string Latexise() => LatexiseDerivative(Expression, Var, Iterations);
@@ -40,9 +44,7 @@ public override string Latexise()
4044
var sb = new StringBuilder();
4145
for (int i = 0; i < Iterations; i++)
4246
sb.Append(@"\int");
43-
sb.Append(@" \left[");
44-
sb.Append(Expression.Latexise(false));
45-
sb.Append(@"\right]");
47+
sb.Append(' ').Append(Expression.Latexise(Expression.LatexPriority < Priority.LatexCalculusOperation));
4648

4749
// NOTE: \mathrm{d} is used for upright 'd' following ISO 80000-2 standard.
4850
// The differential operator should be upright (roman) to distinguish it from variables.
@@ -54,14 +56,7 @@ public override string Latexise()
5456
{
5557
sb.Append(@"\,");// Leading space before first differential and between differentials
5658
sb.Append(@"\mathrm{d}");
57-
if (Var is Variable { IsLatexUprightFormatted: false })
58-
sb.Append(Var.Latexise());
59-
else
60-
{
61-
sb.Append(@"\left(");
62-
sb.Append(Var.Latexise());
63-
sb.Append(@"\right)");
64-
}
59+
sb.Append(Var.Latexise(Var is not Variable { IsLatexUprightFormatted: false }));
6560
}
6661
return sb.ToString();
6762
}
@@ -79,23 +74,18 @@ public override string Latexise()
7974
switch (ApproachFrom)
8075
{
8176
case ApproachFrom.Left:
82-
sb.Append(Destination.Latexise(Destination.Priority <= Priority.Pow)).Append("^-");
77+
sb.Append(Destination.Latexise(Destination.LatexPriority <= Priority.Pow)).Append("^-");
8378
break;
8479
case ApproachFrom.Right:
85-
sb.Append(Destination.Latexise(Destination.Priority <= Priority.Pow)).Append("^+");
80+
sb.Append(Destination.Latexise(Destination.LatexPriority <= Priority.Pow)).Append("^+");
8681
break;
8782
case ApproachFrom.BothSides:
8883
sb.Append(Destination.Latexise());
8984
break;
9085
}
9186

9287
sb.Append("} ");
93-
if (Expression.Priority < Priority.Pow)
94-
sb.Append(@"\left[");
95-
sb.Append(Expression.Latexise());
96-
if (Expression.Priority < Priority.Pow)
97-
sb.Append(@"\right]");
98-
88+
sb.Append(Expression.Latexise(Expression.LatexPriority < Priority.LatexCalculusOperation));
9989
return sb.ToString();
10090
}
10191
}

0 commit comments

Comments
 (0)