It's #6428, but the only explanation is "Working directly with specific polynomials (like X^n - X - 1) is really painful, since whenever you try to do something simple like checking that the polynomial is monic or computing its degree, you have to prove a bunch of trivial inequalities. Bundling solves this problem".
@Yaël Dillies I just thought it was easier to work with four coefficients than with a submodule of R[X], and I was really just following the quadratic_discriminant file that’s already present (PS: can an admin kick my other account? I don’t have access to that one anymore and want to receive the right pings)
When I wrote this I really only needed cubics that aren’t quadratic/linear/constant (so the four coefficients are kinda natural to start from), but I was told to include the degenerate cases as well, so now R[X] <= 3 seems more natural
Just a follow-up on the difficulty of computing explicit nat_degrees, I gave it a shot! Below is my attempt: it has not been easy! But I was indeed able to prove that the degree of X^2 + 1 is 2 in any non-trivial semiring! :upside_down:
Computing degrees
importdata.polynomial.degree.definitionsopenpolynomialopen_localepolynomialclassicalvariables{R:Type*}[semiringR]lemmaC_X_pow_add_C_X_pow1{rs:R}{mn:ℕ}(mn:m<n):Cr*(1*X)^n+Cs*(1*X)^m=Cs*(1*X)^m+Cr*(1*X)^n:=add_comm__lemmanat_degree_monomial_le(n:ℕ)(r:R):(monomialnr).nat_degree≤n:=by{rwnat_degree_monomial,split_ifs,simp}lemmanat_degree_add_add{mn:ℕ}(rs:R)(s0:s≠0)(f:R[X])(hf:f.nat_degree≤m)(mn:m<n):(f+monomialmr+monomialns).nat_degree=n:=beginrwa[nat_degree_add_eq_right_of_nat_degree_lt,nat_degree_monomial,if_negs0],refine(nat_degree_add_le__).trans_lt_,applymax_lt,refinehf.trans_lt(mn.trans_le(le_of_eq_)),rw[nat_degree_monomial,if_negs0],refine(nat_degree_monomial_le__).trans_lt(mn.trans_le(le_of_eq_)),rw[nat_degree_monomial,if_negs0],endlemmanat_degree_add_once{mn:ℕ}(rs:R)(s0:s≠0)(mn:m<n):(monomialmr+monomialns).nat_degree=n:=beginrwa[nat_degree_add_eq_right_of_nat_degree_lt,nat_degree_monomial,if_negs0],rw[nat_degree_monomial,nat_degree_monomial,if_negs0],split_ifs,exact(nat.zero_le_).trans_ltmn,assumption,endvariable[nontrivialR]-- my test polynomial: C u * X + X ^ 5 + C s + C t * X ^ 2 + X ^ 8-- {r s t u : R} (r0 : t ≠ 0)-- Kevin's examplelemmanat_degree_le:(X^2+1:R[X]).nat_degree≤2:=begin-- First, make sure that all summands are `monomial`stry{rwX},try{rw←C_1},repeat{rw←monomial_zero_left},repeat{rwmonomial_pow},repeat{rwmonomial_mul_monomial},-- and clean up various add_zeros, mul_ones,...try{simp[-monomial_zero_left]},-- We peel off each summand and produce goals where we need to show that each summand has-- degree bounded by our targetrepeat{refine(nat_degree_add_le__).trans(max_le__)},-- since our summands are monomials, we know that their degrees are at most their exponentsany_goals{apply(nat_degree_monomial_le_(_:R)).trans_},-- therefore, `norm_num` should be able to prove the inequalities.all_goals{norm_num},end-- {r s t u : R} (r0 : t ≠ 0) (z0 : (1 : R) ≠ 0)lemmale_nat_degree:2≤(X^2+1:R[X]).nat_degree:=begin-- First, make sure that all summands are `monomial`stry{rwX},try{rw←C_1},repeat{rw←monomial_zero_left},repeat{rwmonomial_pow},repeat{rwmonomial_mul_monomial},-- and clean up various add_zeros, mul_ones,...try{simp[-monomial_zero_left]},-- Now that all summands are monomials, we convert them to the form `C r * X ^ n`,-- with possibly `r = 1` and `n = 0` or `n = 1`, for uniformity.repeat{rw[monomial_eq_C_mul_X]},-- we also rearrange parentheses, just in caserepeat{rw←add_assoc},-- mark each summand with a `1 * X`: later on, we will remove these markers, to go deeper into-- the various summands. The presence of a `1 *` in a term is our cue that we have not yet-- rearranged it.rw←one_mul(X:R[X]),-- This block sorts the summands in ascending degree of exponent.-- The number of iterations should be adapted to each case -- this choice works for small cases.-- We visit each pair of consecutive summands and decide whether we swap them or not.-- The number of times that we visit all the summands is determined by the first `iterate`.-- The visits to the summands happens in the second `iterate`.iterate2-- The inner loop visits each consecutive pairs of summands and swaps them, if the swap makes the-- sequence of exponents decrease.{iterate3{-- We focus on the first pair. `C_X_pow_add_C_X_pow1` will swap the two summands-- creating a proof obligation of showing that the new order is strictly monotone increasing.-- `norm_num` will try to solve side goal and if it does not succeed, then `refl` fails and-- the swap does not occur.-- If `norm_num` succeeds, we have straightened a little, otherwise, the two first terms are-- in correct order.-- In either case, after the `try`, we have not increased the number of inversions among the-- exponents of our summands and it could have decreased.try{work_on_goal1{rwC_X_pow_add_C_X_pow1,rotate,norm_num;refl}},-- so we shift parentheses around isolating the not-yet-visited terms (highlighted by the `1`)repeat{rwadd_assoc(C_*(1*X)^_)},-- we mark as straightened the term that was just (possibly) rearrangedtry{nth_rewrite0one_mulX},-- and we prepare to start again with the next pairrepeat{rw←add_assoc(C_*(1*X)^_)}},-- We are now outside of the first `iterate`.-- we clear and replace the markerstry{rw[one_mul]},try{rw[←one_mul(X:R[X])]},-- we rearrange parenthesesrepeat{rw←add_assoc}},-- Unless we made mistakes (or we looped too little), our expression has exponents sorted in-- strictly increasing order.-- We clear the leftover markerstry{rw[one_mulX]},-- We re-express everything in terms of `monomial`srepeat{rw[←monomial_eq_C_mul_X]},-- Now we strip off each summand from the right-most one back-- remember that the further right we go, the higher the exponent.-- Lemma `nat_degree_add_add` creates proof obligations of checking that the ordering is-- correct and that the various coefficients are non-zero-- (really, we only need the highest coefficient to be non-zero, the remaining ones might well-- be zero, but I decided that this was enough!)repeat{rwnat_degree_add_add},try{rwnat_degree_add_once},-- Finally, we try to prove these proof obligations using `norm_num` or `assumptions that might-- be floating around.any_goals{norm_num},all_goals{assumption},end
Btw, the challenge is now to find a polynomial written as a sum of terms with distinct exponents and non-zero coefficients, for which the above code does not compute the degree!
norm_num essentially works in an "extendable" way, I'm not really sure how. but it means that if you have some definition that you want to evaluate, you don't have to edit the core tactic, you can just do it in a separate file
By the way, most of the structure above is about sorting a sum of monomials by degree, which is independent of computing degrees. I can see if abel or ring works there.