Skip to content

Commit 1079f32

Browse files
authored
Fix stack overflow for nonexistent two-sided limits (#654)
1 parent 6b61aac commit 1079f32

5 files changed

Lines changed: 34 additions & 8 deletions

File tree

Sources/AngouriMath/Functions/Continuous/Limits/Limit.Definition.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,7 @@ partial record Entity
6060
public Entity Limit(Variable x, Entity destination, ApproachFrom side)
6161
{
6262
var res = ComputeLimit(this, x, destination, side);
63-
if (res is null || res == MathS.NaN)
64-
return new Limitf(this, x, destination, side);
63+
if (res is null) return new Limitf(this, x, destination, side);
6564
return res.InnerSimplified;
6665
}
6766

Sources/AngouriMath/Functions/Continuous/Limits/Solvers/Solvers.Definition.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,15 @@ private static Entity ExpandLogarithm(Entity expr)
7272
return fromLeft;
7373
if (ExpressionNumerical.AreEqual(fromLeft, fromRight) && (acceptNaN || fromLeft.Evaled != MathS.NaN))
7474
return fromLeft;
75-
expr = ApplylHopitalRule(expr, x, dest);
76-
return ComputeLimit(expr, x, dest, acceptNaN: true);
75+
var lhopital = ApplylHopitalRule(expr, x, dest);
76+
if (lhopital != null) return ComputeLimit(lhopital, x, dest, acceptNaN: true);
77+
else return MathS.NaN; // A two-sided limit cannot exist if the limit from left and right don't match.
7778
}
7879
else
7980
{
80-
expr = ApplylHopitalRule(expr, x, dest);
81-
return ComputeLimit(expr, x, dest);
81+
var lhopital = ApplylHopitalRule(expr, x, dest);
82+
if (lhopital != null) return ComputeLimit(lhopital, x, dest);
83+
else return null;
8284
}
8385
}
8486
throw new AngouriBugException($"Unresolved enum parameter {side}");

Sources/AngouriMath/Functions/Continuous/Limits/Transformations.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ private static bool IsInfiniteNode(Entity expr)
5555
private static bool IsFiniteNode(Entity expr)
5656
=> !IsInfiniteNode(expr) && expr != MathS.NaN;
5757

58-
private static Entity ApplylHopitalRule(Entity expr, Variable x, Entity dest)
58+
private static Entity? ApplylHopitalRule(Entity expr, Variable x, Entity dest)
5959
{
6060
if (expr is Divf(var num, var den))
6161
if (EvalAssumingContinuous(num.Limit(x, dest)) is var numLimit && EvalAssumingContinuous(den.Limit(x, dest)) is var denLimit)
@@ -69,7 +69,7 @@ private static Entity ApplylHopitalRule(Entity expr, Variable x, Entity dest)
6969
if (ComputeLimit(applied, x, dest) is { } resLim)
7070
return resLim;
7171
}
72-
return expr;
72+
return null;
7373
}
7474

7575
private static Entity ApplyTrivialTransformations(Entity expr, Variable x, Entity dest, Func<Entity, Entity, Entity> transformation)

Sources/Tests/UnitTests/Calculus/LimitTest.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,5 +130,24 @@ public void TestComplicated()
130130
Assert.NotNull(limit);
131131
Assert.Equal("sqrt(a / c * 3 / sin(a / c) + sin(d))", limit?.Stringize());
132132
}
133+
[Theory]
134+
[InlineData("limit(1/x,x,0)")]
135+
[InlineData("limit(1/x^3,x,0)")]
136+
[InlineData("limit(x^x,x,0)")]
137+
public void TestNoLimit(string input) // a two-sided limit does not exist
138+
{
139+
var limit = input.ToEntity();
140+
Assert.Equal(MathS.NaN, limit.InnerSimplified);
141+
Assert.Equal(MathS.NaN, limit.Evaled);
142+
}
143+
[Theory]
144+
[InlineData("limit(apply(f, x),x,-1)")]
145+
[InlineData("limit(x!,x,-1)")]
146+
public void TestCantSolve(string input) // this is different from no limit
147+
{
148+
var limit = input.ToEntity();
149+
Assert.Equal(input, limit.InnerSimplified);
150+
Assert.Equal(input, limit.Evaled);
151+
}
133152
}
134153
}

Sources/Tests/UnitTests/PatternsTest/SimplifyTest.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,12 @@ [Fact] public void BigSimple1() => AssertSimplifyToString(
161161
[InlineData("ln(a) - ln(b)", "ln(a / b)")]
162162
[InlineData("log(2, a) + ln(b)", "log(2, a) + ln(b)")]
163163
public void PowerRulesTest(string input, string output) => AssertSimplifyToString(input, output);
164+
165+
[Theory]
166+
[InlineData("a/a", "1 provided not a = 0")]
167+
[InlineData("(-a)/a", "-1 provided not a = 0")]
168+
[InlineData("a/(-a)", "-1 provided not a = 0")]
169+
public void MakeProvided(string input, string output) => AssertSimplifyToString(input, output, 1);
164170
}
165171
}
166172

0 commit comments

Comments
 (0)