Repository navigation
Factor a polynomial in two variables, by Kronecker's substitution - #1055
Merged
Merged
Conversation
#746 item 43 asks for multivariate factorisation and names the case it wants: `Factor("x ^ 2 - y ^ 2", "x")` was `null`. It is `(x + y) * (x - y)` now. A factor of a polynomial of degree `d` in `x` has degree at most `d` in `x`, so with `s = d + 1` the map `x^i y^j -> t^(i + s*j)` is injective on every monomial that can appear in the polynomial or in any of its factors -- `i` is the remainder and `j` the quotient of the exponent by `s`, and neither can be confused with another pair. So the one-variable image is factored by the factoriser that already exists, and each subset of its irreducible factors names a candidate two-variable factor. x ^ 2 - y ^ 2 -> (x + y) * (x - y) x ^ 2 + 2 * x * y + y ^ 2 -> (x + y) ^ 2 x ^ 3 - y ^ 3 -> (x - y) * (x ^ 2 + x * y + y ^ 2) x ^ 4 - y ^ 4 -> (x + y) * (x ^ 2 + y ^ 2) * (x - y) x ^ 2 * y ^ 2 - 1 -> (x * y + 1) * (x * y - 1) x ^ 2 - y ^ 2 + 2 * x + 1 -> (x + y + 1) * (x - y + 1) x ^ 2 + y ^ 2 -> null, irreducible over Q x * y + z -> null, three variables **It cannot answer wrongly.** The substitution is injective on monomials and not on factorisations, so the image can factor further than the polynomial does and every candidate is a guess. Each is tested by exact division before it is kept, and the assembled factors are divided back into the input before they are returned, so the failure mode is a refusal. What it refuses: the image has degree `d + s*e` and the one-variable factoriser stops at 32, so this reaches bidegrees like (2, 10), (3, 7) and (5, 4). The recombination is over subsets, so the image's factor count is capped as well. Lifting that ceiling is Hensel lifting with an evaluation homomorphism and is a different piece of work; this is not a step towards it, it is the classical method that is exact within its bound. Repeated factors are collected into a power, as the one-variable path does, so one question does not get two shapes of answer depending on which path took it. `MathS.Polynomials.Factor` has no caller inside the library -- checked, not assumed -- so no simplification, solution or integral moves with it. Suite 8574/0, casbench and simpsweep byte-identical to master's committed reports. Part of #746 tier 1, item 43. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Bjumi5K7fg8yx6UK1mZTQd
This was referenced Aug 25, 2026
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #746 tier 1, item 43 — and it is the case that item names.
How
A factor of a polynomial of degree
dinxhas degree at mostdinx. So withs = d + 1, themap
x^i y^j → t^(i + s*j)is injective on every monomial that can appear in the polynomial or inany of its factors:
iis the remainder andjthe quotient of the exponent bys, and neithercan be confused with another pair. The one-variable image is handed to the factoriser that already
exists, and each subset of its irreducible factors names a candidate two-variable factor.
Factor("x ^ 2 - y ^ 2", "x")null(x + y) * (x - y)Factor("x ^ 2 + 2 * x * y + y ^ 2", "x")null(x + y) ^ 2Factor("x ^ 3 - y ^ 3", "x")null(x - y) * (x ^ 2 + x * y + y ^ 2)Factor("x ^ 4 - y ^ 4", "x")null(x + y) * (x ^ 2 + y ^ 2) * (x - y)Factor("x ^ 2 * y ^ 2 - 1", "x")null(x * y + 1) * (x * y - 1)Factor("x ^ 2 - y ^ 2 + 2 * x + 1", "x")null(x + y + 1) * (x - y + 1)Factor("x ^ 2 + y ^ 2", "x")nullnull— irreducible over ℚFactor("x * y + z", "x")nullnull— three variablesIt cannot answer wrongly
This is the part worth reviewing. The substitution is injective on monomials but not on
factorisations, so the image may factor further than the polynomial does and every candidate really
is a guess. Two checks stand between a guess and an answer:
So the failure mode is a refusal, never a wrong factorisation.
What it refuses
The image has degree
d + s*efor degreeein the second variable, and the one-variable factoriserstops at 32 — so this reaches bidegrees like (2, 10), (3, 7) and (5, 4) and refuses past them. The
recombination is over subsets, so the image's factor count is capped at 12.
Lifting that ceiling means Hensel lifting with an evaluation homomorphism. This is not a step
towards that — it is the classical method, exact inside its bound and honest outside it. Item 43's
full ask is still open.
Blast radius
MathS.Polynomials.Factorhas no caller inside the library — checked with a grep, not assumed —so no simplification, solution or integral moves with this.
casbenchandsimpsweepre-run against this branch are byte-identical to master's committedreports.
not get two shapes of answer depending on which path took it.
factorisation reported as a whole one fails rather than passing quietly.