Limit over any floor or ceil overflows the stack and terminates the process. It is not an
exception a caller can catch, so a host embedding the library dies with it.
This is #704 reappearing on a new pair of
nodes, introduced by #827.
Measured on master (46140f8)
lim_x->0 (floor(x)) Stack overflow. (frame repeated 3741 times)
lim_x->2 (floor(x)) Stack overflow.
lim_x->+oo(floor(x)) Stack overflow.
lim_x->0 (ceil(x)) Stack overflow.
For contrast, the neighbours that behave:
lim_x->0 (sgn(x)) limit(sgn(x), x, 0) <- unevaluated, which is the honest answer
lim_x->0 (abs(x)) 0
The repeating frame is
AngouriMath.Entity.ExpandOnTwoAndTArguments<(Entity, ApproachFrom)>(...)
AngouriMath.Functions.Algebra.LimitFunctional.ComputeLimit(...)
AngouriMath.Core.Transformations.Transformation+LimitTransformation.ApplyCore(...)
AngouriMath.Core.Transformations.Transformation.Apply(...)
AngouriMath.Entity.Limit(...)
Cause
Entity.ComputeLimitDivideEtImpera (Limit.Definition.cs:148) has a base implementation that returns
new Limitf(this, x, dist, side) — an unevaluated limit of the very node being asked about. The
two-sided path then compares its two one-sided results by evaluating them, evaluating a limit computes
it, and control arrives back at the same node.
Floorf and Ceilf have no override, so they take that default.
This is written down already. The Signumf override
(Functions/Continuous/Limits/Solvers/Limit.Classes.cs:368-391) carries a comment naming #704 and
saying exactly this:
Nothing is returned there rather than an unevaluated limit of this very expression. That is what
used to be here, and it does not merely fail to answer [...] The recursion ends by overflowing the
stack, which kills the process rather than raising anything a caller could catch.
What the answer should be
floor is constant between consecutive integers, so where the argument tends to a value that is not
on a discontinuity the limit is floor of that value. On a discontinuity the two sides disagree and
there is nothing to say, so the answer is null — which makes the caller hand back an unevaluated
limit(...), the same as sgn does at zero.
Note that the side the argument approaches its own limit from is not the side the variable
approaches dist from, so a one-sided limit is not enough to decide the discontinuity case either.
Signumf is conservative there for the same reason.
Beyond this pair
Nothing in the repository lists the places a new Entity subtype has to be wired, and this is the
cost of that: #827 wired the node, evaluation, ToString, Latexise, ToSympy, differentiation,
InvertNode, Substitute, Sort, Domains, the grammar and tests — and missed one file, and the
result is a process kill.
A test generated by reflection over every Function subtype, asserting that a limit of it terminates,
would have caught this before merge and will catch the next one. round, trunc, min, max and
everything else proposed under #809 hits the same trap.
Limitover anyfloororceiloverflows the stack and terminates the process. It is not anexception a caller can catch, so a host embedding the library dies with it.
This is #704 reappearing on a new pair of
nodes, introduced by #827.
Measured on
master(46140f8)For contrast, the neighbours that behave:
The repeating frame is
Cause
Entity.ComputeLimitDivideEtImpera(Limit.Definition.cs:148) has a base implementation that returnsnew Limitf(this, x, dist, side)— an unevaluated limit of the very node being asked about. Thetwo-sided path then compares its two one-sided results by evaluating them, evaluating a limit computes
it, and control arrives back at the same node.
FloorfandCeilfhave no override, so they take that default.This is written down already. The
Signumfoverride(
Functions/Continuous/Limits/Solvers/Limit.Classes.cs:368-391) carries a comment naming #704 andsaying exactly this:
What the answer should be
flooris constant between consecutive integers, so where the argument tends to a value that is noton a discontinuity the limit is
floorof that value. On a discontinuity the two sides disagree andthere is nothing to say, so the answer is
null— which makes the caller hand back an unevaluatedlimit(...), the same assgndoes at zero.Note that the side the argument approaches its own limit from is not the side the variable
approaches
distfrom, so a one-sided limit is not enough to decide the discontinuity case either.Signumfis conservative there for the same reason.Beyond this pair
Nothing in the repository lists the places a new
Entitysubtype has to be wired, and this is thecost of that: #827 wired the node, evaluation,
ToString,Latexise,ToSympy, differentiation,InvertNode,Substitute,Sort,Domains, the grammar and tests — and missed one file, and theresult is a process kill.
A test generated by reflection over every
Functionsubtype, asserting that a limit of it terminates,would have caught this before merge and will catch the next one.
round,trunc,min,maxandeverything else proposed under #809 hits the same trap.