Spotted by Claude:
limit(cos(1/x)/(sin(x)+2), x, inf); /* actual: und; expected: ind */
Cause:
((eq d1 '$ind)
(cond ((and (infinityp n1) (eq ($sign dn) '$pos)) (return n1))
((and (infinityp n1) (eq ($sign dn) '$neg)) (return (simpinf (m* -1 n1))))
((and (zerop1 n1) (or (eq ($sign dn) '$pos) (eq ($sign dn) '$neg))) (return 0))
(t (return '$und)))) ; <- finite nonzero n1 falls here
When the numerator limit n1 is a finite nonzero constant and the denominator limit d1 is ind (bounded oscillation), the catch-all returns und unconditionally. But a finite constant divided by a bounded, provably nonzero oscillating quantity is itself bounded, i.e. ind. The sibling ind/ind clause just above (line 945) already does exactly the right guard.
Comment by Claude:
Follow-up — before "fixing" this, note that the obvious one-liner is not safe.
The tempting fix is to just mirror the
ind/indclause above it:That last clause is a catch-all reached by three structurally different
situations, and
indis correct for only one of them.At that clause we know
d1(post-ridofab) is$ind, andn1(post-ridofab)is neither
$und(already consumed by the((or (eq d1 '$und) ...))clause) nor$ind(theind/indcase is consumed above, andridofabnever manufactures$ind). Son1is one of { finite value, an infinity, exact 0 }, and thesub-
conddispatches on which:n1finite nonzero → always falls through to the catch-all (the earlier armsgate on
infinityp/zerop1ofn1). Correct: finite / bounded-nonzero =ind. ✓ This is the reported case.
n1an infinity ($inf/$minf/$infinity) → falls through only when($sign dn)is neither$posnor$neg(otherwise theinfinityparmsfire). Correct: ∞ / bounded-nonzero = infinity, not ind.
n1exact 0 → falls through only when($sign dn)is neither$posnor$neg(otherwise thezerop1arm fires). Correct: 0 / bounded-nonzero =0, not ind.
The unconditional
(if (eq t (mnqp dn 0)) '$ind '$und)returnsindfor allthree, so it turns cases 2 and 3 into wrong answers — and worse than the
current
und, which is at least a vague "can't say" rather than a definite(wrong) "bounded".
Cases 2 and 3 are reachable with a complex denominator: then
($sign dn)is$complex(neither pos nor neg) whilemnqp(dn,0)=t. (For a real continuousdn, provably-nonzero forces a constant sign by IVT, so the pos/neg arms catchit before the catch-all.) Untested, but the routing looks like:
limit(x/(sin(x)+%i), x, inf)— should beinfinity; naive fix givesindcase 3:
limit(sin(1/x)/(sin(x)+%i), x, inf)— should be0; naive fix givesindTwo more subtleties worth recording:
Keep the
(eq t (mnqp dn 0))form; do not shorten to(if (mnqp dn 0) ...).mnqpis three-valued:t= proven ≠ 0,nil= proven = 0, otherwise a$notequal/$unknownnoun that is truthy in Lisp. A bareifwould treat"can't decide" as "nonzero" and wrongly return
ind.mnqp(dn,0)=tprovesdn ≠ 0but not thatdnis bounded away from 0. Adnwith limitindcan be provably nonzero yet have infimum 0 — e.g.exp(-x)+1+cos(x)(provably > 0, limit1+cos(x) = ind, but → 0 wherecos(x) = -1). Then even finite/dnis unbounded and the honest answer isund/infinity, notind. This heuristic gap already exists in theind/indclause, so it is pre-existing, but note that "provably nonzero ⇒ ind" is not
airtight even for case 1.
Safe minimal fix — restrict
indto the case it is valid for, leave 2 and 3 asthey are today (no regression), which still resolves the reported
cos(1/x)/(sin(x)+2)case:Optional — case 2 can be sharpened safely (a bounded-nonzero denominator cannot
stop an infinite numerator from diverging), while case 3 should stay
und(0/dnis only 0 when
dnis bounded away from 0, whichmnqpdoes not establish):(The same bounded-away-from-zero caveat applies to the
ind/indclause if anyonedecides to harden that too.)