Skip to content

Instantly share code, notes, and snippets.

@adomani
Last active January 13, 2021 10:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save adomani/6f82a36069e60b318741bdf8d3bcbf76 to your computer and use it in GitHub Desktop.
Save adomani/6f82a36069e60b318741bdf8d3bcbf76 to your computer and use it in GitHub Desktop.
import data.finsupp.basic
import data.polynomial.erase_lead
import data.polynomial.degree.trailing_degree
import tactic
open finset
variables {α : Type*}
def to_dual : α → order_dual α := id
def of_dual : order_dual α → α := id
@[simp] lemma to_dual_of_dual (a : order_dual α) : to_dual (of_dual a) = a := rfl
@[simp] lemma of_dual_to_dual (a : α) : of_dual (to_dual a) = a := rfl
section
lemma to_dual_injective : function.injective (to_dual : α → order_dual α) :=
function.injective_id
@[simp] lemma to_dual_inj {a b : α} :
to_dual a = to_dual b ↔ a = b := iff.rfl
@[simp] lemma to_dual_le_to_dual [has_le α] {a b : α} :
to_dual a ≤ to_dual b ↔ b ≤ a := iff.rfl
@[simp] lemma to_dual_lt_to_dual [has_lt α] {a b : α} :
to_dual a < to_dual b ↔ b < a := iff.rfl
lemma of_dual_injective : function.injective (of_dual : order_dual α → α) :=
function.injective_id
@[simp] lemma of_dual_inj {a b : order_dual α} :
of_dual a = of_dual b ↔ a = b := iff.rfl
@[simp] lemma of_dual_le_of_dual [has_le α] {a b : order_dual α} :
of_dual a ≤ of_dual b ↔ b ≤ a := iff.rfl
@[simp] lemma of_dual_lt_of_dual [has_lt α] {a b : order_dual α} :
of_dual a < of_dual b ↔ b < a := iff.rfl
lemma le_to_dual [has_le α] {a : order_dual α} {b : α} :
a ≤ to_dual b ↔ b ≤ of_dual a := iff.rfl
lemma lt_to_dual [has_lt α] {a : order_dual α} {b : α} :
a < to_dual b ↔ b < of_dual a := iff.rfl
lemma to_dual_le [has_le α] {a : α} {b : order_dual α} :
to_dual a ≤ b ↔ of_dual b ≤ a := iff.rfl
lemma to_dual_lt [has_lt α] {a : α} {b : order_dual α} :
to_dual a < b ↔ of_dual b < a := iff.rfl
end
lemma monotone_max'_min' [linear_order α] {s : finset α} (hs : s.nonempty) :
max' s hs = of_dual (min' (image to_dual s) (nonempty.image hs to_dual)) :=
begin
apply le_antisymm (max'_le s hs _ (λ y hy, _)) (to_dual_le.mp (le_min' _ _ _ (λ _ hx, _))),
{ exact le_to_dual.mp (min'_le _ _ (mem_image.mpr ⟨_, hy, rfl⟩)), },
{ rcases (mem_image.mp hx) with ⟨_, H, rfl⟩,
exact le_max' s (to_dual w) H, }
end
lemma monotone_min'_max' [linear_order α] {s : finset α} (hs : s.nonempty) :
min' s hs = of_dual (max' (image to_dual s) (nonempty.image hs to_dual)) :=
begin
rw monotone_max'_min',
convert refl _,
{ ext1,
refine ⟨λ y, _,λ y, mem_image.mpr ⟨a, ⟨mem_image.mpr ⟨a, ⟨y, rfl⟩⟩, rfl⟩⟩⟩,
{ repeat { rw mem_image at y, rcases y with ⟨_, y, rfl⟩, },
exact y, }, },
{ exact ⟨λ a, rfl⟩, },
end
/-!
# Reverse of a univariate polynomial
The main definition is `reverse`. Applying `reverse` to a polynomial `f : polynomial R` produces
the polynomial with a reversed list of coefficients, equivalent to `X^f.nat_degree * f(1/X)`.
The main results are:
1. `reverse (f * g) = reverse (f) * reverse (g)`, provided the leading
coefficients of `f` and `g` do not multiply to zero;
2. if the coefficient ring R is an integral domain, then the function
`trailing_coeff_hom : polynomial R →* R` is a multiplicative monoid homomorphism.
-/
open_locale classical
namespace polynomial
open polynomial finsupp finset
section semiring
variables {R : Type*} [semiring R] {f : polynomial R}
/-- If `i ≤ N`, then `rev_at_fun N i` returns `N - i`, otherwise it returns `i`.
This is the map used by the embedding `rev_at`.
-/
def rev_at_fun (N i : ℕ) : order_dual ℕ := ite (i ≤ N) (to_dual (N-i)) (to_dual i)
lemma rev_at_fun_invol {N i : ℕ} : rev_at_fun N (rev_at_fun N i) = i :=
begin
unfold rev_at_fun,
by_cases Ni : i ≤ N,
{ rwa [if_pos Ni, if_pos _],
rw to_dual,simp only [id.def],
exact nat.sub_sub_self Ni,
rw [to_dual, id.def],exact nat.sub_le N i, },
--, if_pos (nat.sub_le_self N i), nat.sub_sub_self], },
{ simp_rw [to_dual, id.def],
repeat { rw if_neg _, }; assumption, },
end
lemma rev_at_fun_inj {N : ℕ} : function.injective (rev_at_fun N) :=
λ a b hab, by rw [← @rev_at_fun_invol N a, hab, rev_at_fun_invol]
/-- If `i ≤ N`, then `rev_at N i` returns `N - i`, otherwise it returns `i`.
Essentially, this embedding is only used for `i ≤ N`.
The advantage of `rev_at N i` over `N - i` is that `rev_at` is an involution.
-/
def rev_at (N : ℕ) : function.embedding ℕ (order_dual ℕ) :=
{ to_fun := λ i , (ite (i ≤ N) (to_dual (N-i)) (to_dual i)),
inj' := rev_at_fun_inj }
/-- We prefer to use the bundled `rev_at` over unbundled `rev_at_fun`. -/
@[simp] lemma rev_at_fun_eq (N i : ℕ) : rev_at_fun N i = rev_at N i := rfl
@[simp] lemma rev_at_invol {N i : ℕ} : (rev_at N) (rev_at N i) = i :=
rev_at_fun_invol
@[simp] lemma rev_at_le {N i : ℕ} (H : i ≤ N) : rev_at N i = to_dual (N - i) :=
if_pos H
lemma rev_at_add {N O n o : ℕ} (hn : n ≤ N) (ho : o ≤ O) :
rev_at (N + O) (n + o) = rev_at N n + rev_at O o :=
begin
rcases nat.le.dest hn with ⟨n', rfl⟩,
rcases nat.le.dest ho with ⟨o', rfl⟩,
repeat { rw rev_at_le (le_add_right rfl.le), },
rw [add_assoc, add_left_comm n' o, ← add_assoc, rev_at_le (le_add_right rfl.le)],
repeat { rw nat.add_sub_cancel_left },
refl,
end
/-- `reflect N f` is the polynomial such that `(reflect N f).coeff i = f.coeff (rev_at N i)`.
In other words, the terms with exponent `[0, ..., N]` now have exponent `[N, ..., 0]`.
In practice, `reflect` is only used when `N` is at least as large as the degree of `f`.
Eventually, it will be used with `N` exactly equal to the degree of `f`. -/
noncomputable def reflect (N : ℕ) (f : polynomial R) : polynomial R :=
finsupp.emb_domain (rev_at N) f
lemma reflect_support (N : ℕ) (f : polynomial R) :
(reflect N f).support = image (rev_at N) f.support :=
finset.ext (λ (a : ℕ), (by rw [reflect, mem_image, support_emb_domain, mem_map]))
@[simp] lemma coeff_reflect (N : ℕ) (f : polynomial R) (i : ℕ) :
coeff (reflect N f) i = f.coeff (rev_at N i) :=
calc finsupp.emb_domain (rev_at N) f i
= finsupp.emb_domain (rev_at N) f (rev_at N (rev_at N i)) : by rw rev_at_invol
... = f.coeff (rev_at N i) : finsupp.emb_domain_apply _ _ _
@[simp] lemma reflect_zero {N : ℕ} : reflect N (0 : polynomial R) = 0 := rfl
@[simp] lemma reflect_eq_zero_iff {N : ℕ} {f : polynomial R} :
reflect N (f : polynomial R) = 0 ↔ f = 0 :=
⟨(λ a, by rwa [reflect, emb_domain_eq_zero] at a), by { rintro rfl, refl }⟩
@[simp] lemma reflect_add (f g : polynomial R) (N : ℕ) :
reflect N (f + g) = reflect N f + reflect N g :=
by { ext1, rw [coeff_add, coeff_reflect, coeff_reflect, coeff_reflect, coeff_add], }
@[simp] lemma reflect_C_mul (f : polynomial R) (r : R) (N : ℕ) :
reflect N (C r * f) = C r * (reflect N f) :=
ext (λ (n : ℕ), by rw [coeff_reflect, coeff_C_mul, coeff_C_mul, coeff_reflect])
@[simp] lemma reflect_C_mul_X_pow (N n : ℕ) {c : R} :
reflect N (C c * X ^ n) = C c * X ^ (of_dual (rev_at N n)) :=
begin
ext a,
rw [reflect_C_mul, coeff_C_mul, coeff_C_mul, coeff_X_pow, coeff_reflect],
split_ifs,
{ rw [h],
congr,
convert coeff_X_pow_self _,
convert rev_at_invol, },
{ rw not_mem_support_iff_coeff_zero.mp,
intro rs,
rw [← one_mul (X ^ n), ← C_1] at rs,
apply h,
rw [← (mem_support_C_mul_X_pow rs), rev_at_invol],
refl, },
end
@[simp] lemma reflect_monomial (N n : ℕ) : reflect N ((X : polynomial R) ^ n) = X ^ (of_dual (rev_at N n)) :=
by rw [← one_mul (X ^ n), ← one_mul (X ^ (of_dual (rev_at N n))), ← C_1, reflect_C_mul_X_pow]
lemma reflect_mul_induction (cf cg : ℕ) :
∀ N O : ℕ, ∀ f g : polynomial R,
f.support.card ≤ cf.succ → g.support.card ≤ cg.succ → f.nat_degree ≤ N → g.nat_degree ≤ O →
(reflect (N + O) (f * g)) = (reflect N f) * (reflect O g) :=
begin
induction cf with cf hcf,
--first induction (left): base case
{ induction cg with cg hcg,
-- second induction (right): base case
{ intros N O f g Cf Cg Nf Og,
rw [← C_mul_X_pow_eq_self Cf, ← C_mul_X_pow_eq_self Cg],
simp only [mul_assoc, X_pow_mul, ← pow_add X, reflect_C_mul, reflect_monomial,
add_comm, rev_at_add Nf Og],
refl, },
-- second induction (right): induction step
{ intros N O f g Cf Cg Nf Og,
by_cases g0 : g = 0,
{ rw [g0, reflect_zero, mul_zero, mul_zero, reflect_zero], },
rw [← erase_lead_add_C_mul_X_pow g, mul_add, reflect_add, reflect_add, mul_add, hcg, hcg];
try { assumption },
{ exact le_add_left card_support_C_mul_X_pow_le_one },
{ exact (le_trans (nat_degree_C_mul_X_pow_le g.leading_coeff g.nat_degree) Og) },
{ exact nat.lt_succ_iff.mp (gt_of_ge_of_gt Cg (erase_lead_support_card_lt g0)) },
{ exact le_trans erase_lead_nat_degree_le Og } } },
--first induction (left): induction step
{ intros N O f g Cf Cg Nf Og,
by_cases f0 : f = 0,
{ rw [f0, reflect_zero, zero_mul, zero_mul, reflect_zero], },
rw [← erase_lead_add_C_mul_X_pow f, add_mul, reflect_add, reflect_add, add_mul, hcf, hcf];
try { assumption },
{ exact le_add_left card_support_C_mul_X_pow_le_one },
{ exact (le_trans (nat_degree_C_mul_X_pow_le f.leading_coeff f.nat_degree) Nf) },
{ exact nat.lt_succ_iff.mp (gt_of_ge_of_gt Cf (erase_lead_support_card_lt f0)) },
{ exact (le_trans erase_lead_nat_degree_le Nf) } },
end
@[simp] theorem reflect_mul
(f g : polynomial R) {F G : ℕ} (Ff : f.nat_degree ≤ F) (Gg : g.nat_degree ≤ G) :
reflect (F + G) (f * g) = reflect F f * reflect G g :=
reflect_mul_induction _ _ F G f g f.support.card.le_succ g.support.card.le_succ Ff Gg
/-- The reverse of a polynomial f is the polynomial obtained by "reading f backwards".
Even though this is not the actual definition, reverse f = f (1/X) * X ^ f.nat_degree. -/
noncomputable def reverse (f : polynomial R) : polynomial R := reflect f.nat_degree f
@[simp] lemma reverse_zero : reverse (0 : polynomial R) = 0 := rfl
theorem reverse_mul {f g : polynomial R} (fg : f.leading_coeff * g.leading_coeff ≠ 0) :
reverse (f * g) = reverse f * reverse g :=
by { rw [reverse, reverse, reverse, nat_degree_mul' fg, reflect_mul _ _ (refl _) (refl _)] }
@[simp] lemma reverse_mul_of_domain {R : Type*} [domain R] (f g : polynomial R) :
reverse (f * g) = reverse f * reverse g :=
begin
by_cases f0 : f=0,
{ rw [f0, zero_mul, reverse_zero, zero_mul], },
by_cases g0 : g=0,
{ rw [g0, mul_zero, reverse_zero, mul_zero], },
apply reverse_mul,
apply mul_ne_zero;
{ rwa [← leading_coeff_eq_zero] at *, },
end
lemma reflect_ne_zero_iff {N : ℕ} {f : polynomial R} :
reflect N (f : polynomial R) ≠ 0 ↔ f ≠ 0 :=
not_congr reflect_eq_zero_iff
@[simp] lemma rev_at_gt {N n : ℕ} (H : N < n) : rev_at N n = n :=
if_neg (not_le.mpr H)
@[simp] lemma reflect_invol (N : ℕ) : reflect N (reflect N f) = f :=
ext (λ (n : ℕ), by rw [coeff_reflect, coeff_reflect, rev_at_invol])
/- above, the file contains lemmas and results that essentially already appear in mathlib -/
--from here, I start trying to use order_dual
/-- `monotone_rev_at N _` coincides with `rev_at N _` in the range [0,..,N]. I use
`monotone_rev_at` just to show that `rev_at` exchanges `min`s and `max`s. With an alternative
proof of the exchange property that does not use this definition, it can be removed! -/
def monotone_rev_at (N : ℕ) : ℕ → order_dual ℕ := λ i : ℕ , to_dual (N-i)
lemma monotone_rev_at_monotone (N : ℕ) : monotone (monotone_rev_at N) :=
begin
intros x y hxy,
rw [monotone_rev_at, to_dual_le_to_dual, nat.sub_le_iff],
by_cases xle : x ≤ N,
{ rwa nat.sub_sub_self xle, },
rw not_le at xle,
apply le_of_lt,
convert gt_of_ge_of_gt hxy xle,
convert nat.sub_zero N,
exact nat.sub_eq_zero_iff_le.mpr (le_of_lt xle),
end
lemma monotone_rev_at_max'_min' {N : ℕ} {s : finset ℕ} {hs : s.nonempty} :
max' (image (monotone_rev_at N) s) (nonempty.image hs (monotone_rev_at N)) =
monotone_rev_at N (max' s hs) :=
begin
simp_rw [@monotone_max'_min' _ _ (image (monotone_rev_at N) s) _, monotone_rev_at, image_image],
apply le_antisymm,
{ apply le_min',
{ apply (nonempty.image hs), },
intros y hy,
have : ∃ (a : ℕ) (H : a ∈ s), N - a = y := mem_image.mp (by convert hy),
rcases this with ⟨y, hhy, rfl⟩,
exact nat.sub_le_sub_left N (le_max' _ _ hhy), },
{ apply min'_le,
convert mem_image.mpr _,
refine ⟨(max' s hs), ⟨max'_mem _ hs, congr_arg _ (refl _)⟩⟩, },
end
@[simp] lemma monotone_rev_at_eq_rev_at_small {N n : ℕ} :
(n ≤ N) → rev_at N n = monotone_rev_at N n :=
λ H, by convert (@rev_at_le N n H)
lemma image_monotone_rev_at_eq_image_rev_at
{N : ℕ} {s : finset ℕ} {hs : s.nonempty} (hN : max' s hs ≤ N) :
image (monotone_rev_at N) s = image (rev_at N) s :=
begin
ext a,
repeat {rw mem_image},
refine ⟨_, _⟩;
{ intro ha,
rcases ha with ⟨x, ha, rfl⟩,
refine ⟨x, ⟨ha, _⟩⟩,
rw ← monotone_rev_at_eq_rev_at_small,
apply le_trans (le_max' _ _ ‹_›) hN, },
end
lemma rev_at_small_min_max {N : ℕ} {s : finset ℕ} {hs : s.nonempty} (sm : s.max' hs ≤ N) :
max' (image (rev_at N) s) (nonempty.image hs (rev_at N)) = rev_at N (max' s hs) :=
begin
rw monotone_rev_at_eq_rev_at_small (le_trans (le_max' _ _ (max'_mem s hs)) sm),
rw ← monotone_rev_at_max'_min',
conv_rhs { congr, rw image_monotone_rev_at_eq_image_rev_at ‹_›, },
end
lemma typ {s : finset ℕ} (hs : s.nonempty) :
of_dual (max' (image to_dual s) (nonempty.image hs _)) = s.min' hs :=
begin
congr,
convert image_id,
assumption,
end
-- from here on, I was not able to get the proofs to work
lemma nat_degree_reflect_eq_nat_trailing_degree {N : ℕ} (f0 : f ≠ 0) (fs : f.support.nonempty) (H : f.nat_degree ≤ N) :
(reflect N f).nat_degree = rev_at N f.nat_trailing_degree :=
begin
convert rev_at_small_min_max _,
rw nat_degree_eq_support_max' (reflect_ne_zero_iff.mpr f0),
simp_rw [reflect, finsupp.support_emb_domain, map_eq_image],
rw [nat_trailing_degree_eq_support_min' f0],
rw monotone_rev_at_eq_rev_at_small,
rw monotone_max'_min',
simp_rw [reflect, finsupp.support_emb_domain, map_eq_image],
congr,
rw ← image_monotone_rev_at_eq_image_rev_at,
norm_num,
funext,
funext,
simp_rw [to_dual],
simp * at *,
have : image (monotone_rev_at N) f.support = image (rev_at N) f.support :=
@image_monotone_rev_at_eq_image_rev_at N f.support fs _,
simp_rw ← this,
-- rw ← nonempty_support_iff at f0,
-- conv_lhs { congr, rw ← image_monotone_rev_at_eq_image_rev_at begin
-- end},
--‹_›
-- apply congr_arg id,
rw monotone_min'_max',
rw @rev_at_small_min_max N f.support _ _,
rw monotone_rev_at_max'_min',
refine rev_at_small_min_max (by rwa ← nat_degree_eq_support_max' f0),
end
#exit
lemma nat_degree_reflect_le {N : ℕ} : (reflect N f).nat_degree ≤ max N f.nat_degree :=
begin
by_cases f0 : f=0,
{ rw [f0, reflect_zero, nat_degree_zero], exact zero_le (max N 0), },
by_cases dsm : f.nat_degree ≤ N,
{ rw [nat_degree_reflect_eq_nat_trailing_degree f0 dsm, max_eq_left dsm],
rw rev_at_le (le_trans nat_trailing_degree_le_nat_degree dsm),
exact (nat.sub_le N f.nat_trailing_degree), },
{ rw [not_le, lt_iff_le_and_ne] at dsm,
rw [max_eq_right (dsm.1), nat_degree_eq_support_max', nat_degree_eq_support_max' f0],
{ apply max'_le,
intros y hy,
rw [reflect_support, mem_image] at hy,
rcases hy with ⟨ y , hy , rfl ⟩,
rw ← nat_degree_eq_support_max',
by_cases ys : y ≤ N,
{ rw rev_at_le ys, exact (le_trans (nat.sub_le N y) dsm.1), },
{ rw rev_at_gt (not_le.mp ys), exact le_nat_degree_of_mem_supp _ hy, }, },
{ rwa [ne.def, (@reflect_eq_zero_iff R _ N f)], }, },
end
@[simp] lemma reverse_invol (h : f.coeff 0 ≠ 0) : reverse (reverse f) = f :=
begin
rw [reverse, reverse, nat_degree_reflect_eq_nat_trailing_degree _ rfl.le],
{ rw [rev_at_le nat_trailing_degree_le_nat_degree, nat_trailing_degree_eq_zero h, nat.sub_zero,
reflect_invol], },
{ intro f0, apply h, rw [f0, coeff_zero], },
end
lemma lead_reflect_eq_trailing (N : ℕ) (H : f.nat_degree ≤ N) :
leading_coeff (reflect N f) = trailing_coeff f :=
begin
by_cases f0 : f = 0,
{ rw [f0, reflect_zero, leading_coeff, trailing_coeff, coeff_zero, coeff_zero], },
rw [leading_coeff, trailing_coeff, nat_trailing_degree_eq_support_min' f0],
rw nat_degree_eq_support_max' (reflect_ne_zero_iff.mpr f0),
simp_rw [coeff_reflect, reflect_support],
rw [rev_at_small_min_max, rev_at_invol],
convert H,
rw nat_degree_eq_support_max',
end
lemma trailing_reflect_eq_lead (N : ℕ) (H : f.nat_degree ≤ N) :
trailing_coeff (reflect N f) = leading_coeff f :=
begin
rw [← @reflect_invol R _ f N] {occs := occurrences.pos [2]},
rw lead_reflect_eq_trailing,
convert @nat_degree_reflect_le R _ f N,
rwa max_eq_left,
end
lemma nat_trailing_degree_reflect_eq_nat_degree {N : ℕ} (f0 : f ≠ 0) (H : f.nat_degree ≤ N) :
(reflect N f).nat_trailing_degree = rev_at N f.nat_degree :=
begin
rw [← @reflect_invol R _ f N] {occs := occurrences.pos [2]},
rw [nat_degree_reflect_eq_nat_trailing_degree (reflect_ne_zero_iff.mpr f0), rev_at_invol],
apply le_trans nat_degree_reflect_le _,
rw max_eq_left H,
end
lemma lead_reverse_eq_trailing (f : polynomial R) : leading_coeff (reverse f) = trailing_coeff f :=
lead_reflect_eq_trailing _ rfl.le
lemma trailing_reverse_eq_lead (f : polynomial R) : trailing_coeff (reverse f) = leading_coeff f :=
trailing_reflect_eq_lead _ rfl.le
end semiring
end polynomial
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment