Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ partial record Entity
/// <summary>
/// A node of derivative
/// </summary>
public sealed partial record Derivativef(Entity Expression, Entity Var, int Iterations) : Function
public sealed partial record Derivativef(Entity Expression, Entity Var, int Iterations) : CalculusOperator(Expression, Var)
{
/// <summary>Reuse the cache by returning the same object if possible</summary>
private Derivativef New(Entity expression, Entity var) =>
Expand All @@ -32,7 +32,7 @@ public override Entity Replace(Func<Entity, Entity> func) =>
/// <summary>
/// A node of integral
/// </summary>
public sealed partial record Integralf(Entity Expression, Entity Var, int Iterations) : Function
public sealed partial record Integralf(Entity Expression, Entity Var, int Iterations) : CalculusOperator(Expression, Var)
{
/// <summary>Reuse the cache by returning the same object if possible</summary>
private Integralf New(Entity expression, Entity var) =>
Expand All @@ -48,7 +48,7 @@ public override Entity Replace(Func<Entity, Entity> func) =>
/// <summary>
/// A node of limit
/// </summary>
public sealed partial record Limitf(Entity Expression, Entity Var, Entity Destination, ApproachFrom ApproachFrom) : Function
public sealed partial record Limitf(Entity Expression, Entity Var, Entity Destination, ApproachFrom ApproachFrom) : CalculusOperator(Expression, Var)
{
/// <summary>Reuse the cache by returning the same object if possible</summary>
private Limitf New(Entity expression, Entity var, Entity destination, ApproachFrom approachFrom) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,27 @@ public abstract partial record ContinuousNode : Entity

}

/// <summary>
/// Describes any node that is a function (e. g. sin, cos, etc.) or calculus operator (e. g. derivative, integral, limit)
/// but not an arithmetic operator or leaf
/// </summary>
public abstract record Function : ContinuousNode
{
internal override Priority Priority => Priority.Func;
}

/// <summary>
/// Describes any function that is related to trigonometry
/// </summary>
public abstract record TrigonometricFunction : Function
{

}
/// <summary>
/// Describes any calculus operator
/// </summary>
public abstract partial record CalculusOperator(Entity Expression, Entity Var) : Function
{
}

/// <summary>
Expand Down Expand Up @@ -100,14 +115,5 @@ public abstract record TrigonometricFunction : Function
public Entity Signum() => new Signumf(this);
/// <summary><see cref="MathS.Abs(Entity)"/></summary>
public Entity Abs() => new Absf(this);

/// <summary>
/// Describes any node that is a function (e. g. sin, cos, etc.)
/// but not an operator or leaf
/// </summary>
public abstract record Function : ContinuousNode
{
internal override Priority Priority => Priority.Func;
}
}
}
3 changes: 2 additions & 1 deletion Sources/AngouriMath/Core/Entity/Entity.Definition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,14 @@ internal enum Priority

Sum = 20 | NumericalOperation,
Minus = 20 | NumericalOperation,
/// <summary>For text formats, <see cref="Func"/> is used instead.</summary>
LatexCalculusOperation = 30 | NumericalOperation,
Mul = 40 | NumericalOperation,
Div = 40 | NumericalOperation,
Pow = 60 | NumericalOperation,
Factorial = 70 | NumericalOperation,
Func = 80 | NumericalOperation,


Leaf = 100 | NumericalOperation,
}
#pragma warning restore CA1069 // Enums values should not be duplicated
Expand Down
5 changes: 5 additions & 0 deletions Sources/AngouriMath/Functions/Output/Latex.Definition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ partial record Entity : ILatexiseable
/// </code>
/// </example>
public abstract string Latexise();
/// <summary>
/// Calculus operators, unlike other functions, have a <see cref="LatexPriority"/> between addition/subtraction
/// and multiplication/division which is different from <see cref="Priority"/>.
/// </summary>
internal virtual Priority LatexPriority => Priority;

/// <summary>Returns the expression in LaTeX (for example, a / b -> \frac{a}{b})</summary>
/// <param name="parenthesesRequired">Whether to wrap it with parentheses</param>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@ public partial record Sumf
{
/// <inheritdoc/>
public override string Latexise() =>
Augend.Latexise(Augend.Priority < Priority)
+ (Addend.Latexise(Addend.Priority < Priority) is var addend && addend.StartsWith("-")
Augend.Latexise(Augend.LatexPriority < LatexPriority)
+ (Addend.Latexise(Addend.LatexPriority < LatexPriority) is var addend && addend.StartsWith("-")
? addend : "+" + addend);
}

public partial record Minusf
{
/// <inheritdoc/>
public override string Latexise() =>
Subtrahend.Latexise(Subtrahend.Priority < Priority)
+ "-" + Minuend.Latexise(Minuend.Priority <= Priority);
Subtrahend.Latexise(Subtrahend.LatexPriority < LatexPriority)
+ "-" + Minuend.Latexise(Minuend.LatexPriority <= LatexPriority);
}

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

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

// Don't juxtapose upright variables with numbers like displaying "var2" for "var*2" since "var2" may be interpreted as one variable.
// Also, don't produce upright "ei" (one variable with two chars) for e*i, or "ei^2" for e*i^2.
// but "e (2+i)" and "e (2+i)^2" are fine with the parentheses - so we have the priority check.
// but "e (2+i)" and "e (2+i)^2" are fine with the parentheses - so we have the LatexPriority check.
(Variable { IsLatexUprightFormatted: true }
or Complex { ImaginaryPart.IsZero: false, Priority: >= Priority.Mul } /* don't combine upright "i" with an upright variable*/,
Variable { IsLatexUprightFormatted: true } or Number { Priority: >= Priority.Mul }
or Factorialf(Number { Priority: Priority.Leaf } or Variable { IsLatexUprightFormatted: true })
or Powf(Number { Priority: Priority.Leaf } or Variable { IsLatexUprightFormatted: true }
or Factorialf(Number { Priority: Priority.Leaf } or Variable { IsLatexUprightFormatted: true }), _)) => false,
or Complex { ImaginaryPart.IsZero: false, LatexPriority: >= Priority.Mul } /* don't combine upright "i" with an upright variable*/,
Variable { IsLatexUprightFormatted: true } or Number { LatexPriority: >= Priority.Mul }
or Factorialf(Number { LatexPriority: Priority.Leaf } or Variable { IsLatexUprightFormatted: true })
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Condition always evaluates to 'false'.

Suggested change
or Factorialf(Number { LatexPriority: Priority.Leaf } or Variable { IsLatexUprightFormatted: true })
or Factorialf(Variable { IsLatexUprightFormatted: true })

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this the case?

or Powf(Number { LatexPriority: Priority.Leaf } or Variable { IsLatexUprightFormatted: true }
or Factorialf(Number { LatexPriority: Priority.Leaf } or Variable { IsLatexUprightFormatted: true }), _)) => false,
// 2 * (3/4) instead of 2 (3/4) which is a mixed number (= 2 + 3/4)
(Number { Priority: Priority.Leaf }, { Priority: Priority.Div }) => false,
// 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.
(_, Number { Priority: >= Priority.Mul } or Factorialf(Number { Priority: Priority.Leaf })
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"
(var left, var right) => left.Priority >= right.Priority &&
!(left.Priority == Priority.Div && right.Priority == Priority.Div) // Without \cdot, the fraction lines may appear too closely together.
(Number { LatexPriority: Priority.Leaf }, { LatexPriority: Priority.Div }) => false,
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Condition always evaluates to 'false'.

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Condition always evaluates to 'false'.

Copilot uses AI. Check for mistakes.
// 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.
(_, Number { LatexPriority: >= Priority.Mul } or Factorialf(Number { LatexPriority: Priority.Leaf })
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Condition always evaluates to 'false'.

Suggested change
(_, Number { LatexPriority: >= Priority.Mul } or Factorialf(Number { LatexPriority: Priority.Leaf })
(_, Number or Factorialf(Number { LatexPriority: Priority.Leaf })

Copilot uses AI. Check for mistakes.
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"
Comment on lines +74 to +75
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Condition always evaluates to 'false'.

Copilot uses AI. Check for mistakes.
(var left, var right) => left.LatexPriority >= right.LatexPriority &&
!(left.LatexPriority == Priority.Div && right.LatexPriority == Priority.Div) // Without \cdot, the fraction lines may appear too closely together.
} ? $@"{prevOut} {currOut}" : $@"{prevOut} \cdot {currOut}";
});

Expand Down Expand Up @@ -125,7 +125,7 @@ public override string Latexise()
}
else
{
return "{" + Base.Latexise(Base.Priority <= Priority) + "}^{" + Exponent.Latexise() + "}";
return "{" + Base.Latexise(Base.LatexPriority <= LatexPriority) + "}^{" + Exponent.Latexise() + "}";
}
}
}
Expand All @@ -134,7 +134,7 @@ public partial record Factorialf
{
/// <inheritdoc/>
public override string Latexise() =>
Argument.Latexise(Argument.Priority <= Priority) + "!";
Argument.Latexise(Argument.LatexPriority <= LatexPriority) + "!";
}

partial record Signumf
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,20 @@ namespace AngouriMath
{
partial record Entity
{
partial record CalculusOperator
{
/// <inheritdoc/>
internal override Priority LatexPriority => Priority.LatexCalculusOperation;
}
public partial record Derivativef
{
internal static string LatexiseDerivative(Entity expression, Entity var, int iterations)
{
var powerIfNeeded = iterations == 1 ? "" : "^{" + iterations + "}";
var varOverDeriv = var is Variable { IsLatexUprightFormatted: false } ? var.Latexise() : @"\left(" + var.Latexise() + @"\right)";
string ParenIfNeeded(string paren) => expression.Priority < Priority.Pow ? paren : "";
// NOTE: \mathrm{d} is used for upright 'd' following ISO 80000-2 standard.
// The differential operator should be upright (roman) to distinguish it from variables, similar to sin, cos, log, etc.
return $$"""\frac{\mathrm{d}{{powerIfNeeded}}}{\mathrm{d}{{varOverDeriv}}{{powerIfNeeded}}}{{ParenIfNeeded(@"\left[")}}{{expression.Latexise()}}{{ParenIfNeeded(@"\right]")}}""";
return $$"""\frac{\mathrm{d}{{powerIfNeeded}}}{\mathrm{d}{{var.Latexise(var is not Variable { IsLatexUprightFormatted: false })
}}{{powerIfNeeded}}}{{expression.Latexise(expression.LatexPriority < Priority.LatexCalculusOperation)}}""";
}
/// <inheritdoc/>
public override string Latexise() => LatexiseDerivative(Expression, Var, Iterations);
Expand All @@ -40,9 +44,7 @@ public override string Latexise()
var sb = new StringBuilder();
for (int i = 0; i < Iterations; i++)
sb.Append(@"\int");
sb.Append(@" \left[");
sb.Append(Expression.Latexise(false));
sb.Append(@"\right]");
sb.Append(' ').Append(Expression.Latexise(Expression.LatexPriority < Priority.LatexCalculusOperation));

// NOTE: \mathrm{d} is used for upright 'd' following ISO 80000-2 standard.
// The differential operator should be upright (roman) to distinguish it from variables.
Expand All @@ -54,14 +56,7 @@ public override string Latexise()
{
sb.Append(@"\,");// Leading space before first differential and between differentials
sb.Append(@"\mathrm{d}");
if (Var is Variable { IsLatexUprightFormatted: false })
sb.Append(Var.Latexise());
else
{
sb.Append(@"\left(");
sb.Append(Var.Latexise());
sb.Append(@"\right)");
}
sb.Append(Var.Latexise(Var is not Variable { IsLatexUprightFormatted: false }));
}
return sb.ToString();
}
Expand All @@ -79,23 +74,18 @@ public override string Latexise()
switch (ApproachFrom)
{
case ApproachFrom.Left:
sb.Append(Destination.Latexise(Destination.Priority <= Priority.Pow)).Append("^-");
sb.Append(Destination.Latexise(Destination.LatexPriority <= Priority.Pow)).Append("^-");
break;
case ApproachFrom.Right:
sb.Append(Destination.Latexise(Destination.Priority <= Priority.Pow)).Append("^+");
sb.Append(Destination.Latexise(Destination.LatexPriority <= Priority.Pow)).Append("^+");
break;
case ApproachFrom.BothSides:
sb.Append(Destination.Latexise());
break;
}

sb.Append("} ");
if (Expression.Priority < Priority.Pow)
sb.Append(@"\left[");
sb.Append(Expression.Latexise());
if (Expression.Priority < Priority.Pow)
sb.Append(@"\right]");

sb.Append(Expression.Latexise(Expression.LatexPriority < Priority.LatexCalculusOperation));
return sb.ToString();
}
}
Expand Down
Loading
Loading