You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The exact algebraic layer of the generalized Ramanujan–Nagell pipeline: turn x^2 + D = k * b^n on the supported regimes into a finite, exactly computed list of branches, each of which is a binary recurrence with exact integer initial data whose terms must hit one exact integer target. No analysis, no bounds, no floating point: everything here is ideal arithmetic in the maximal order of an imaginary quadratic field plus integer recurrences. #87 bounds the branch index; #88 wires the result into the solver.
Algorithmic template: Pethő–de Weger, Products of prime powers in binary recurrence sequences I. The hyperbolic case, with an application to the generalized Ramanujan–Nagell equation, Mathematics of Computation 47 (1986), 713–727 (PethoDeWeger1986, bibliography addition below). The reduction written out here is stated in full with inline proofs — every lemma is proved in this issue, so the citation is attribution, not a load-bearing pointer (ground: proved inline; and the resulting branch enumeration was machine-checked against exhaustive search on Sage 10.7 — see "Acceptance criteria").
Concrete integers D >= 1, k >= 1, b >= 2 with gcd(k, b) = 1 and b not a perfect power (#88 performs that reduction upstream), in exactly two regimes:
(A)gcd(2*D, b) = 1 (so b is odd and coprime to D);
(B-split)b = 2 and D ≡ 7 (mod 8) (k is then odd), with n <= 2 checked directly and n >= 3 handled by the analysis below.
Which D are supported (explicitly).Every concrete D >= 1 satisfying the regime condition on b — D need not be squarefree, and D ≡ 3 (mod 4) is fully supported. The reason is the choice of ring: all ideal arithmetic happens in the maximal orderO_K of K = QQ(sqrt(-D)) = QQ(sqrt(-d0)), never in the (in general non-maximal) order ZZ[sqrt(-D)]. ZZ[sqrt(-D)] has conductor f in O_K when d0 ≢ 3 (mod 4) and conductor 2*f when d0 ≡ 3 (mod 4) (notation of §1); working in O_K means unique factorization of ideals and "to contain is to divide" are available with no conductor hypothesis at all. The non-maximality resurfaces in exactly one place — the exact coordinate target B_q = F of §6, stated in the ZZ-basis of O_K — and nowhere in the hypotheses. The D ≡ 3 (mod 4) half-integer basis is what makes F = 2*f/delta rather than f/delta, and it is also what makes the (B-split) half-integer factorization legal in the first place.
The b = 2, D ≢ 7 (mod 8) cases (odd and even D) are #85's elementary regimes and are not treated here. Everything else (b > 2 sharing an odd prime with D, gcd(k, b) > 1, D < 1, non-concrete parameters) stays on #88's fallback.
Algorithm and conventions
1. Setup and notation (all exact)
D = f^2 * d0 with d0 squarefree (d0 = ZZ(D).squarefree_part(), f = ZZ(D // d0).isqrt()).
K = QQ(sqrt(-d0)), maximal order O_K = ZZ[omega] with
F := 2*f/delta if d0 ≡ 3 (mod 4), else F := f/delta (in the second case delta = 1, so F = f); F is a positive integer in every supported case.
alpha lies in O_K, and the norm equation.
Regime (A): alpha = x + f*sqrt(-d0) ∈ O_K trivially, and alpha*alphabar = x^2 + D = k*b^n = k*b^m.
Regime (B-split): for n >= 3, x^2 = k*2^n - D ≡ -D ≡ 1 (mod 8), so x is odd; f is odd too (D odd), and d0 ≡ D ≡ 7 (mod 8) (because f^2 ≡ 1 (mod 8)), so d0 ≡ 3 (mod 4) and omega = (1 + sqrt(-d0))/2. Then
alpha = (x + f*sqrt(-d0))/2 = (x - f)/2 + f*omega ∈ O_K ((x - f)/2 ∈ ZZ since x, f are both odd),
alpha*alphabar = (x^2 + D)/4 = k*2^(n-2) = k*b^m.
n ∈ {0, 1, 2} is checked directly (exact square test), so n >= 3 may be assumed.
Also disc(K) = -d0 ≡ 1 (mod 8), so 2 splits in K — the fact that makes this regime work at all.
So in both regimes, in O_K:
(alpha) * (alphabar) = (k) * (b)^m, m = n - s >= 0.
2. The ideal-gcd lemma (stated and proved inline)
Lemma IG. Let g := (alpha) + (alphabar) (the ideal gcd of the two principal ideals) in the Dedekind domain O_K. Then g divides both (alpha + alphabar) and (alpha - alphabar) = (beta). In particular
regime (A): g | (2*sqrt(-D));
regime (B-split): g | (sqrt(-D)).
Proof.g contains alpha and alphabar, hence contains their sum and their difference; in a Dedekind domain an ideal containing an element divides the principal ideal it generates ("to contain is to divide"). beta = (2/delta)*sqrt(-D) is 2*sqrt(-D) when delta = 1 and sqrt(-D) when delta = 2. ∎
Corollary IG1 (the exact consequence: g is coprime to b). In both supported regimes g + (b) = (1).
Proof. Regime (A): suppose a prime P of O_K divides both g and (b), and let p be the rational prime under P, so p | b; gcd(2*D, b) = 1 gives p odd and p ∤ D. By Lemma IG, P | (2)*(sqrt(-D)), so P | (2) or P | (sqrt(-D)) (P prime). P | (2) forces p = 2, excluded. P | (sqrt(-D)) gives P | (sqrt(-D))^2 = (D), hence p | N(P) | N((D)) = D^2, so p | D, excluded. ∎
Regime (B-split): g | (sqrt(-D)) gives N(g) | N((sqrt(-D))) = D, which is odd, so no prime of g lies over 2 = b. ∎
(Both regimes also give g | (2*x/delta) from the sum, which is not needed below.)
Corollary IG2 (no ramification over b). Every prime p | b is unramified in K. Proof.disc(K) ∈ {-d0, -4*d0} divides 4*d0. Regime (A): gcd(2*d0, b) = 1 since d0 | D, so gcd(disc(K), b) = 1. Regime (B-split): b = 2 and disc(K) = -d0 ≡ 1 (mod 8), so 2 is unramified (indeed split). Unramified ⟺ p ∤ disc(K). ∎
3. Inert primes kill every positive exponent
Lemma INERT. If some p | b is inert in K, then every solution has m = 0 (i.e. n = s).
Proof.P := (p) is prime in O_K and stable under conjugation, so v_P(alpha) = v_P(alphabar). By IG1, min(v_P(alpha), v_P(alphabar)) = v_P(g) = 0, hence both vanish. But v_P(alpha) + v_P(alphabar) = v_P((k)*(b)^m) = v_p(k) + m*v_p(b) = m*v_p(b) (using gcd(k, b) = 1), and v_p(b) >= 1, so m = 0. ∎
Implementation: detect this with len(K.primes_above(p)) == 1 for some p | b (with IG2 this means inert), emit the finitely many direct checks for n = s (and, in regime (B-split), n ∈ {0, 1, 2}), and record Evidence(kind="structural", ...) with the argument above. This is a complete proof of the whole instance; no branch enumeration is needed.
4. The b-part splits: branch data
Assume from now on that everyp | b splits: (p) = P_p * Pbar_p with P_p != Pbar_p. A splitting pattern is a choice Q_p ∈ {P_p, Pbar_p} for each p | b; write b = prod_p p^(a_p) and
C := prod_{p | b} Q_p^(a_p), N(C) = b, C * Cbar = (b).
Lemma SPLIT. For every solution there is a unique splitting pattern and a unique ideal A of O_K with
(alpha) = A * C^m, A * Abar = (k), A coprime to (b).
Proof. Write (alpha) = B_alpha * A with B_alpha the part supported on primes over b and A the part coprime to (b). Conjugating, (alphabar) = Bbar_alpha * Abar. Multiplying and comparing the (b)-supported and (b)-coprime parts of (k)*(b)^m separately (legitimate because gcd(k, b) = 1): B_alpha * Bbar_alpha = (b)^m and A * Abar = (k). For each p | b, v_{P_p}(alpha) + v_{P_p}(alphabar) = m*a_p and min(v_{P_p}(alpha), v_{P_p}(alphabar)) = 0 by IG1, so one of the two is m*a_p and the other 0; setting Q_p := the prime of {P_p, Pbar_p} at which alpha carries the full exponent gives B_alpha = C^m (the choices at P_p and Pbar_p are consistent because v_{Pbar_p}(alpha) = v_{P_p}(alphabar)). ∎
Lemma CLASS. Fix a pattern and an A with A*Abar = (k). Let t := ord([C]) in the class group Cl(K) (so t | h := |Cl(K)|). If there is no r ∈ {0, ..., t-1} with [A]*[C]^r = 1, the branch is provably empty. Otherwise that r is unique, every solution of this branch has m ≡ r (mod t), and writing m = t*q + r with q >= 0:
alpha = u * tau * mu^q, tau := a generator of A*C^r, mu := a generator of C^t, u ∈ O_K^*.
Proof.(alpha) = A*C^m is principal, so [A]*[C]^m = 1 in Cl(K); [C]^m depends only on m mod t, which gives existence/uniqueness of r and the congruence. A*C^r and C^t are principal by construction, and A*C^m = (A*C^r)*(C^t)^q, so (alpha) = (tau*mu^q); two generators of the same principal ideal differ by a unit. m >= 0 and m ≡ r (mod t) with 0 <= r < t force q >= 0. ∎
Units.K is imaginary quadratic, so O_K^* is exactly the group of roots of unity in K: {±1} for d0 ∉ {1, 3}, {±1, ±i} for d0 = 1, the six sixth roots of unity for d0 = 3. Write w := |O_K^*| ∈ {2, 4, 6} (K.unit_group().zeta_order(); generator K.unit_group().torsion_generator()).
A branch is therefore the tuple (pattern, A, r, t, tau, mu, u). tau and mu are only determined up to units, but u ranges over all of O_K^*, so any deterministic choice of generator is sound; pin Sage's gens_reduced()[0] for reproducibility.
Lemma FINITE (branch count). The number of branches is at most
2^omega(b) * sigma_0((k)) * w,
where omega(b) is the number of distinct primes dividing b and sigma_0((k)) is the number of ideal divisors of (k) in O_K (both exactly computable; sigma_0((k)) <= prod_{p^e || k} (2*e + 1)). Proof. At most 2^omega(b) patterns; A | A*Abar = (k) so A is one of the sigma_0((k)) ideal divisors of (k); at most one r per (pattern, A) by Lemma CLASS; w units. ∎ The class number enters only through t | h, which fixes the arithmetic progression m ≡ r (mod t) — it does not multiply the branch count.
5. Exact per-branch data consumed downstream
For each branch, with T := Tr(mu) = mu + mubar ∈ ZZ and N := N(mu) = mu*mubar ∈ ZZ:
N = N(C)^t = b^t (positive; K is imaginary quadratic);
N(tau) = N(A) * N(C)^r = k * b^r, hence |mu| = b^(t/2) and |tau| = sqrt(k*b^r);
v_{Q_p}(mu) = t*a_p > 0 and v_{Qbar_p}(mu) = 0; v_{Q_p}(tau) = r*a_p and v_{Qbar_p}(tau) = 0;
p ∤ T for every p | b.Proof.v_{Qbar_p}(mu) = 0 and v_{Qbar_p}(mubar) = v_{Q_p}(mu) = t*a_p >= 1, so v_{Qbar_p}(T) = v_{Qbar_p}(mu + mubar) = min(0, t*a_p) = 0, while p | T would give v_{Qbar_p}(T) >= v_{Qbar_p}(p) = 1. ∎ (Explicit analytic bounds and certified reductions for Ramanujan–Nagell #87 needs exactly this to represent mu p-adically.)
6. The recurrence and the target normalization
Write w_q := u * tau * mu^q = A_q + B_q * omega with A_q, B_q ∈ ZZ (coordinates in the ZZ-basis (1, omega) of O_K). Since mu^2 = T*mu - N:
with exact initial data (A_0, B_0) := coordinates of u*tau and (A_1, B_1) := coordinates of u*tau*mu. This is the Lucas-sequence pair of the branch: both components run on the companion recurrence of mu's minimal polynomial x^2 - T*x + N, and they differ only in their initial data. (Proof: w_{q+2} = w_q * mu^2 = T*w_{q+1} - N*w_q; taking coordinates is ZZ-linear. ∎)
Target-power normalization. With m = t*q + r and n = m + s:
(x, n) is a solution arising from this branch at index q <=> B_q = F,
and then x = delta * A_q + (f if d0 ≡ 3 (mod 4) else 0).
Proof.alpha = x/delta + (f/delta)*sqrt(-d0). If d0 ≡ 3 (mod 4), A_q + B_q*omega = (A_q + B_q/2) + (B_q/2)*sqrt(-d0), so B_q/2 = f/delta — i.e. B_q = 2*f/delta = F — and x/delta = A_q + B_q/2 = A_q + f/delta, i.e. x = delta*A_q + f. If d0 ≢ 3 (mod 4) then delta = 1 and A_q + B_q*sqrt(-d0) = x + f*sqrt(-d0) gives B_q = f = F and x = A_q. Conversely, B_q = F makes w_q of exactly that shape, and w_q * conj(w_q) = N(u*tau)*N(mu)^q = k*b^(r + t*q) = k*b^m reproduces the equation. ∎
Relation to the Pethő–de Weger shape.PethoDeWeger1986 studies terms of a binary recurrence that equal a prescribed product of prime powers. The normalization above is exactly that shape with the b-power absorbed into the index: the target is the constantF, and the prime power reappears as N = b^t inside the recurrence and as n = t*q + r + s in the exponent. This is also the shape of the classical Nagell argument (B_q = ±1 for the (7, 1, 2) sequence).
7. Pinned Sage realization (verified on Sage 10.7)
d0 = ZZ(D).squarefree_part(), f = ZZ(D // d0).isqrt() (f^2*d0 == D asserted).
K = QuadraticField(-d0, 'a'); K.class_group(), K.unit_group().zeta_order(), K.unit_group().torsion_generator().
Ideal divisors of (k): expand K.ideal(k).factor(); keep A iff A * A.apply_morphism(K.hom([-K.gen()])) == K.ideal(k). Note: in Sage 10.7 a NumberFieldFractionalIdeal has no.conjugate() method — conjugation must go through apply_morphism with the nontrivial automorphism (verified; the naive .conjugate() call raises AttributeError).
t = K.class_group()(C).order(); mu = (C**t).gens_reduced()[0]; tau = (A * C**r).gens_reduced()[0]; the identities K.ideal(mu) == C**t and K.ideal(tau) == A*C**r are asserted.
Coordinates: list(K(z)) returns (z0, z1) with z = z0 + z1*sqrt(-d0); then (A_q, B_q) = (z0 - z1, 2*z1) if d0 % 4 == 3, else (z0, z1).
T = mu.trace(), N = mu.norm() (asserted equal to b^t).
Output and API contract
A private, purely algebraic helper in solvers.py (no public API change):
@dataclass(frozen=True)class_RNBranch:
pattern: tuple# ((p, a_p, Q_p), ...) — the chosen prime over each p | bideal_A: object# the O_K-ideal A with A*Abar = (k)r: ZZ# m ≡ r (mod t)t: ZZ# order of [C] in Cl(K)tau: object# generator of A*C^r (exact K element)mu: object# generator of C^t (exact K element)unit: object# u ∈ O_K^* (exact K element)trace: ZZ# T = Tr(mu)norm: ZZ# N = N(mu) = b^tinit: tuple# ((A_0, B_0), (A_1, B_1)) — exact ZZ coordinates_rn_setup(D, k, b) ->sharedrecord (d0, f, K, omega, delta, shift, F, beta,
class_number, w, regime)
_rn_branches(setup) ->tupleof_RNBranch, ortheINERTverdict
Every numeric field is an exact Sage ZZ; every algebraic field is an exact K element. No floats, no balls, no p-adics in this child.
Deterministic: the pattern order is the factorization order of b with K.primes_above(p) order; the A order is the divisor-expansion order of K.ideal(k).factor(); the unit order is zeta^j, j = 0 .. w-1. The tuple is reproducible bit for bit across runs (locked by the fixtures below).
Empty branches (no r) are dropped from the returned tuple, and their count is reported so Ramanujan–Nagell solver integration and regression fixtures #88 can put it in the structural evidence text (the sentence "all N branches are provably empty", with the exact count substituted, is itself a complete proof of kind="empty").
The INERT verdict carries the offending prime p and the argument text for Evidence(kind="structural", ...).
Failure and fallback semantics
Out-of-regime inputs never reach this child (Ramanujan–Nagell solver integration and regression fixtures #88's guards); the helper asserts its preconditions and raises ValueError if they are violated — an internal-consistency failure, not a user-facing outcome.
This child never returns partial data and never decides completeness: it either produces the full finite branch list (or the INERT/all-empty verdict) or fails typed.
Acceptance criteria
Every table below was produced by the specification above and cross-checked against exhaustive search (k*b^n - D tested for squareness with exact integer arithmetic) on Sage 10.7; the union of the branch hits equals the brute-force solution set in each case.
Fixture (D, k, b) = (7, 1, 2) (regime B-split, h = 1).d0 = 7, f = 1, delta = 2, s = 2, F = 1, beta = sqrt(-7), h = 1, w = 2, sigma_0((1)) = 1, #A = 1, t = 1, r = 0, tau = 1, 4 branches, each with T = 1, N = 2:
Q_2
mu
u
(A_0, B_0)
(A_1, B_1)
hits (q, x, n)
((1-sqrt(-7))/2)
(1-sqrt(-7))/2
1
(1, 0)
(1, -1)
(3,-5,5), (5,11,7), (13,-181,15)
((1-sqrt(-7))/2)
(1-sqrt(-7))/2
-1
(-1, 0)
(-1, 1)
(1,-1,3), (2,3,4)
((1+sqrt(-7))/2)
(1+sqrt(-7))/2
1
(1, 0)
(0, 1)
(1,1,3), (2,-3,4)
((1+sqrt(-7))/2)
(1+sqrt(-7))/2
-1
(-1, 0)
(0, -1)
(3,5,5), (5,-11,7), (13,181,15)
Union of (|x|, n): exactly {(1, 3), (3, 4), (5, 5), (11, 7), (181, 15)} — it must agree with the hardwired Nagell 1961 answer, and the test asserts that equality. (mu's minimal polynomial is x^2 - x + 2.)
Fixture (D, k, b) = (5, 1, 3) (regime A, class number h = 2).d0 = 5, f = 1, delta = 1, s = 0, F = 1, beta = 2*sqrt(-5), w = 2, #A = 1, t = 2, r = 0, tau = 1, 4 branches, each with T = -4, N = 9:
Q_3
mu
u
(A_0, B_0)
(A_1, B_1)
hits (q, x, n)
(3, a+1)
a - 2
1
(1, 0)
(-2, 1)
(1, -2, 2)
(3, a+1)
a - 2
-1
(-1, 0)
(2, -1)
—
(3, a+2)
-a - 2
1
(1, 0)
(-2, -1)
—
(3, a+2)
-a - 2
-1
(-1, 0)
(2, 1)
(1, 2, 2)
(a = sqrt(-5); mu's minimal polynomial is x^2 + 4*x + 9.) Union: exactly {(2, 2)}, matching brute force for n <= 300. This fixture locks the class-number->1 behaviour: [C_3] has order t = 2, so only even n can occur and the odd-n half of the problem is closed by the "non-principal ⟹ provably empty" step, not by search.
Fixture (D, k, b) = (2, 2, 3) (regime A, k > 1, non-trivial tau).d0 = 2, f = 1, delta = 1, s = 0, F = 1, beta = 2*sqrt(-2), h = 1, w = 2, sigma_0((2)) = 3, #A = 1 (the ramified A = (sqrt(-2)), so tau = sqrt(-2) != 1), t = 1, r = 0, 4 branches, each with T = -2, N = 3:
Q_3
mu
u
(A_0, B_0)
(A_1, B_1)
hits (q, x, n)
(-a-1)
-a - 1
1
(0, 1)
(2, -1)
(0, 0, 0)
(-a-1)
-a - 1
-1
(0, -1)
(-2, 1)
(1,-2,1), (2,4,2), (5,22,5)
(a-1)
a - 1
1
(0, 1)
(-2, -1)
(0, 0, 0)
(a-1)
a - 1
-1
(0, -1)
(2, 1)
(1,2,1), (2,-4,2), (5,-22,5)
Union: exactly {(0, 0), (2, 1), (4, 2), (22, 5)}, matching brute force for n <= 60.
Inert regression.(D, k, b) = (1, 1, 3): 3 is inert in QQ(i), so Lemma INERT applies and the complete answer is the direct check at n = 0, namely {(0, 0)} (brute force for n <= 300 agrees). Same for (D, k, b) = (3, 4, 5) (5 inert in QQ(sqrt(-3)), answer {(1, 0)}) and (7, 1, 3), (9, 4, 7), (12, 1, 5) (answer empty).
All-branches-empty regression.(D, k, b) = (6, 5, 7): h = 2, #A = 2, sigma_0((5)) = 4, and no(pattern, A) pair admits a residue r — every branch is provably empty, so the instance is proved to have no solutions at all (kind="empty", completeness="proved"), with no search involved. Brute force for n <= 40 agrees. Same verdict, with non-empty branch lists this time, for (14, 9, 5) (h = 4, sigma_0((9)) = 9, #A = 3, 12 branches, no hits) and (2, 9, 11) (h = 1, sigma_0((9)) = 9, #A = 3, 12 branches, no hits).
F != 1 regression.(11, 1, 3): d0 = 11 ≡ 3 (mod 4), f = 1, so F = 2; 4 branches; answer {(4, 3)}. (4, 1, 5): d0 = 1, f = 2, so F = 2 and w = 4 (8 branches); answer {(1, 1), (11, 3)}. (1, 1, 5): w = 4, 8 branches, answer {(0, 0), (2, 1)}. These lock the f > 1 and d0 ≡ 3 (mod 4) coordinate targets and the w ∈ {4, 6} unit sets.
Branch-count bound. For every fixture above the test asserts len(branches) <= 2^omega(b) * sigma_0((k)) * w (Lemma FINITE).
Determinism. The branch tuple (including generator choices and ordering) is reproduced bit for bit on repeated runs; the tables above are committed as the expected values.
Bibliography: add PethoDeWeger1986 to data/references.bib — Attila Pethő, Benjamin M. M. de Weger, Products of prime powers in binary recurrence sequences I. The hyperbolic case, with an application to the generalized Ramanujan–Nagell equation, Mathematics of Computation, volume 47, 1986, pages 713–727 (tools/check_references.py's REQUIRED_FIELDS are author, title, year — all pinned here, together with journal, volume and pages. The optional doi/url fields are not pinned in this issue and are not load-bearing: add them only if verified at commit time by tools/check_references.py with network access, and add a url only if it points to a legally free copy.) Add the key to the ramanujan-nagell family YAML with why = "the binary-recurrence reduction of the generalized Ramanujan–Nagell equation implemented by the branch enumeration". make references and make registry-docs must pass.
Every function (private helpers included) has a Sage-convention docstring with INPUT/OUTPUT and EXAMPLES passing sage -t; make test, make doctest, make coverage (docstring coverage stays 100%) all clean.
Non-maximal orders, ring class fields, and D-dependent unit machinery beyond the roots of unity of an imaginary quadratic field: none of these is needed, and none is introduced.
Goal
The exact algebraic layer of the generalized Ramanujan–Nagell pipeline: turn
x^2 + D = k * b^non the supported regimes into a finite, exactly computed list of branches, each of which is a binary recurrence with exact integer initial data whose terms must hit one exact integer target. No analysis, no bounds, no floating point: everything here is ideal arithmetic in the maximal order of an imaginary quadratic field plus integer recurrences. #87 bounds the branch index; #88 wires the result into the solver.Algorithmic template: Pethő–de Weger, Products of prime powers in binary recurrence sequences I. The hyperbolic case, with an application to the generalized Ramanujan–Nagell equation, Mathematics of Computation 47 (1986), 713–727 (
PethoDeWeger1986, bibliography addition below). The reduction written out here is stated in full with inline proofs — every lemma is proved in this issue, so the citation is attribution, not a load-bearing pointer (ground: proved inline; and the resulting branch enumeration was machine-checked against exhaustive search on Sage 10.7 — see "Acceptance criteria").Target base
review-architecture@85f8e0a(the composedtree on the
roed-mathfork: wave-1 code = PRs Packaging, CI, design notes, and the annotated bibliography #2–Family: waring — Waring-type diagonal representation #48, plus the wave-2 andarchitecture-review series, which are not yet opened as PRs).
must land after them before this issue's work can merge to
main.Implementation happens on the composed tree, not on a split branch.
Dependencies
q), Ramanujan–Nagell solver integration and regression fixtures #88 (which dispatches).Supported inputs
Concrete integers
D >= 1,k >= 1,b >= 2withgcd(k, b) = 1andbnot a perfect power (#88 performs that reduction upstream), in exactly two regimes:gcd(2*D, b) = 1(sobis odd and coprime toD);b = 2andD ≡ 7 (mod 8)(kis then odd), withn <= 2checked directly andn >= 3handled by the analysis below.Which
Dare supported (explicitly). Every concreteD >= 1satisfying the regime condition onb—Dneed not be squarefree, andD ≡ 3 (mod 4)is fully supported. The reason is the choice of ring: all ideal arithmetic happens in the maximal orderO_KofK = QQ(sqrt(-D)) = QQ(sqrt(-d0)), never in the (in general non-maximal) orderZZ[sqrt(-D)].ZZ[sqrt(-D)]has conductorfinO_Kwhend0 ≢ 3 (mod 4)and conductor2*fwhend0 ≡ 3 (mod 4)(notation of §1); working inO_Kmeans unique factorization of ideals and "to contain is to divide" are available with no conductor hypothesis at all. The non-maximality resurfaces in exactly one place — the exact coordinate targetB_q = Fof §6, stated in theZZ-basis ofO_K— and nowhere in the hypotheses. TheD ≡ 3 (mod 4)half-integer basis is what makesF = 2*f/deltarather thanf/delta, and it is also what makes the (B-split) half-integer factorization legal in the first place.The b = 2, D ≢ 7 (mod 8) cases (odd and even
D) are #85's elementary regimes and are not treated here. Everything else (b > 2sharing an odd prime withD,gcd(k, b) > 1,D < 1, non-concrete parameters) stays on #88's fallback.Algorithm and conventions
1. Setup and notation (all exact)
D = f^2 * d0withd0squarefree (d0 = ZZ(D).squarefree_part(),f = ZZ(D // d0).isqrt()).K = QQ(sqrt(-d0)), maximal orderO_K = ZZ[omega]with(1, omega)is aZZ-basis ofO_K; every element below is recorded by its exact coordinates in it.Regime constants:
alpha := (x + sqrt(-D))/delta,alphabaritsK/QQ-conjugate, andF := 2*f/deltaifd0 ≡ 3 (mod 4), elseF := f/delta(in the second casedelta = 1, soF = f);Fis a positive integer in every supported case.alphalies inO_K, and the norm equation.Regime (A):
alpha = x + f*sqrt(-d0) ∈ O_Ktrivially, andalpha*alphabar = x^2 + D = k*b^n = k*b^m.Regime (B-split): for
n >= 3,x^2 = k*2^n - D ≡ -D ≡ 1 (mod 8), soxis odd;fis odd too (Dodd), andd0 ≡ D ≡ 7 (mod 8)(becausef^2 ≡ 1 (mod 8)), sod0 ≡ 3 (mod 4)andomega = (1 + sqrt(-d0))/2. Thenn ∈ {0, 1, 2}is checked directly (exact square test), son >= 3may be assumed.Also
disc(K) = -d0 ≡ 1 (mod 8), so 2 splits inK— the fact that makes this regime work at all.So in both regimes, in
O_K:2. The ideal-gcd lemma (stated and proved inline)
Lemma IG. Let
g := (alpha) + (alphabar)(the ideal gcd of the two principal ideals) in the Dedekind domainO_K. Thengdivides both(alpha + alphabar)and(alpha - alphabar) = (beta). In particularProof.
gcontainsalphaandalphabar, hence contains their sum and their difference; in a Dedekind domain an ideal containing an element divides the principal ideal it generates ("to contain is to divide").beta = (2/delta)*sqrt(-D)is2*sqrt(-D)whendelta = 1andsqrt(-D)whendelta = 2. ∎Corollary IG1 (the exact consequence:
gis coprime tob). In both supported regimesg + (b) = (1).Proof. Regime (A): suppose a prime
PofO_Kdivides bothgand(b), and letpbe the rational prime underP, sop | b;gcd(2*D, b) = 1givespodd andp ∤ D. By Lemma IG,P | (2)*(sqrt(-D)), soP | (2)orP | (sqrt(-D))(Pprime).P | (2)forcesp = 2, excluded.P | (sqrt(-D))givesP | (sqrt(-D))^2 = (D), hencep | N(P) | N((D)) = D^2, sop | D, excluded. ∎Regime (B-split):
g | (sqrt(-D))givesN(g) | N((sqrt(-D))) = D, which is odd, so no prime ofglies over2 = b. ∎(Both regimes also give
g | (2*x/delta)from the sum, which is not needed below.)Corollary IG2 (no ramification over
b). Every primep | bis unramified inK. Proof.disc(K) ∈ {-d0, -4*d0}divides4*d0. Regime (A):gcd(2*d0, b) = 1sinced0 | D, sogcd(disc(K), b) = 1. Regime (B-split):b = 2anddisc(K) = -d0 ≡ 1 (mod 8), so2is unramified (indeed split). Unramified ⟺p ∤ disc(K). ∎3. Inert primes kill every positive exponent
Lemma INERT. If some
p | bis inert inK, then every solution hasm = 0(i.e.n = s).Proof.
P := (p)is prime inO_Kand stable under conjugation, sov_P(alpha) = v_P(alphabar). By IG1,min(v_P(alpha), v_P(alphabar)) = v_P(g) = 0, hence both vanish. Butv_P(alpha) + v_P(alphabar) = v_P((k)*(b)^m) = v_p(k) + m*v_p(b) = m*v_p(b)(usinggcd(k, b) = 1), andv_p(b) >= 1, som = 0. ∎Implementation: detect this with
len(K.primes_above(p)) == 1for somep | b(with IG2 this means inert), emit the finitely many direct checks forn = s(and, in regime (B-split),n ∈ {0, 1, 2}), and recordEvidence(kind="structural", ...)with the argument above. This is a complete proof of the whole instance; no branch enumeration is needed.4. The
b-part splits: branch dataAssume from now on that every
p | bsplits:(p) = P_p * Pbar_pwithP_p != Pbar_p. A splitting pattern is a choiceQ_p ∈ {P_p, Pbar_p}for eachp | b; writeb = prod_p p^(a_p)andLemma SPLIT. For every solution there is a unique splitting pattern and a unique ideal
AofO_KwithProof. Write
(alpha) = B_alpha * AwithB_alphathe part supported on primes overbandAthe part coprime to(b). Conjugating,(alphabar) = Bbar_alpha * Abar. Multiplying and comparing the(b)-supported and(b)-coprime parts of(k)*(b)^mseparately (legitimate becausegcd(k, b) = 1):B_alpha * Bbar_alpha = (b)^mandA * Abar = (k). For eachp | b,v_{P_p}(alpha) + v_{P_p}(alphabar) = m*a_pandmin(v_{P_p}(alpha), v_{P_p}(alphabar)) = 0by IG1, so one of the two ism*a_pand the other0; settingQ_p :=the prime of{P_p, Pbar_p}at whichalphacarries the full exponent givesB_alpha = C^m(the choices atP_pandPbar_pare consistent becausev_{Pbar_p}(alpha) = v_{P_p}(alphabar)). ∎Lemma CLASS. Fix a pattern and an
AwithA*Abar = (k). Lett := ord([C])in the class groupCl(K)(sot | h := |Cl(K)|). If there is nor ∈ {0, ..., t-1}with[A]*[C]^r = 1, the branch is provably empty. Otherwise thatris unique, every solution of this branch hasm ≡ r (mod t), and writingm = t*q + rwithq >= 0:Proof.
(alpha) = A*C^mis principal, so[A]*[C]^m = 1inCl(K);[C]^mdepends only onm mod t, which gives existence/uniqueness ofrand the congruence.A*C^randC^tare principal by construction, andA*C^m = (A*C^r)*(C^t)^q, so(alpha) = (tau*mu^q); two generators of the same principal ideal differ by a unit.m >= 0andm ≡ r (mod t)with0 <= r < tforceq >= 0. ∎Units.
Kis imaginary quadratic, soO_K^*is exactly the group of roots of unity inK:{±1}ford0 ∉ {1, 3},{±1, ±i}ford0 = 1, the six sixth roots of unity ford0 = 3. Writew := |O_K^*| ∈ {2, 4, 6}(K.unit_group().zeta_order(); generatorK.unit_group().torsion_generator()).A branch is therefore the tuple
(pattern, A, r, t, tau, mu, u).tauandmuare only determined up to units, buturanges over all ofO_K^*, so any deterministic choice of generator is sound; pin Sage'sgens_reduced()[0]for reproducibility.Lemma FINITE (branch count). The number of branches is at most
where
omega(b)is the number of distinct primes dividingbandsigma_0((k))is the number of ideal divisors of(k)inO_K(both exactly computable;sigma_0((k)) <= prod_{p^e || k} (2*e + 1)). Proof. At most2^omega(b)patterns;A | A*Abar = (k)soAis one of thesigma_0((k))ideal divisors of(k); at most onerper(pattern, A)by Lemma CLASS;wunits. ∎ The class number enters only throught | h, which fixes the arithmetic progressionm ≡ r (mod t)— it does not multiply the branch count.5. Exact per-branch data consumed downstream
For each branch, with
T := Tr(mu) = mu + mubar ∈ ZZandN := N(mu) = mu*mubar ∈ ZZ:N = N(C)^t = b^t(positive;Kis imaginary quadratic);N(tau) = N(A) * N(C)^r = k * b^r, hence|mu| = b^(t/2)and|tau| = sqrt(k*b^r);v_{Q_p}(mu) = t*a_p > 0andv_{Qbar_p}(mu) = 0;v_{Q_p}(tau) = r*a_pandv_{Qbar_p}(tau) = 0;p ∤ Tfor everyp | b. Proof.v_{Qbar_p}(mu) = 0andv_{Qbar_p}(mubar) = v_{Q_p}(mu) = t*a_p >= 1, sov_{Qbar_p}(T) = v_{Qbar_p}(mu + mubar) = min(0, t*a_p) = 0, whilep | Twould givev_{Qbar_p}(T) >= v_{Qbar_p}(p) = 1. ∎ (Explicit analytic bounds and certified reductions for Ramanujan–Nagell #87 needs exactly this to representmup-adically.)6. The recurrence and the target normalization
Write
w_q := u * tau * mu^q = A_q + B_q * omegawithA_q, B_q ∈ ZZ(coordinates in theZZ-basis(1, omega)ofO_K). Sincemu^2 = T*mu - N:with exact initial data
(A_0, B_0) :=coordinates ofu*tauand(A_1, B_1) :=coordinates ofu*tau*mu. This is the Lucas-sequence pair of the branch: both components run on the companion recurrence ofmu's minimal polynomialx^2 - T*x + N, and they differ only in their initial data. (Proof:w_{q+2} = w_q * mu^2 = T*w_{q+1} - N*w_q; taking coordinates isZZ-linear. ∎)Target-power normalization. With
m = t*q + randn = m + s:Proof.
alpha = x/delta + (f/delta)*sqrt(-d0). Ifd0 ≡ 3 (mod 4),A_q + B_q*omega = (A_q + B_q/2) + (B_q/2)*sqrt(-d0), soB_q/2 = f/delta— i.e.B_q = 2*f/delta = F— andx/delta = A_q + B_q/2 = A_q + f/delta, i.e.x = delta*A_q + f. Ifd0 ≢ 3 (mod 4)thendelta = 1andA_q + B_q*sqrt(-d0) = x + f*sqrt(-d0)givesB_q = f = Fandx = A_q. Conversely,B_q = Fmakesw_qof exactly that shape, andw_q * conj(w_q) = N(u*tau)*N(mu)^q = k*b^(r + t*q) = k*b^mreproduces the equation. ∎Relation to the Pethő–de Weger shape.
PethoDeWeger1986studies terms of a binary recurrence that equal a prescribed product of prime powers. The normalization above is exactly that shape with theb-power absorbed into the index: the target is the constantF, and the prime power reappears asN = b^tinside the recurrence and asn = t*q + r + sin the exponent. This is also the shape of the classical Nagell argument (B_q = ±1for the(7, 1, 2)sequence).7. Pinned Sage realization (verified on Sage 10.7)
d0 = ZZ(D).squarefree_part(),f = ZZ(D // d0).isqrt()(f^2*d0 == Dasserted).K = QuadraticField(-d0, 'a');K.class_group(),K.unit_group().zeta_order(),K.unit_group().torsion_generator().K.primes_above(p): length1⟺ inert (ramification excluded by IG2), length2⟺ split.(k): expandK.ideal(k).factor(); keepAiffA * A.apply_morphism(K.hom([-K.gen()])) == K.ideal(k). Note: in Sage 10.7 aNumberFieldFractionalIdealhas no.conjugate()method — conjugation must go throughapply_morphismwith the nontrivial automorphism (verified; the naive.conjugate()call raisesAttributeError).t = K.class_group()(C).order();mu = (C**t).gens_reduced()[0];tau = (A * C**r).gens_reduced()[0]; the identitiesK.ideal(mu) == C**tandK.ideal(tau) == A*C**rare asserted.list(K(z))returns(z0, z1)withz = z0 + z1*sqrt(-d0); then(A_q, B_q) = (z0 - z1, 2*z1)ifd0 % 4 == 3, else(z0, z1).T = mu.trace(),N = mu.norm()(asserted equal tob^t).Output and API contract
A private, purely algebraic helper in
solvers.py(no public API change):ZZ; every algebraic field is an exactKelement. No floats, no balls, no p-adics in this child.bwithK.primes_above(p)order; theAorder is the divisor-expansion order ofK.ideal(k).factor(); the unit order iszeta^j,j = 0 .. w-1. The tuple is reproducible bit for bit across runs (locked by the fixtures below).no r) are dropped from the returned tuple, and their count is reported so Ramanujan–Nagell solver integration and regression fixtures #88 can put it in the structural evidence text (the sentence "all N branches are provably empty", with the exact count substituted, is itself a complete proof ofkind="empty").pand the argument text forEvidence(kind="structural", ...).Failure and fallback semantics
ValueErrorif they are violated — an internal-consistency failure, not a user-facing outcome.gens_reduced) surface asoutcome="cas-failure"per the existing vocabulary, raised by Ramanujan–Nagell solver integration and regression fixtures #88's wrapper.Acceptance criteria
Every table below was produced by the specification above and cross-checked against exhaustive search (
k*b^n - Dtested for squareness with exact integer arithmetic) on Sage 10.7; the union of the branch hits equals the brute-force solution set in each case.Fixture
(D, k, b) = (7, 1, 2)(regime B-split,h = 1).d0 = 7,f = 1,delta = 2,s = 2,F = 1,beta = sqrt(-7),h = 1,w = 2,sigma_0((1)) = 1,#A = 1,t = 1,r = 0,tau = 1, 4 branches, each withT = 1,N = 2:Q_2muu(A_0, B_0)(A_1, B_1)(q, x, n)((1-sqrt(-7))/2)(1-sqrt(-7))/21(1, 0)(1, -1)(3,-5,5), (5,11,7), (13,-181,15)((1-sqrt(-7))/2)(1-sqrt(-7))/2-1(-1, 0)(-1, 1)(1,-1,3), (2,3,4)((1+sqrt(-7))/2)(1+sqrt(-7))/21(1, 0)(0, 1)(1,1,3), (2,-3,4)((1+sqrt(-7))/2)(1+sqrt(-7))/2-1(-1, 0)(0, -1)(3,5,5), (5,-11,7), (13,181,15)Union of
(|x|, n): exactly{(1, 3), (3, 4), (5, 5), (11, 7), (181, 15)}— it must agree with the hardwired Nagell 1961 answer, and the test asserts that equality. (mu's minimal polynomial isx^2 - x + 2.)Fixture
(D, k, b) = (5, 1, 3)(regime A, class numberh = 2).d0 = 5,f = 1,delta = 1,s = 0,F = 1,beta = 2*sqrt(-5),w = 2,#A = 1,t = 2,r = 0,tau = 1, 4 branches, each withT = -4,N = 9:Q_3muu(A_0, B_0)(A_1, B_1)(q, x, n)(3, a+1)a - 21(1, 0)(-2, 1)(1, -2, 2)(3, a+1)a - 2-1(-1, 0)(2, -1)(3, a+2)-a - 21(1, 0)(-2, -1)(3, a+2)-a - 2-1(-1, 0)(2, 1)(1, 2, 2)(
a = sqrt(-5);mu's minimal polynomial isx^2 + 4*x + 9.) Union: exactly{(2, 2)}, matching brute force forn <= 300. This fixture locks the class-number->1behaviour:[C_3]has ordert = 2, so only evenncan occur and the odd-nhalf of the problem is closed by the "non-principal ⟹ provably empty" step, not by search.Fixture
(D, k, b) = (2, 2, 3)(regime A,k > 1, non-trivialtau).d0 = 2,f = 1,delta = 1,s = 0,F = 1,beta = 2*sqrt(-2),h = 1,w = 2,sigma_0((2)) = 3,#A = 1(the ramifiedA = (sqrt(-2)), sotau = sqrt(-2) != 1),t = 1,r = 0, 4 branches, each withT = -2,N = 3:Q_3muu(A_0, B_0)(A_1, B_1)(q, x, n)(-a-1)-a - 11(0, 1)(2, -1)(0, 0, 0)(-a-1)-a - 1-1(0, -1)(-2, 1)(1,-2,1), (2,4,2), (5,22,5)(a-1)a - 11(0, 1)(-2, -1)(0, 0, 0)(a-1)a - 1-1(0, -1)(2, 1)(1,2,1), (2,-4,2), (5,-22,5)Union: exactly
{(0, 0), (2, 1), (4, 2), (22, 5)}, matching brute force forn <= 60.Inert regression.
(D, k, b) = (1, 1, 3):3is inert inQQ(i), so Lemma INERT applies and the complete answer is the direct check atn = 0, namely{(0, 0)}(brute force forn <= 300agrees). Same for(D, k, b) = (3, 4, 5)(5inert inQQ(sqrt(-3)), answer{(1, 0)}) and(7, 1, 3),(9, 4, 7),(12, 1, 5)(answer empty).All-branches-empty regression.
(D, k, b) = (6, 5, 7):h = 2,#A = 2,sigma_0((5)) = 4, and no(pattern, A)pair admits a residuer— every branch is provably empty, so the instance is proved to have no solutions at all (kind="empty",completeness="proved"), with no search involved. Brute force forn <= 40agrees. Same verdict, with non-empty branch lists this time, for(14, 9, 5)(h = 4,sigma_0((9)) = 9,#A = 3, 12 branches, no hits) and(2, 9, 11)(h = 1,sigma_0((9)) = 9,#A = 3, 12 branches, no hits).F != 1regression.(11, 1, 3):d0 = 11 ≡ 3 (mod 4),f = 1, soF = 2; 4 branches; answer{(4, 3)}.(4, 1, 5):d0 = 1,f = 2, soF = 2andw = 4(8 branches); answer{(1, 1), (11, 3)}.(1, 1, 5):w = 4, 8 branches, answer{(0, 0), (2, 1)}. These lock thef > 1andd0 ≡ 3 (mod 4)coordinate targets and thew ∈ {4, 6}unit sets.Branch-count bound. For every fixture above the test asserts
len(branches) <= 2^omega(b) * sigma_0((k)) * w(Lemma FINITE).Determinism. The branch tuple (including generator choices and ordering) is reproduced bit for bit on repeated runs; the tables above are committed as the expected values.
Bibliography: add
PethoDeWeger1986todata/references.bib— Attila Pethő, Benjamin M. M. de Weger, Products of prime powers in binary recurrence sequences I. The hyperbolic case, with an application to the generalized Ramanujan–Nagell equation, Mathematics of Computation, volume 47, 1986, pages 713–727 (tools/check_references.py'sREQUIRED_FIELDSareauthor,title,year— all pinned here, together withjournal,volumeandpages. The optionaldoi/urlfields are not pinned in this issue and are not load-bearing: add them only if verified at commit time bytools/check_references.pywith network access, and add aurlonly if it points to a legally free copy.) Add the key to theramanujan-nagellfamily YAML withwhy= "the binary-recurrence reduction of the generalized Ramanujan–Nagell equation implemented by the branch enumeration".make referencesandmake registry-docsmust pass.Every function (private helpers included) has a Sage-convention docstring with INPUT/OUTPUT and EXAMPLES passing
sage -t;make test,make doctest,make coverage(docstring coverage stays 100%) all clean.Out of scope
qand any reduction round — Explicit analytic bounds and certified reductions for Ramanujan–Nagell #87 (this child produces the branches; it does not search them).b = 2regimes withD ≢ 7 (mod 8)— Elementary b = 2 Ramanujan–Nagell branches #85.D-dependent unit machinery beyond the roots of unity of an imaginary quadratic field: none of these is needed, and none is introduced.