Limit over phi overflows the stack and terminates the process, the same way #829 does over
floor and ceil and #704 did over signum. Unlike those two this is not new — Phif has never had
a limit case.
Measured on master (46140f8)
lim_x->0 (phi(x)) Stack overflow. (frame repeated 3741 times)
lim_x->2 (phi(x)) Stack overflow.
lim_x->+oo(phi(x)) Stack overflow.
Cause
The same one. Entity.ComputeLimitDivideEtImpera (Limit.Definition.cs:148) defaults to returning
new Limitf(this, x, dist, side) — an unevaluated limit of the node being asked about. The two-sided
path compares its one-sided results by evaluating them, evaluating a limit computes it, and control
arrives back at the same node.
Phif is the only remaining node with this fault. I swept every concrete type that does not override
the method, each in its own process:
| Node |
Limit at 0 |
phi(x) |
Stack overflow |
x > 0, x >= 0, x < 0, x = 0 |
limit(…) unevaluated |
and, or, not, xor, implies |
limit(…) unevaluated |
x in RR |
limit(…) unevaluated |
{ x }, unite, intersect, setsubtract, [0; x] |
limit(…) unevaluated |
true, RR |
limit(…) unevaluated |
The boolean and set nodes survive the same default because the recursion needs the two-sided numeric
comparison path, which they never reach. So the rule is narrower than "every node needs an override"
and sharper than "the two nodes #827 added": every node that can carry a numeric value needs one.
What the answer should be
Euler's totient is a function on the positive integers, not on an interval. There is no neighbourhood
of a point on which it is defined other than at that point, so a limit along a real variable through
its argument is not something to answer at all — unlike floor, which is locally constant between its
jumps and can be answered there.
So null, always, which hands the caller an unevaluated limit(...).
The point this makes
Three nodes now, in three separate discoveries, with the same cause and the same fix shape. Nothing in
the repository says a new Entity subtype must decide what its limit is, and the base implementation
is a trap rather than a safe default — it does not decline, it recurses.
Two things would end this:
- A test over every concrete
ContinuousNode subtype asserting that
ComputeLimitDivideEtImpera is overridden somewhere below Entity. It is a reflection check, so
it cannot itself crash, and it forces the decision rather than checking a behaviour.
- Changing the base implementation to return
null. That is the safe default and would have made
all three of these a missing answer instead of a dead process — but it is a behaviour change for
every node that currently relies on the default, so it wants measuring rather than assuming, and
it belongs in its own change.
Related: #829, #704, and #809 for the nodes still to be added, every one of which hits this.
Limitoverphioverflows the stack and terminates the process, the same way #829 does overfloorandceiland #704 did oversignum. Unlike those two this is not new —Phifhas never hada limit case.
Measured on
master(46140f8)Cause
The same one.
Entity.ComputeLimitDivideEtImpera(Limit.Definition.cs:148) defaults to returningnew Limitf(this, x, dist, side)— an unevaluated limit of the node being asked about. The two-sidedpath compares its one-sided results by evaluating them, evaluating a limit computes it, and control
arrives back at the same node.
Phifis the only remaining node with this fault. I swept every concrete type that does not overridethe method, each in its own process:
phi(x)x > 0,x >= 0,x < 0,x = 0limit(…)unevaluatedand,or,not,xor,implieslimit(…)unevaluatedx in RRlimit(…)unevaluated{ x },unite,intersect,setsubtract,[0; x]limit(…)unevaluatedtrue,RRlimit(…)unevaluatedThe boolean and set nodes survive the same default because the recursion needs the two-sided numeric
comparison path, which they never reach. So the rule is narrower than "every node needs an override"
and sharper than "the two nodes #827 added": every node that can carry a numeric value needs one.
What the answer should be
Euler's totient is a function on the positive integers, not on an interval. There is no neighbourhood
of a point on which it is defined other than at that point, so a limit along a real variable through
its argument is not something to answer at all — unlike
floor, which is locally constant between itsjumps and can be answered there.
So
null, always, which hands the caller an unevaluatedlimit(...).The point this makes
Three nodes now, in three separate discoveries, with the same cause and the same fix shape. Nothing in
the repository says a new
Entitysubtype must decide what its limit is, and the base implementationis a trap rather than a safe default — it does not decline, it recurses.
Two things would end this:
ContinuousNodesubtype asserting thatComputeLimitDivideEtImperais overridden somewhere belowEntity. It is a reflection check, soit cannot itself crash, and it forces the decision rather than checking a behaviour.
null. That is the safe default and would have madeall three of these a missing answer instead of a dead process — but it is a behaviour change for
every node that currently relies on the default, so it wants measuring rather than assuming, and
it belongs in its own change.
Related: #829, #704, and #809 for the nodes still to be added, every one of which hits this.