Skip to content

Limit over phi overflows the stack, and the base limit implementation is a trap #833

Description

@Rafael-SOWNet

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:

  1. 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.
  2. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions