/-
Copyright (c) 2020 Damiano Testa. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Damiano Testa

! This file was ported from Lean 3 source module data.polynomial.reverse
! leanprover-community/mathlib commit 69c6a5a12d8a2b159f20933e60115a4f2de62b58
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Polynomial.Degree.TrailingDegree
import Mathbin.Data.Polynomial.EraseLead
import Mathbin.Data.Polynomial.Eval

/-!
# Reverse of a univariate polynomial

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.

The main definition is `reverse`.  Applying `reverse` to a polynomial `f : R[X]` produces
the polynomial with a reversed list of coefficients, equivalent to `X^f.nat_degree * f(1/X)`.

The main result is that `reverse (f * g) = reverse f * reverse g`, provided the leading
coefficients of `f` and `g` do not multiply to zero.
-/

namespace Polynomial

open Polynomial Finsupp Finset

open Classical Polynomial

section Semiring

variable {R : Type _} [Semiring R] {f : R[X]}

#print Polynomial.revAtFun /-
/-- 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 revAtFun (N i : ℕ) : ℕ :=
  ite (i ≤ N) (N - i) i
#align polynomial.rev_at_fun Polynomial.revAtFun
-/

#print Polynomial.revAtFun_invol /-
theorem revAtFun_invol {N i : ℕ} : revAtFun N (revAtFun N i) = i :=
  by
  unfold rev_at_fun
  split_ifs with h j
  · exact tsub_tsub_cancel_of_le h
  · exfalso
    apply j
    exact Nat.sub_le N i
  · rfl
#align polynomial.rev_at_fun_invol Polynomial.revAtFun_invol
-/

#print Polynomial.revAtFun_inj /-
theorem revAtFun_inj {N : ℕ} : Function.Injective (revAtFun N) :=
  by
  intro a b hab
  rw [← @rev_at_fun_invol N a, hab, rev_at_fun_invol]
#align polynomial.rev_at_fun_inj Polynomial.revAtFun_inj
-/

#print Polynomial.revAt /-
/-- 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 revAt (N : ℕ) : Function.Embedding ℕ ℕ
    where
  toFun i := ite (i ≤ N) (N - i) i
  inj' := revAtFun_inj
#align polynomial.rev_at Polynomial.revAt
-/

#print Polynomial.revAtFun_eq /-
/-- We prefer to use the bundled `rev_at` over unbundled `rev_at_fun`. -/
@[simp]
theorem revAtFun_eq (N i : ℕ) : revAtFun N i = revAt N i :=
  rfl
#align polynomial.rev_at_fun_eq Polynomial.revAtFun_eq
-/

#print Polynomial.revAt_invol /-
@[simp]
theorem revAt_invol {N i : ℕ} : (revAt N) (revAt N i) = i :=
  revAtFun_invol
#align polynomial.rev_at_invol Polynomial.revAt_invol
-/

#print Polynomial.revAt_le /-
@[simp]
theorem revAt_le {N i : ℕ} (H : i ≤ N) : revAt N i = N - i :=
  if_pos H
#align polynomial.rev_at_le Polynomial.revAt_le
-/

#print Polynomial.revAt_add /-
theorem revAt_add {N O n o : ℕ} (hn : n ≤ N) (ho : o ≤ O) :
    revAt (N + O) (n + o) = revAt N n + revAt O o :=
  by
  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 [add_tsub_cancel_left]
#align polynomial.rev_at_add Polynomial.revAt_add
-/

#print Polynomial.revAt_zero /-
@[simp]
theorem revAt_zero (N : ℕ) : revAt N 0 = N := by simp [rev_at]
#align polynomial.rev_at_zero Polynomial.revAt_zero
-/

#print Polynomial.reflect /-
/-- `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 : ℕ) : R[X] → R[X]
  | ⟨f⟩ => ⟨Finsupp.embDomain (revAt N) f⟩
#align polynomial.reflect Polynomial.reflect
-/

#print Polynomial.reflect_support /-
theorem reflect_support (N : ℕ) (f : R[X]) :
    (reflect N f).support = Finset.image (revAt N) f.support :=
  by
  rcases f with ⟨⟩
  ext1
  simp only [reflect, support_of_finsupp, support_emb_domain, Finset.mem_map, Finset.mem_image]
#align polynomial.reflect_support Polynomial.reflect_support
-/

#print Polynomial.coeff_reflect /-
@[simp]
theorem coeff_reflect (N : ℕ) (f : R[X]) (i : ℕ) : coeff (reflect N f) i = f.coeff (revAt N i) :=
  by
  rcases f with ⟨⟩
  simp only [reflect, coeff]
  calc
    Finsupp.embDomain (rev_at N) f i = Finsupp.embDomain (rev_at N) f (rev_at N (rev_at N i)) := by
      rw [rev_at_invol]
    _ = f (rev_at N i) := Finsupp.embDomain_apply _ _ _

#align polynomial.coeff_reflect Polynomial.coeff_reflect
-/

#print Polynomial.reflect_zero /-
@[simp]
theorem reflect_zero {N : ℕ} : reflect N (0 : R[X]) = 0 :=
  rfl
#align polynomial.reflect_zero Polynomial.reflect_zero
-/

#print Polynomial.reflect_eq_zero_iff /-
@[simp]
theorem reflect_eq_zero_iff {N : ℕ} {f : R[X]} : reflect N (f : R[X]) = 0 ↔ f = 0 :=
  by
  rcases f with ⟨⟩
  simp [reflect]
#align polynomial.reflect_eq_zero_iff Polynomial.reflect_eq_zero_iff
-/

#print Polynomial.reflect_add /-
@[simp]
theorem reflect_add (f g : R[X]) (N : ℕ) : reflect N (f + g) = reflect N f + reflect N g :=
  by
  ext
  simp only [coeff_add, coeff_reflect]
#align polynomial.reflect_add Polynomial.reflect_add
-/

/- warning: polynomial.reflect_C_mul -> Polynomial.reflect_C_mul is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] (f : Polynomial.{u1} R _inst_1) (r : R) (N : Nat), Eq.{succ u1} (Polynomial.{u1} R _inst_1) (Polynomial.reflect.{u1} R _inst_1 N (HMul.hMul.{u1, u1, u1} (Polynomial.{u1} R _inst_1) (Polynomial.{u1} R _inst_1) (Polynomial.{u1} R _inst_1) (instHMul.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.mul'.{u1} R _inst_1)) (coeFn.{succ u1, succ u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) (fun (_x : RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) => R -> (Polynomial.{u1} R _inst_1)) (RingHom.hasCoeToFun.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) (Polynomial.C.{u1} R _inst_1) r) f)) (HMul.hMul.{u1, u1, u1} (Polynomial.{u1} R _inst_1) (Polynomial.{u1} R _inst_1) (Polynomial.{u1} R _inst_1) (instHMul.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.mul'.{u1} R _inst_1)) (coeFn.{succ u1, succ u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) (fun (_x : RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) => R -> (Polynomial.{u1} R _inst_1)) (RingHom.hasCoeToFun.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) (Polynomial.C.{u1} R _inst_1) r) (Polynomial.reflect.{u1} R _inst_1 N f))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] (f : Polynomial.{u1} R _inst_1) (r : R) (N : Nat), Eq.{succ u1} (Polynomial.{u1} R _inst_1) (Polynomial.reflect.{u1} R _inst_1 N (HMul.hMul.{u1, u1, u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => Polynomial.{u1} R _inst_1) r) (Polynomial.{u1} R _inst_1) ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => Polynomial.{u1} R _inst_1) r) (instHMul.{u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => Polynomial.{u1} R _inst_1) r) (Polynomial.mul'.{u1} R _inst_1)) (FunLike.coe.{succ u1, succ u1, succ u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (fun (_x : R) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => Polynomial.{u1} R _inst_1) _x) (MulHomClass.toFunLike.{u1, u1, u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (Polynomial.{u1} R _inst_1) (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (NonUnitalNonAssocSemiring.toMul.{u1} (Polynomial.{u1} R _inst_1) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1)))) (NonUnitalRingHomClass.toMulHomClass.{u1, u1, u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (Polynomial.{u1} R _inst_1) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) (RingHomClass.toNonUnitalRingHomClass.{u1, u1, u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1)) (RingHom.instRingHomClassRingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1)))))) (Polynomial.C.{u1} R _inst_1) r) f)) (HMul.hMul.{u1, u1, u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => Polynomial.{u1} R _inst_1) r) (Polynomial.{u1} R _inst_1) ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => Polynomial.{u1} R _inst_1) r) (instHMul.{u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => Polynomial.{u1} R _inst_1) r) (Polynomial.mul'.{u1} R _inst_1)) (FunLike.coe.{succ u1, succ u1, succ u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (fun (_x : R) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => Polynomial.{u1} R _inst_1) _x) (MulHomClass.toFunLike.{u1, u1, u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (Polynomial.{u1} R _inst_1) (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (NonUnitalNonAssocSemiring.toMul.{u1} (Polynomial.{u1} R _inst_1) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1)))) (NonUnitalRingHomClass.toMulHomClass.{u1, u1, u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (Polynomial.{u1} R _inst_1) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) (RingHomClass.toNonUnitalRingHomClass.{u1, u1, u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1)) (RingHom.instRingHomClassRingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1)))))) (Polynomial.C.{u1} R _inst_1) r) (Polynomial.reflect.{u1} R _inst_1 N f))
Case conversion may be inaccurate. Consider using '#align polynomial.reflect_C_mul Polynomial.reflect_C_mulₓ'. -/
@[simp]
theorem reflect_C_mul (f : R[X]) (r : R) (N : ℕ) : reflect N (C r * f) = C r * reflect N f :=
  by
  ext
  simp only [coeff_reflect, coeff_C_mul]
#align polynomial.reflect_C_mul Polynomial.reflect_C_mul

/- warning: polynomial.reflect_C_mul_X_pow -> Polynomial.reflect_C_mul_X_pow is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] (N : Nat) (n : Nat) {c : R}, Eq.{succ u1} (Polynomial.{u1} R _inst_1) (Polynomial.reflect.{u1} R _inst_1 N (HMul.hMul.{u1, u1, u1} (Polynomial.{u1} R _inst_1) (Polynomial.{u1} R _inst_1) (Polynomial.{u1} R _inst_1) (instHMul.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.mul'.{u1} R _inst_1)) (coeFn.{succ u1, succ u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) (fun (_x : RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) => R -> (Polynomial.{u1} R _inst_1)) (RingHom.hasCoeToFun.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) (Polynomial.C.{u1} R _inst_1) c) (HPow.hPow.{u1, 0, u1} (Polynomial.{u1} R _inst_1) Nat (Polynomial.{u1} R _inst_1) (instHPow.{u1, 0} (Polynomial.{u1} R _inst_1) Nat (Monoid.Pow.{u1} (Polynomial.{u1} R _inst_1) (MonoidWithZero.toMonoid.{u1} (Polynomial.{u1} R _inst_1) (Semiring.toMonoidWithZero.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))))) (Polynomial.X.{u1} R _inst_1) n))) (HMul.hMul.{u1, u1, u1} (Polynomial.{u1} R _inst_1) (Polynomial.{u1} R _inst_1) (Polynomial.{u1} R _inst_1) (instHMul.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.mul'.{u1} R _inst_1)) (coeFn.{succ u1, succ u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) (fun (_x : RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) => R -> (Polynomial.{u1} R _inst_1)) (RingHom.hasCoeToFun.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) (Polynomial.C.{u1} R _inst_1) c) (HPow.hPow.{u1, 0, u1} (Polynomial.{u1} R _inst_1) Nat (Polynomial.{u1} R _inst_1) (instHPow.{u1, 0} (Polynomial.{u1} R _inst_1) Nat (Monoid.Pow.{u1} (Polynomial.{u1} R _inst_1) (MonoidWithZero.toMonoid.{u1} (Polynomial.{u1} R _inst_1) (Semiring.toMonoidWithZero.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))))) (Polynomial.X.{u1} R _inst_1) (coeFn.{1, 1} (Function.Embedding.{1, 1} Nat Nat) (fun (_x : Function.Embedding.{1, 1} Nat Nat) => Nat -> Nat) (Function.Embedding.hasCoeToFun.{1, 1} Nat Nat) (Polynomial.revAt N) n)))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] (N : Nat) (n : Nat) {c : R}, Eq.{succ u1} (Polynomial.{u1} R _inst_1) (Polynomial.reflect.{u1} R _inst_1 N (HMul.hMul.{u1, u1, u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => Polynomial.{u1} R _inst_1) c) (Polynomial.{u1} R _inst_1) (Polynomial.{u1} R _inst_1) (instHMul.{u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => Polynomial.{u1} R _inst_1) c) (Polynomial.mul'.{u1} R _inst_1)) (FunLike.coe.{succ u1, succ u1, succ u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (fun (_x : R) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => Polynomial.{u1} R _inst_1) _x) (MulHomClass.toFunLike.{u1, u1, u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (Polynomial.{u1} R _inst_1) (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (NonUnitalNonAssocSemiring.toMul.{u1} (Polynomial.{u1} R _inst_1) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1)))) (NonUnitalRingHomClass.toMulHomClass.{u1, u1, u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (Polynomial.{u1} R _inst_1) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) (RingHomClass.toNonUnitalRingHomClass.{u1, u1, u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1)) (RingHom.instRingHomClassRingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1)))))) (Polynomial.C.{u1} R _inst_1) c) (HPow.hPow.{u1, 0, u1} (Polynomial.{u1} R _inst_1) Nat (Polynomial.{u1} R _inst_1) (instHPow.{u1, 0} (Polynomial.{u1} R _inst_1) Nat (Monoid.Pow.{u1} (Polynomial.{u1} R _inst_1) (MonoidWithZero.toMonoid.{u1} (Polynomial.{u1} R _inst_1) (Semiring.toMonoidWithZero.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))))) (Polynomial.X.{u1} R _inst_1) n))) (HMul.hMul.{u1, u1, u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => Polynomial.{u1} R _inst_1) c) (Polynomial.{u1} R _inst_1) (Polynomial.{u1} R _inst_1) (instHMul.{u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => Polynomial.{u1} R _inst_1) c) (Polynomial.mul'.{u1} R _inst_1)) (FunLike.coe.{succ u1, succ u1, succ u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (fun (_x : R) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => Polynomial.{u1} R _inst_1) _x) (MulHomClass.toFunLike.{u1, u1, u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (Polynomial.{u1} R _inst_1) (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (NonUnitalNonAssocSemiring.toMul.{u1} (Polynomial.{u1} R _inst_1) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1)))) (NonUnitalRingHomClass.toMulHomClass.{u1, u1, u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (Polynomial.{u1} R _inst_1) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) (RingHomClass.toNonUnitalRingHomClass.{u1, u1, u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1)) (RingHom.instRingHomClassRingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1)))))) (Polynomial.C.{u1} R _inst_1) c) (HPow.hPow.{u1, 0, u1} (Polynomial.{u1} R _inst_1) ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : Nat) => Nat) n) (Polynomial.{u1} R _inst_1) (instHPow.{u1, 0} (Polynomial.{u1} R _inst_1) ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : Nat) => Nat) n) (Monoid.Pow.{u1} (Polynomial.{u1} R _inst_1) (MonoidWithZero.toMonoid.{u1} (Polynomial.{u1} R _inst_1) (Semiring.toMonoidWithZero.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))))) (Polynomial.X.{u1} R _inst_1) (FunLike.coe.{1, 1, 1} (Function.Embedding.{1, 1} Nat Nat) Nat (fun (_x : Nat) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : Nat) => Nat) _x) (EmbeddingLike.toFunLike.{1, 1, 1} (Function.Embedding.{1, 1} Nat Nat) Nat Nat (Function.instEmbeddingLikeEmbedding.{1, 1} Nat Nat)) (Polynomial.revAt N) n)))
Case conversion may be inaccurate. Consider using '#align polynomial.reflect_C_mul_X_pow Polynomial.reflect_C_mul_X_powₓ'. -/
@[simp]
theorem reflect_C_mul_X_pow (N n : ℕ) {c : R} : reflect N (C c * X ^ n) = C c * X ^ revAt N n :=
  by
  ext
  rw [reflect_C_mul, coeff_C_mul, coeff_C_mul, coeff_X_pow, coeff_reflect]
  split_ifs with h j
  · rw [h, rev_at_invol, coeff_X_pow_self]
  · rw [not_mem_support_iff.mp]
    intro a
    rw [← one_mul (X ^ n), ← C_1] at a
    apply h
    rw [← mem_support_C_mul_X_pow a, rev_at_invol]
#align polynomial.reflect_C_mul_X_pow Polynomial.reflect_C_mul_X_pow

/- warning: polynomial.reflect_C -> Polynomial.reflect_C is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] (r : R) (N : Nat), Eq.{succ u1} (Polynomial.{u1} R _inst_1) (Polynomial.reflect.{u1} R _inst_1 N (coeFn.{succ u1, succ u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) (fun (_x : RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) => R -> (Polynomial.{u1} R _inst_1)) (RingHom.hasCoeToFun.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) (Polynomial.C.{u1} R _inst_1) r)) (HMul.hMul.{u1, u1, u1} (Polynomial.{u1} R _inst_1) (Polynomial.{u1} R _inst_1) (Polynomial.{u1} R _inst_1) (instHMul.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.mul'.{u1} R _inst_1)) (coeFn.{succ u1, succ u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) (fun (_x : RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) => R -> (Polynomial.{u1} R _inst_1)) (RingHom.hasCoeToFun.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) (Polynomial.C.{u1} R _inst_1) r) (HPow.hPow.{u1, 0, u1} (Polynomial.{u1} R _inst_1) Nat (Polynomial.{u1} R _inst_1) (instHPow.{u1, 0} (Polynomial.{u1} R _inst_1) Nat (Monoid.Pow.{u1} (Polynomial.{u1} R _inst_1) (MonoidWithZero.toMonoid.{u1} (Polynomial.{u1} R _inst_1) (Semiring.toMonoidWithZero.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))))) (Polynomial.X.{u1} R _inst_1) N))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] (r : R) (N : Nat), Eq.{succ u1} (Polynomial.{u1} R _inst_1) (Polynomial.reflect.{u1} R _inst_1 N (FunLike.coe.{succ u1, succ u1, succ u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (fun (_x : R) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => Polynomial.{u1} R _inst_1) _x) (MulHomClass.toFunLike.{u1, u1, u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (Polynomial.{u1} R _inst_1) (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (NonUnitalNonAssocSemiring.toMul.{u1} (Polynomial.{u1} R _inst_1) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1)))) (NonUnitalRingHomClass.toMulHomClass.{u1, u1, u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (Polynomial.{u1} R _inst_1) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) (RingHomClass.toNonUnitalRingHomClass.{u1, u1, u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1)) (RingHom.instRingHomClassRingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1)))))) (Polynomial.C.{u1} R _inst_1) r)) (HMul.hMul.{u1, u1, u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => Polynomial.{u1} R _inst_1) r) (Polynomial.{u1} R _inst_1) (Polynomial.{u1} R _inst_1) (instHMul.{u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => Polynomial.{u1} R _inst_1) r) (Polynomial.mul'.{u1} R _inst_1)) (FunLike.coe.{succ u1, succ u1, succ u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (fun (_x : R) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => Polynomial.{u1} R _inst_1) _x) (MulHomClass.toFunLike.{u1, u1, u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (Polynomial.{u1} R _inst_1) (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))) (NonUnitalNonAssocSemiring.toMul.{u1} (Polynomial.{u1} R _inst_1) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1)))) (NonUnitalRingHomClass.toMulHomClass.{u1, u1, u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (Polynomial.{u1} R _inst_1) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) (RingHomClass.toNonUnitalRingHomClass.{u1, u1, u1} (RingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))) R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1)) (RingHom.instRingHomClassRingHom.{u1, u1} R (Polynomial.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1)))))) (Polynomial.C.{u1} R _inst_1) r) (HPow.hPow.{u1, 0, u1} (Polynomial.{u1} R _inst_1) Nat (Polynomial.{u1} R _inst_1) (instHPow.{u1, 0} (Polynomial.{u1} R _inst_1) Nat (Monoid.Pow.{u1} (Polynomial.{u1} R _inst_1) (MonoidWithZero.toMonoid.{u1} (Polynomial.{u1} R _inst_1) (Semiring.toMonoidWithZero.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.semiring.{u1} R _inst_1))))) (Polynomial.X.{u1} R _inst_1) N))
Case conversion may be inaccurate. Consider using '#align polynomial.reflect_C Polynomial.reflect_Cₓ'. -/
@[simp]
theorem reflect_C (r : R) (N : ℕ) : reflect N (C r) = C r * X ^ N := by
  conv_lhs => rw [← mul_one (C r), ← pow_zero X, reflect_C_mul_X_pow, rev_at_zero]
#align polynomial.reflect_C Polynomial.reflect_C

#print Polynomial.reflect_monomial /-
@[simp]
theorem reflect_monomial (N n : ℕ) : reflect N ((X : R[X]) ^ n) = X ^ revAt N n := by
  rw [← one_mul (X ^ n), ← one_mul (X ^ rev_at N n), ← C_1, reflect_C_mul_X_pow]
#align polynomial.reflect_monomial Polynomial.reflect_monomial
-/

#print Polynomial.reflect_mul_induction /-
theorem reflect_mul_induction (cf cg : ℕ) :
    ∀ N O : ℕ,
      ∀ f g : R[X],
        f.support.card ≤ cf.succ →
          g.support.card ≤ cg.succ →
            f.natDegree ≤ N →
              g.natDegree ≤ O → reflect (N + O) (f * g) = reflect N f * reflect O g :=
  by
  induction' cf with cf hcf
  --first induction (left): base case
  · induction' cg with cg hcg
    -- second induction (right): base case
    · intro N O f g Cf Cg Nf Og
      rw [← C_mul_X_pow_eq_self Cf, ← C_mul_X_pow_eq_self Cg]
      simp_rw [mul_assoc, X_pow_mul, mul_assoc, ← pow_add (X : R[X]), reflect_C_mul,
        reflect_monomial, add_comm, rev_at_add Nf Og, mul_assoc, X_pow_mul, mul_assoc, ←
        pow_add (X : R[X]), add_comm]
    -- second induction (right): induction step
    · intro N O f g Cf Cg Nf Og
      by_cases g0 : g = 0
      · rw [g0, reflect_zero, MulZeroClass.mul_zero, MulZeroClass.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_aux Og
  --first induction (left): induction step
  · intro N O f g Cf Cg Nf Og
    by_cases f0 : f = 0
    · rw [f0, reflect_zero, MulZeroClass.zero_mul, MulZeroClass.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_aux Nf
#align polynomial.reflect_mul_induction Polynomial.reflect_mul_induction
-/

#print Polynomial.reflect_mul /-
@[simp]
theorem reflect_mul (f g : R[X]) {F G : ℕ} (Ff : f.natDegree ≤ F) (Gg : g.natDegree ≤ 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
#align polynomial.reflect_mul Polynomial.reflect_mul
-/

section Eval₂

variable {S : Type _} [CommSemiring S]

/- warning: polynomial.eval₂_reflect_mul_pow -> Polynomial.eval₂_reflect_mul_pow is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] {S : Type.{u2}} [_inst_2 : CommSemiring.{u2} S] (i : RingHom.{u1, u2} R S (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2))) (x : S) [_inst_3 : Invertible.{u2} S (Distrib.toHasMul.{u2} S (NonUnitalNonAssocSemiring.toDistrib.{u2} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} S (Semiring.toNonAssocSemiring.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2))))) (AddMonoidWithOne.toOne.{u2} S (AddCommMonoidWithOne.toAddMonoidWithOne.{u2} S (NonAssocSemiring.toAddCommMonoidWithOne.{u2} S (Semiring.toNonAssocSemiring.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2))))) x] (N : Nat) (f : Polynomial.{u1} R _inst_1), (LE.le.{0} Nat Nat.hasLe (Polynomial.natDegree.{u1} R _inst_1 f) N) -> (Eq.{succ u2} S (HMul.hMul.{u2, u2, u2} S S S (instHMul.{u2} S (Distrib.toHasMul.{u2} S (NonUnitalNonAssocSemiring.toDistrib.{u2} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} S (Semiring.toNonAssocSemiring.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2)))))) (Polynomial.eval₂.{u1, u2} R S _inst_1 (CommSemiring.toSemiring.{u2} S _inst_2) i (Invertible.invOf.{u2} S (Distrib.toHasMul.{u2} S (NonUnitalNonAssocSemiring.toDistrib.{u2} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} S (Semiring.toNonAssocSemiring.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2))))) (AddMonoidWithOne.toOne.{u2} S (AddCommMonoidWithOne.toAddMonoidWithOne.{u2} S (NonAssocSemiring.toAddCommMonoidWithOne.{u2} S (Semiring.toNonAssocSemiring.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2))))) x _inst_3) (Polynomial.reflect.{u1} R _inst_1 N f)) (HPow.hPow.{u2, 0, u2} S Nat S (instHPow.{u2, 0} S Nat (Monoid.Pow.{u2} S (MonoidWithZero.toMonoid.{u2} S (Semiring.toMonoidWithZero.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2))))) x N)) (Polynomial.eval₂.{u1, u2} R S _inst_1 (CommSemiring.toSemiring.{u2} S _inst_2) i x f))
but is expected to have type
  forall {R : Type.{u2}} [_inst_1 : Semiring.{u2} R] {S : Type.{u1}} [_inst_2 : CommSemiring.{u1} S] (i : RingHom.{u2, u1} R S (Semiring.toNonAssocSemiring.{u2} R _inst_1) (Semiring.toNonAssocSemiring.{u1} S (CommSemiring.toSemiring.{u1} S _inst_2))) (x : S) [_inst_3 : Invertible.{u1} S (NonUnitalNonAssocSemiring.toMul.{u1} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} S (Semiring.toNonAssocSemiring.{u1} S (CommSemiring.toSemiring.{u1} S _inst_2)))) (Semiring.toOne.{u1} S (CommSemiring.toSemiring.{u1} S _inst_2)) x] (N : Nat) (f : Polynomial.{u2} R _inst_1), (LE.le.{0} Nat instLENat (Polynomial.natDegree.{u2} R _inst_1 f) N) -> (Eq.{succ u1} S (HMul.hMul.{u1, u1, u1} S S S (instHMul.{u1} S (NonUnitalNonAssocSemiring.toMul.{u1} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} S (Semiring.toNonAssocSemiring.{u1} S (CommSemiring.toSemiring.{u1} S _inst_2))))) (Polynomial.eval₂.{u2, u1} R S _inst_1 (CommSemiring.toSemiring.{u1} S _inst_2) i (Invertible.invOf.{u1} S (NonUnitalNonAssocSemiring.toMul.{u1} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} S (Semiring.toNonAssocSemiring.{u1} S (CommSemiring.toSemiring.{u1} S _inst_2)))) (Semiring.toOne.{u1} S (CommSemiring.toSemiring.{u1} S _inst_2)) x _inst_3) (Polynomial.reflect.{u2} R _inst_1 N f)) (HPow.hPow.{u1, 0, u1} S Nat S (instHPow.{u1, 0} S Nat (Monoid.Pow.{u1} S (MonoidWithZero.toMonoid.{u1} S (Semiring.toMonoidWithZero.{u1} S (CommSemiring.toSemiring.{u1} S _inst_2))))) x N)) (Polynomial.eval₂.{u2, u1} R S _inst_1 (CommSemiring.toSemiring.{u1} S _inst_2) i x f))
Case conversion may be inaccurate. Consider using '#align polynomial.eval₂_reflect_mul_pow Polynomial.eval₂_reflect_mul_powₓ'. -/
theorem eval₂_reflect_mul_pow (i : R →+* S) (x : S) [Invertible x] (N : ℕ) (f : R[X])
    (hf : f.natDegree ≤ N) : eval₂ i (⅟ x) (reflect N f) * x ^ N = eval₂ i x f :=
  by
  refine'
    induction_with_nat_degree_le (fun f => eval₂ i (⅟ x) (reflect N f) * x ^ N = eval₂ i x f) _ _ _
      _ f hf
  · simp
  · intro n r hr0 hnN
    simp only [rev_at_le hnN, reflect_C_mul_X_pow, eval₂_X_pow, eval₂_C, eval₂_mul]
    conv in x ^ N => rw [← Nat.sub_add_cancel hnN]
    rw [pow_add, ← mul_assoc, mul_assoc (i r), ← mul_pow, invOf_mul_self, one_pow, mul_one]
  · intros
    simp [*, add_mul]
#align polynomial.eval₂_reflect_mul_pow Polynomial.eval₂_reflect_mul_pow

/- warning: polynomial.eval₂_reflect_eq_zero_iff -> Polynomial.eval₂_reflect_eq_zero_iff is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] {S : Type.{u2}} [_inst_2 : CommSemiring.{u2} S] (i : RingHom.{u1, u2} R S (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2))) (x : S) [_inst_3 : Invertible.{u2} S (Distrib.toHasMul.{u2} S (NonUnitalNonAssocSemiring.toDistrib.{u2} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} S (Semiring.toNonAssocSemiring.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2))))) (AddMonoidWithOne.toOne.{u2} S (AddCommMonoidWithOne.toAddMonoidWithOne.{u2} S (NonAssocSemiring.toAddCommMonoidWithOne.{u2} S (Semiring.toNonAssocSemiring.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2))))) x] (N : Nat) (f : Polynomial.{u1} R _inst_1), (LE.le.{0} Nat Nat.hasLe (Polynomial.natDegree.{u1} R _inst_1 f) N) -> (Iff (Eq.{succ u2} S (Polynomial.eval₂.{u1, u2} R S _inst_1 (CommSemiring.toSemiring.{u2} S _inst_2) i (Invertible.invOf.{u2} S (Distrib.toHasMul.{u2} S (NonUnitalNonAssocSemiring.toDistrib.{u2} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} S (Semiring.toNonAssocSemiring.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2))))) (AddMonoidWithOne.toOne.{u2} S (AddCommMonoidWithOne.toAddMonoidWithOne.{u2} S (NonAssocSemiring.toAddCommMonoidWithOne.{u2} S (Semiring.toNonAssocSemiring.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2))))) x _inst_3) (Polynomial.reflect.{u1} R _inst_1 N f)) (OfNat.ofNat.{u2} S 0 (OfNat.mk.{u2} S 0 (Zero.zero.{u2} S (MulZeroClass.toHasZero.{u2} S (NonUnitalNonAssocSemiring.toMulZeroClass.{u2} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} S (Semiring.toNonAssocSemiring.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2))))))))) (Eq.{succ u2} S (Polynomial.eval₂.{u1, u2} R S _inst_1 (CommSemiring.toSemiring.{u2} S _inst_2) i x f) (OfNat.ofNat.{u2} S 0 (OfNat.mk.{u2} S 0 (Zero.zero.{u2} S (MulZeroClass.toHasZero.{u2} S (NonUnitalNonAssocSemiring.toMulZeroClass.{u2} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} S (Semiring.toNonAssocSemiring.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2))))))))))
but is expected to have type
  forall {R : Type.{u2}} [_inst_1 : Semiring.{u2} R] {S : Type.{u1}} [_inst_2 : CommSemiring.{u1} S] (i : RingHom.{u2, u1} R S (Semiring.toNonAssocSemiring.{u2} R _inst_1) (Semiring.toNonAssocSemiring.{u1} S (CommSemiring.toSemiring.{u1} S _inst_2))) (x : S) [_inst_3 : Invertible.{u1} S (NonUnitalNonAssocSemiring.toMul.{u1} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} S (Semiring.toNonAssocSemiring.{u1} S (CommSemiring.toSemiring.{u1} S _inst_2)))) (Semiring.toOne.{u1} S (CommSemiring.toSemiring.{u1} S _inst_2)) x] (N : Nat) (f : Polynomial.{u2} R _inst_1), (LE.le.{0} Nat instLENat (Polynomial.natDegree.{u2} R _inst_1 f) N) -> (Iff (Eq.{succ u1} S (Polynomial.eval₂.{u2, u1} R S _inst_1 (CommSemiring.toSemiring.{u1} S _inst_2) i (Invertible.invOf.{u1} S (NonUnitalNonAssocSemiring.toMul.{u1} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} S (Semiring.toNonAssocSemiring.{u1} S (CommSemiring.toSemiring.{u1} S _inst_2)))) (Semiring.toOne.{u1} S (CommSemiring.toSemiring.{u1} S _inst_2)) x _inst_3) (Polynomial.reflect.{u2} R _inst_1 N f)) (OfNat.ofNat.{u1} S 0 (Zero.toOfNat0.{u1} S (CommMonoidWithZero.toZero.{u1} S (CommSemiring.toCommMonoidWithZero.{u1} S _inst_2))))) (Eq.{succ u1} S (Polynomial.eval₂.{u2, u1} R S _inst_1 (CommSemiring.toSemiring.{u1} S _inst_2) i x f) (OfNat.ofNat.{u1} S 0 (Zero.toOfNat0.{u1} S (CommMonoidWithZero.toZero.{u1} S (CommSemiring.toCommMonoidWithZero.{u1} S _inst_2))))))
Case conversion may be inaccurate. Consider using '#align polynomial.eval₂_reflect_eq_zero_iff Polynomial.eval₂_reflect_eq_zero_iffₓ'. -/
theorem eval₂_reflect_eq_zero_iff (i : R →+* S) (x : S) [Invertible x] (N : ℕ) (f : R[X])
    (hf : f.natDegree ≤ N) : eval₂ i (⅟ x) (reflect N f) = 0 ↔ eval₂ i x f = 0 :=
  by
  conv_rhs => rw [← eval₂_reflect_mul_pow i x N f hf]
  constructor
  · intro h
    rw [h, MulZeroClass.zero_mul]
  · intro h
    rw [← mul_one (eval₂ i (⅟ x) _), ← one_pow N, ← mul_invOf_self x, mul_pow, ← mul_assoc, h,
      MulZeroClass.zero_mul]
#align polynomial.eval₂_reflect_eq_zero_iff Polynomial.eval₂_reflect_eq_zero_iff

end Eval₂

#print Polynomial.reverse /-
/-- 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 : R[X]) : R[X] :=
  reflect f.natDegree f
#align polynomial.reverse Polynomial.reverse
-/

#print Polynomial.coeff_reverse /-
theorem coeff_reverse (f : R[X]) (n : ℕ) : f.reverse.coeff n = f.coeff (revAt f.natDegree n) := by
  rw [reverse, coeff_reflect]
#align polynomial.coeff_reverse Polynomial.coeff_reverse
-/

#print Polynomial.coeff_zero_reverse /-
@[simp]
theorem coeff_zero_reverse (f : R[X]) : coeff (reverse f) 0 = leadingCoeff f := by
  rw [coeff_reverse, rev_at_le (zero_le f.nat_degree), tsub_zero, leading_coeff]
#align polynomial.coeff_zero_reverse Polynomial.coeff_zero_reverse
-/

#print Polynomial.reverse_zero /-
@[simp]
theorem reverse_zero : reverse (0 : R[X]) = 0 :=
  rfl
#align polynomial.reverse_zero Polynomial.reverse_zero
-/

#print Polynomial.reverse_eq_zero /-
@[simp]
theorem reverse_eq_zero : f.reverse = 0 ↔ f = 0 := by simp [reverse]
#align polynomial.reverse_eq_zero Polynomial.reverse_eq_zero
-/

#print Polynomial.reverse_natDegree_le /-
theorem reverse_natDegree_le (f : R[X]) : f.reverse.natDegree ≤ f.natDegree :=
  by
  rw [nat_degree_le_iff_degree_le, degree_le_iff_coeff_zero]
  intro n hn
  rw [WithBot.coe_lt_coe] at hn
  rw [coeff_reverse, rev_at, Function.Embedding.coeFn_mk, if_neg (not_le_of_gt hn),
    coeff_eq_zero_of_nat_degree_lt hn]
#align polynomial.reverse_nat_degree_le Polynomial.reverse_natDegree_le
-/

#print Polynomial.natDegree_eq_reverse_natDegree_add_natTrailingDegree /-
theorem natDegree_eq_reverse_natDegree_add_natTrailingDegree (f : R[X]) :
    f.natDegree = f.reverse.natDegree + f.natTrailingDegree :=
  by
  by_cases hf : f = 0
  · rw [hf, reverse_zero, nat_degree_zero, nat_trailing_degree_zero]
  apply le_antisymm
  · refine' tsub_le_iff_right.mp _
    apply le_nat_degree_of_ne_zero
    rw [reverse, coeff_reflect, ← rev_at_le f.nat_trailing_degree_le_nat_degree, rev_at_invol]
    exact trailing_coeff_nonzero_iff_nonzero.mpr hf
  · rw [← le_tsub_iff_left f.reverse_nat_degree_le]
    apply nat_trailing_degree_le_of_ne_zero
    have key := mt leading_coeff_eq_zero.mp (mt reverse_eq_zero.mp hf)
    rwa [leading_coeff, coeff_reverse, rev_at_le f.reverse_nat_degree_le] at key
#align polynomial.nat_degree_eq_reverse_nat_degree_add_nat_trailing_degree Polynomial.natDegree_eq_reverse_natDegree_add_natTrailingDegree
-/

#print Polynomial.reverse_natDegree /-
theorem reverse_natDegree (f : R[X]) : f.reverse.natDegree = f.natDegree - f.natTrailingDegree := by
  rw [f.nat_degree_eq_reverse_nat_degree_add_nat_trailing_degree, add_tsub_cancel_right]
#align polynomial.reverse_nat_degree Polynomial.reverse_natDegree
-/

#print Polynomial.reverse_leadingCoeff /-
theorem reverse_leadingCoeff (f : R[X]) : f.reverse.leadingCoeff = f.trailingCoeff := by
  rw [leading_coeff, reverse_nat_degree, ← rev_at_le f.nat_trailing_degree_le_nat_degree,
    coeff_reverse, rev_at_invol, trailing_coeff]
#align polynomial.reverse_leading_coeff Polynomial.reverse_leadingCoeff
-/

#print Polynomial.reverse_natTrailingDegree /-
theorem reverse_natTrailingDegree (f : R[X]) : f.reverse.natTrailingDegree = 0 :=
  by
  by_cases hf : f = 0
  · rw [hf, reverse_zero, nat_trailing_degree_zero]
  · rw [← le_zero_iff]
    apply nat_trailing_degree_le_of_ne_zero
    rw [coeff_zero_reverse]
    exact mt leading_coeff_eq_zero.mp hf
#align polynomial.reverse_nat_trailing_degree Polynomial.reverse_natTrailingDegree
-/

#print Polynomial.reverse_trailingCoeff /-
theorem reverse_trailingCoeff (f : R[X]) : f.reverse.trailingCoeff = f.leadingCoeff := by
  rw [trailing_coeff, reverse_nat_trailing_degree, coeff_zero_reverse]
#align polynomial.reverse_trailing_coeff Polynomial.reverse_trailingCoeff
-/

/- warning: polynomial.reverse_mul -> Polynomial.reverse_mul is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] {f : Polynomial.{u1} R _inst_1} {g : Polynomial.{u1} R _inst_1}, (Ne.{succ u1} R (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Distrib.toHasMul.{u1} R (NonUnitalNonAssocSemiring.toDistrib.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1))))) (Polynomial.leadingCoeff.{u1} R _inst_1 f) (Polynomial.leadingCoeff.{u1} R _inst_1 g)) (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))))))) -> (Eq.{succ u1} (Polynomial.{u1} R _inst_1) (Polynomial.reverse.{u1} R _inst_1 (HMul.hMul.{u1, u1, u1} (Polynomial.{u1} R _inst_1) (Polynomial.{u1} R _inst_1) (Polynomial.{u1} R _inst_1) (instHMul.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.mul'.{u1} R _inst_1)) f g)) (HMul.hMul.{u1, u1, u1} (Polynomial.{u1} R _inst_1) (Polynomial.{u1} R _inst_1) (Polynomial.{u1} R _inst_1) (instHMul.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.mul'.{u1} R _inst_1)) (Polynomial.reverse.{u1} R _inst_1 f) (Polynomial.reverse.{u1} R _inst_1 g)))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] {f : Polynomial.{u1} R _inst_1} {g : Polynomial.{u1} R _inst_1}, (Ne.{succ u1} R (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (NonUnitalNonAssocSemiring.toMul.{u1} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} R (Semiring.toNonAssocSemiring.{u1} R _inst_1)))) (Polynomial.leadingCoeff.{u1} R _inst_1 f) (Polynomial.leadingCoeff.{u1} R _inst_1 g)) (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (MonoidWithZero.toZero.{u1} R (Semiring.toMonoidWithZero.{u1} R _inst_1))))) -> (Eq.{succ u1} (Polynomial.{u1} R _inst_1) (Polynomial.reverse.{u1} R _inst_1 (HMul.hMul.{u1, u1, u1} (Polynomial.{u1} R _inst_1) (Polynomial.{u1} R _inst_1) (Polynomial.{u1} R _inst_1) (instHMul.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.mul'.{u1} R _inst_1)) f g)) (HMul.hMul.{u1, u1, u1} (Polynomial.{u1} R _inst_1) (Polynomial.{u1} R _inst_1) (Polynomial.{u1} R _inst_1) (instHMul.{u1} (Polynomial.{u1} R _inst_1) (Polynomial.mul'.{u1} R _inst_1)) (Polynomial.reverse.{u1} R _inst_1 f) (Polynomial.reverse.{u1} R _inst_1 g)))
Case conversion may be inaccurate. Consider using '#align polynomial.reverse_mul Polynomial.reverse_mulₓ'. -/
theorem reverse_mul {f g : R[X]} (fg : f.leadingCoeff * g.leadingCoeff ≠ 0) :
    reverse (f * g) = reverse f * reverse g :=
  by
  unfold reverse
  rw [nat_degree_mul' fg, reflect_mul f g rfl.le rfl.le]
#align polynomial.reverse_mul Polynomial.reverse_mul

/- warning: polynomial.reverse_mul_of_domain -> Polynomial.reverse_mul_of_domain is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_2 : Ring.{u1} R] [_inst_3 : NoZeroDivisors.{u1} R (Distrib.toHasMul.{u1} R (Ring.toDistrib.{u1} R _inst_2)) (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R _inst_2)))))] (f : Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (g : Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)), Eq.{succ u1} (Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (Polynomial.reverse.{u1} R (Ring.toSemiring.{u1} R _inst_2) (HMul.hMul.{u1, u1, u1} (Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (instHMul.{u1} (Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (Polynomial.mul'.{u1} R (Ring.toSemiring.{u1} R _inst_2))) f g)) (HMul.hMul.{u1, u1, u1} (Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (instHMul.{u1} (Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (Polynomial.mul'.{u1} R (Ring.toSemiring.{u1} R _inst_2))) (Polynomial.reverse.{u1} R (Ring.toSemiring.{u1} R _inst_2) f) (Polynomial.reverse.{u1} R (Ring.toSemiring.{u1} R _inst_2) g))
but is expected to have type
  forall {R : Type.{u1}} [_inst_2 : Ring.{u1} R] [_inst_3 : NoZeroDivisors.{u1} R (NonUnitalNonAssocRing.toMul.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R _inst_2))) (MonoidWithZero.toZero.{u1} R (Semiring.toMonoidWithZero.{u1} R (Ring.toSemiring.{u1} R _inst_2)))] (f : Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (g : Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)), Eq.{succ u1} (Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (Polynomial.reverse.{u1} R (Ring.toSemiring.{u1} R _inst_2) (HMul.hMul.{u1, u1, u1} (Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (instHMul.{u1} (Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (Polynomial.mul'.{u1} R (Ring.toSemiring.{u1} R _inst_2))) f g)) (HMul.hMul.{u1, u1, u1} (Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (instHMul.{u1} (Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (Polynomial.mul'.{u1} R (Ring.toSemiring.{u1} R _inst_2))) (Polynomial.reverse.{u1} R (Ring.toSemiring.{u1} R _inst_2) f) (Polynomial.reverse.{u1} R (Ring.toSemiring.{u1} R _inst_2) g))
Case conversion may be inaccurate. Consider using '#align polynomial.reverse_mul_of_domain Polynomial.reverse_mul_of_domainₓ'. -/
@[simp]
theorem reverse_mul_of_domain {R : Type _} [Ring R] [NoZeroDivisors R] (f g : R[X]) :
    reverse (f * g) = reverse f * reverse g :=
  by
  by_cases f0 : f = 0
  · simp only [f0, MulZeroClass.zero_mul, reverse_zero]
  by_cases g0 : g = 0
  · rw [g0, MulZeroClass.mul_zero, reverse_zero, MulZeroClass.mul_zero]
  simp [reverse_mul, *]
#align polynomial.reverse_mul_of_domain Polynomial.reverse_mul_of_domain

/- warning: polynomial.trailing_coeff_mul -> Polynomial.trailingCoeff_mul is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_2 : Ring.{u1} R] [_inst_3 : NoZeroDivisors.{u1} R (Distrib.toHasMul.{u1} R (Ring.toDistrib.{u1} R _inst_2)) (MulZeroClass.toHasZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R _inst_2)))))] (p : Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (q : Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)), Eq.{succ u1} R (Polynomial.trailingCoeff.{u1} R (Ring.toSemiring.{u1} R _inst_2) (HMul.hMul.{u1, u1, u1} (Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (instHMul.{u1} (Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (Polynomial.mul'.{u1} R (Ring.toSemiring.{u1} R _inst_2))) p q)) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Distrib.toHasMul.{u1} R (Ring.toDistrib.{u1} R _inst_2))) (Polynomial.trailingCoeff.{u1} R (Ring.toSemiring.{u1} R _inst_2) p) (Polynomial.trailingCoeff.{u1} R (Ring.toSemiring.{u1} R _inst_2) q))
but is expected to have type
  forall {R : Type.{u1}} [_inst_2 : Ring.{u1} R] [_inst_3 : NoZeroDivisors.{u1} R (NonUnitalNonAssocRing.toMul.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R _inst_2))) (MonoidWithZero.toZero.{u1} R (Semiring.toMonoidWithZero.{u1} R (Ring.toSemiring.{u1} R _inst_2)))] (p : Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (q : Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)), Eq.{succ u1} R (Polynomial.trailingCoeff.{u1} R (Ring.toSemiring.{u1} R _inst_2) (HMul.hMul.{u1, u1, u1} (Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (instHMul.{u1} (Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_2)) (Polynomial.mul'.{u1} R (Ring.toSemiring.{u1} R _inst_2))) p q)) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (NonUnitalNonAssocRing.toMul.{u1} R (NonAssocRing.toNonUnitalNonAssocRing.{u1} R (Ring.toNonAssocRing.{u1} R _inst_2)))) (Polynomial.trailingCoeff.{u1} R (Ring.toSemiring.{u1} R _inst_2) p) (Polynomial.trailingCoeff.{u1} R (Ring.toSemiring.{u1} R _inst_2) q))
Case conversion may be inaccurate. Consider using '#align polynomial.trailing_coeff_mul Polynomial.trailingCoeff_mulₓ'. -/
theorem trailingCoeff_mul {R : Type _} [Ring R] [NoZeroDivisors R] (p q : R[X]) :
    (p * q).trailingCoeff = p.trailingCoeff * q.trailingCoeff := by
  rw [← reverse_leading_coeff, reverse_mul_of_domain, leading_coeff_mul, reverse_leading_coeff,
    reverse_leading_coeff]
#align polynomial.trailing_coeff_mul Polynomial.trailingCoeff_mul

#print Polynomial.coeff_one_reverse /-
@[simp]
theorem coeff_one_reverse (f : R[X]) : coeff (reverse f) 1 = nextCoeff f :=
  by
  rw [coeff_reverse, next_coeff]
  split_ifs with hf
  · have : coeff f 1 = 0 := coeff_eq_zero_of_nat_degree_lt (by simp only [hf, zero_lt_one])
    simp [*, rev_at]
  · rw [rev_at_le]
    exact Nat.succ_le_iff.2 (pos_iff_ne_zero.2 hf)
#align polynomial.coeff_one_reverse Polynomial.coeff_one_reverse
-/

section Eval₂

variable {S : Type _} [CommSemiring S]

/- warning: polynomial.eval₂_reverse_mul_pow -> Polynomial.eval₂_reverse_mul_pow is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] {S : Type.{u2}} [_inst_2 : CommSemiring.{u2} S] (i : RingHom.{u1, u2} R S (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2))) (x : S) [_inst_3 : Invertible.{u2} S (Distrib.toHasMul.{u2} S (NonUnitalNonAssocSemiring.toDistrib.{u2} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} S (Semiring.toNonAssocSemiring.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2))))) (AddMonoidWithOne.toOne.{u2} S (AddCommMonoidWithOne.toAddMonoidWithOne.{u2} S (NonAssocSemiring.toAddCommMonoidWithOne.{u2} S (Semiring.toNonAssocSemiring.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2))))) x] (f : Polynomial.{u1} R _inst_1), Eq.{succ u2} S (HMul.hMul.{u2, u2, u2} S S S (instHMul.{u2} S (Distrib.toHasMul.{u2} S (NonUnitalNonAssocSemiring.toDistrib.{u2} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} S (Semiring.toNonAssocSemiring.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2)))))) (Polynomial.eval₂.{u1, u2} R S _inst_1 (CommSemiring.toSemiring.{u2} S _inst_2) i (Invertible.invOf.{u2} S (Distrib.toHasMul.{u2} S (NonUnitalNonAssocSemiring.toDistrib.{u2} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} S (Semiring.toNonAssocSemiring.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2))))) (AddMonoidWithOne.toOne.{u2} S (AddCommMonoidWithOne.toAddMonoidWithOne.{u2} S (NonAssocSemiring.toAddCommMonoidWithOne.{u2} S (Semiring.toNonAssocSemiring.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2))))) x _inst_3) (Polynomial.reverse.{u1} R _inst_1 f)) (HPow.hPow.{u2, 0, u2} S Nat S (instHPow.{u2, 0} S Nat (Monoid.Pow.{u2} S (MonoidWithZero.toMonoid.{u2} S (Semiring.toMonoidWithZero.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2))))) x (Polynomial.natDegree.{u1} R _inst_1 f))) (Polynomial.eval₂.{u1, u2} R S _inst_1 (CommSemiring.toSemiring.{u2} S _inst_2) i x f)
but is expected to have type
  forall {R : Type.{u2}} [_inst_1 : Semiring.{u2} R] {S : Type.{u1}} [_inst_2 : CommSemiring.{u1} S] (i : RingHom.{u2, u1} R S (Semiring.toNonAssocSemiring.{u2} R _inst_1) (Semiring.toNonAssocSemiring.{u1} S (CommSemiring.toSemiring.{u1} S _inst_2))) (x : S) [_inst_3 : Invertible.{u1} S (NonUnitalNonAssocSemiring.toMul.{u1} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} S (Semiring.toNonAssocSemiring.{u1} S (CommSemiring.toSemiring.{u1} S _inst_2)))) (Semiring.toOne.{u1} S (CommSemiring.toSemiring.{u1} S _inst_2)) x] (f : Polynomial.{u2} R _inst_1), Eq.{succ u1} S (HMul.hMul.{u1, u1, u1} S S S (instHMul.{u1} S (NonUnitalNonAssocSemiring.toMul.{u1} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} S (Semiring.toNonAssocSemiring.{u1} S (CommSemiring.toSemiring.{u1} S _inst_2))))) (Polynomial.eval₂.{u2, u1} R S _inst_1 (CommSemiring.toSemiring.{u1} S _inst_2) i (Invertible.invOf.{u1} S (NonUnitalNonAssocSemiring.toMul.{u1} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} S (Semiring.toNonAssocSemiring.{u1} S (CommSemiring.toSemiring.{u1} S _inst_2)))) (Semiring.toOne.{u1} S (CommSemiring.toSemiring.{u1} S _inst_2)) x _inst_3) (Polynomial.reverse.{u2} R _inst_1 f)) (HPow.hPow.{u1, 0, u1} S Nat S (instHPow.{u1, 0} S Nat (Monoid.Pow.{u1} S (MonoidWithZero.toMonoid.{u1} S (Semiring.toMonoidWithZero.{u1} S (CommSemiring.toSemiring.{u1} S _inst_2))))) x (Polynomial.natDegree.{u2} R _inst_1 f))) (Polynomial.eval₂.{u2, u1} R S _inst_1 (CommSemiring.toSemiring.{u1} S _inst_2) i x f)
Case conversion may be inaccurate. Consider using '#align polynomial.eval₂_reverse_mul_pow Polynomial.eval₂_reverse_mul_powₓ'. -/
theorem eval₂_reverse_mul_pow (i : R →+* S) (x : S) [Invertible x] (f : R[X]) :
    eval₂ i (⅟ x) (reverse f) * x ^ f.natDegree = eval₂ i x f :=
  eval₂_reflect_mul_pow i _ _ f le_rfl
#align polynomial.eval₂_reverse_mul_pow Polynomial.eval₂_reverse_mul_pow

/- warning: polynomial.eval₂_reverse_eq_zero_iff -> Polynomial.eval₂_reverse_eq_zero_iff is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Semiring.{u1} R] {S : Type.{u2}} [_inst_2 : CommSemiring.{u2} S] (i : RingHom.{u1, u2} R S (Semiring.toNonAssocSemiring.{u1} R _inst_1) (Semiring.toNonAssocSemiring.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2))) (x : S) [_inst_3 : Invertible.{u2} S (Distrib.toHasMul.{u2} S (NonUnitalNonAssocSemiring.toDistrib.{u2} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} S (Semiring.toNonAssocSemiring.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2))))) (AddMonoidWithOne.toOne.{u2} S (AddCommMonoidWithOne.toAddMonoidWithOne.{u2} S (NonAssocSemiring.toAddCommMonoidWithOne.{u2} S (Semiring.toNonAssocSemiring.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2))))) x] (f : Polynomial.{u1} R _inst_1), Iff (Eq.{succ u2} S (Polynomial.eval₂.{u1, u2} R S _inst_1 (CommSemiring.toSemiring.{u2} S _inst_2) i (Invertible.invOf.{u2} S (Distrib.toHasMul.{u2} S (NonUnitalNonAssocSemiring.toDistrib.{u2} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} S (Semiring.toNonAssocSemiring.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2))))) (AddMonoidWithOne.toOne.{u2} S (AddCommMonoidWithOne.toAddMonoidWithOne.{u2} S (NonAssocSemiring.toAddCommMonoidWithOne.{u2} S (Semiring.toNonAssocSemiring.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2))))) x _inst_3) (Polynomial.reverse.{u1} R _inst_1 f)) (OfNat.ofNat.{u2} S 0 (OfNat.mk.{u2} S 0 (Zero.zero.{u2} S (MulZeroClass.toHasZero.{u2} S (NonUnitalNonAssocSemiring.toMulZeroClass.{u2} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} S (Semiring.toNonAssocSemiring.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2))))))))) (Eq.{succ u2} S (Polynomial.eval₂.{u1, u2} R S _inst_1 (CommSemiring.toSemiring.{u2} S _inst_2) i x f) (OfNat.ofNat.{u2} S 0 (OfNat.mk.{u2} S 0 (Zero.zero.{u2} S (MulZeroClass.toHasZero.{u2} S (NonUnitalNonAssocSemiring.toMulZeroClass.{u2} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} S (Semiring.toNonAssocSemiring.{u2} S (CommSemiring.toSemiring.{u2} S _inst_2)))))))))
but is expected to have type
  forall {R : Type.{u2}} [_inst_1 : Semiring.{u2} R] {S : Type.{u1}} [_inst_2 : CommSemiring.{u1} S] (i : RingHom.{u2, u1} R S (Semiring.toNonAssocSemiring.{u2} R _inst_1) (Semiring.toNonAssocSemiring.{u1} S (CommSemiring.toSemiring.{u1} S _inst_2))) (x : S) [_inst_3 : Invertible.{u1} S (NonUnitalNonAssocSemiring.toMul.{u1} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} S (Semiring.toNonAssocSemiring.{u1} S (CommSemiring.toSemiring.{u1} S _inst_2)))) (Semiring.toOne.{u1} S (CommSemiring.toSemiring.{u1} S _inst_2)) x] (f : Polynomial.{u2} R _inst_1), Iff (Eq.{succ u1} S (Polynomial.eval₂.{u2, u1} R S _inst_1 (CommSemiring.toSemiring.{u1} S _inst_2) i (Invertible.invOf.{u1} S (NonUnitalNonAssocSemiring.toMul.{u1} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} S (Semiring.toNonAssocSemiring.{u1} S (CommSemiring.toSemiring.{u1} S _inst_2)))) (Semiring.toOne.{u1} S (CommSemiring.toSemiring.{u1} S _inst_2)) x _inst_3) (Polynomial.reverse.{u2} R _inst_1 f)) (OfNat.ofNat.{u1} S 0 (Zero.toOfNat0.{u1} S (CommMonoidWithZero.toZero.{u1} S (CommSemiring.toCommMonoidWithZero.{u1} S _inst_2))))) (Eq.{succ u1} S (Polynomial.eval₂.{u2, u1} R S _inst_1 (CommSemiring.toSemiring.{u1} S _inst_2) i x f) (OfNat.ofNat.{u1} S 0 (Zero.toOfNat0.{u1} S (CommMonoidWithZero.toZero.{u1} S (CommSemiring.toCommMonoidWithZero.{u1} S _inst_2)))))
Case conversion may be inaccurate. Consider using '#align polynomial.eval₂_reverse_eq_zero_iff Polynomial.eval₂_reverse_eq_zero_iffₓ'. -/
@[simp]
theorem eval₂_reverse_eq_zero_iff (i : R →+* S) (x : S) [Invertible x] (f : R[X]) :
    eval₂ i (⅟ x) (reverse f) = 0 ↔ eval₂ i x f = 0 :=
  eval₂_reflect_eq_zero_iff i x _ _ le_rfl
#align polynomial.eval₂_reverse_eq_zero_iff Polynomial.eval₂_reverse_eq_zero_iff

end Eval₂

end Semiring

section Ring

variable {R : Type _} [Ring R]

#print Polynomial.reflect_neg /-
@[simp]
theorem reflect_neg (f : R[X]) (N : ℕ) : reflect N (-f) = -reflect N f := by
  rw [neg_eq_neg_one_mul, ← C_1, ← C_neg, reflect_C_mul, C_neg, C_1, ← neg_eq_neg_one_mul]
#align polynomial.reflect_neg Polynomial.reflect_neg
-/

#print Polynomial.reflect_sub /-
@[simp]
theorem reflect_sub (f g : R[X]) (N : ℕ) : reflect N (f - g) = reflect N f - reflect N g := by
  rw [sub_eq_add_neg, sub_eq_add_neg, reflect_add, reflect_neg]
#align polynomial.reflect_sub Polynomial.reflect_sub
-/

#print Polynomial.reverse_neg /-
@[simp]
theorem reverse_neg (f : R[X]) : reverse (-f) = -reverse f := by
  rw [reverse, reverse, reflect_neg, nat_degree_neg]
#align polynomial.reverse_neg Polynomial.reverse_neg
-/

end Ring

end Polynomial


import tactic tactic.induction

namespace exlean

inductive fuzzy
  | yes : fuzzy
  | no : fuzzy
  | meh : fuzzy

example : Type := fuzzy

open fuzzy

namespace fuzzy

universe u

def fuzzy.rec_on' {C : fuzzy → Sort u} (t : fuzzy) (h₀ : C yes) (h₁ : C no) (h₂ : C meh) : C t :=
  fuzzy.rec_on t h₀ h₁ h₂

example : fuzzy := yes

def tea (t : fuzzy) : string :=
fuzzy.rec_on t "Here's your tea!" "Get out." "Are you sure?"

example : tea yes = "Here's your tea!" := rfl

def tea2 (t : fuzzy) : string :=
@fuzzy.rec_on (λ x, string) t "Here's your tea!" "Get out." "Are you sure?"

def tea3 (t : fuzzy) : string :=
begin
  cases t,
  { exact "Here's your tea!" },
  { exact "Get out." },
  { exact "Are you sure?"}
end

def tea4 : fuzzy → string
  | yes := "Here's your tea!"
  | no  := "Get out."
  | meh := "Are you sure?"

def tea5 (f : fuzzy) : string :=
"My response to your response is: '" ++
match f with
  | yes := "Here's your tea!"
  | no  := "Get out."
  | meh := "Are you sure?"
end
++ "' "

#eval tea5 yes

lemma only_ynm (t : fuzzy) : t = yes ∨ t = no ∨ t = meh :=
fuzzy.rec_on t (or.inl rfl) (or.inr $ or.inl rfl) (or.inr $ or.inr rfl)

def C_only_ynm := λ (x : fuzzy), x = yes ∨ x = no ∨ x = meh

lemma only_ynm2 (t : fuzzy) : t = yes ∨ (t = no ∨ t = meh) :=
@fuzzy.rec_on C_only_ynm t (or.inl rfl) (or.inr $ or.inl rfl) (or.inr $ or.inr rfl)

lemma only_ynm3 (t : fuzzy) : t = yes ∨ t = no ∨ t = meh :=
begin
  apply fuzzy.rec_on t,
  { left, refl, },
  { right, left, refl, },
  { right, right, refl, },
end

lemma only_ynm4 (t : fuzzy) : t = yes ∨ t = no ∨ t = meh :=
begin
  cases t,
  { left, refl, },
  { right, left, refl, },
  { right, right, refl, },
end

lemma only_ynm5 (t : fuzzy) : t = yes ∨ t = no ∨ t = meh := by { cases t; tauto }

def yes_no : ¬ yes = no .

def yes_no2 : ¬ yes = no := assume h : yes = no, fuzzy.no_confusion h

def yes_no3 : ¬ yes = no := assume h : yes = no, @fuzzy.no_confusion false yes no h

def f_no_confusion_type_full : Sort u → fuzzy → fuzzy → Sort u :=
λ (P : Sort u) (v₁ v₂ : fuzzy),
  @fuzzy.rec_on (λ (v₁ : fuzzy), Sort u) v₁
    (@fuzzy.rec_on (λ (v₁ : fuzzy), Sort u) v₂ (P → P) P P)
    (@fuzzy.rec_on (λ (v₁ : fuzzy), Sort u) v₂ P (P → P) P)
    (@fuzzy.rec_on (λ (v₁ : fuzzy), Sort u) v₂ P P (P → P))

def f_no_confusion_type : Sort u → fuzzy → fuzzy → Sort u :=
λ (P : Sort u) (v₁ v₂ : fuzzy),
  fuzzy.rec_on v₁
    (fuzzy.rec_on v₂ (P → P) P P)
    (fuzzy.rec_on v₂ P (P → P) P)
    (fuzzy.rec_on v₂ P P (P → P))

def f_no_confusion : Π {P : Sort u} {v₁ v₂ : fuzzy}, v₁ = v₂ → f_no_confusion_type P v₁ v₂ :=
λ {P : Sort u} {v₁ v₂ : fuzzy} (h : v₁ = v₂),
  eq.rec_on h (λ (h₂ : v₁ = v₁), fuzzy.rec_on v₁ (λ (a : P), a) (λ (a : P), a) (λ (a : P), a)) h

end fuzzy

namespace staff

inductive staff
  | hod : staff
  | doctor (n : fuzzy) : staff
  | secretary (id : ℕ) (boss : staff) : staff

example : Type := staff

open staff fuzzy

def jill : staff := hod

def dr_no : staff := doctor no

def dr_meh : staff := doctor meh

def justin : staff := secretary 10 jill

def selima : staff := secretary 5 dr_no

def gursel : staff := secretary 7 selima

def salary : staff → ℚ
  | hod := 500000
  | (doctor yes) := 100000
  | (doctor _) := 5000
  | (secretary num boss) := 1 / num + (salary boss) / 2

example : salary dr_meh = 5000 := rfl

example : salary selima = 12501 / 5 := by { dsimp [selima, dr_no, salary], norm_num }

def salary2 (s : staff) : ℚ := staff.rec_on s
  500000 (λ (f : fuzzy), f.rec_on 100000 5000 5000)
  (λ (n : ℕ) (boss : staff) (bs : ℚ), 1 / n + bs / 2)

def salary3 (s : staff) : ℚ := staff.rec_on s
  500000 (λ f, f.rec_on 100000 5000 5000)
  (λ n _ bs, 1 / n + bs / 2)

def salary_new : staff → ℚ
  | hod := 500000
  | (doctor yes) := 100000
  | (doctor _) := 5000
  | (secretary 0 boss) := (salary_new boss) / 200
  | (secretary (n+1) boss) := 1 / n + salary_new (secretary n boss)

end staff

section inductive_propositions

/-
The following defintions `myor` and `myand` are quite different from the definitions `and` and `or`.
In particular, for each `α β : Prop`, `myand α β : Type`, whereas `and α β : Prop`.
-/

-- `myor` and `myand` ARE NOT INDUCTIVELY-DEFINED PROPOSITIONS!!!
inductive myor (α β : Prop)
  | inl (h : α) : myor
  | inr (h : β) : myor

example : Prop → Prop → Type := myor

inductive myand (α β : Prop)
  | intro (left : α) (right : β) : myand

/-
Some proofs work as expected.
-/
example (p q : Prop) : myand p q → p :=
begin
  intro h,
  cases h with hp hq,
  exact hp,
end

example (p q : Prop) : myand p q → myand q p :=
begin
  intro h,
  cases h with hp hq,
  split,
  { exact hq, },
  { exact hp, },
end

/-
However, the following statement does not type check. The issue is that `myand q r : Type`,
but `myand q r` is the second argument to `myand` in `myand p (myand q r)`. Both arguments of
`myand` are required to have type `Prop`.
-/
--example (p q r : Prop) : myand p (myand q r) → q := sorry

/-
One major difference between `Prop` and other type universes is that an inductive type in `Prop`
can only eliminate to other types in `Prop`. Put another way, if `α` is an inductive type in `Prop`
then the motive `C` of `α.rec_on` is also of type `Prop`.
-/

-- Here's a pukka application of `or.rec_on`.
example (p q : Prop) (h : or p q) : or q p := or.rec_on h (λ hp, or.inr hp) (λ hq, or.inl hq)

-- The following doesn't work.
-- example (p q : Prop) (h : or p q) : ℕ := or.rec_on h (λ hp, 5) (λ hq, 10)

-- But it does work with `myor` as `myor` is not a type in `Prop`.
example (p q : Prop) (h : myor p q) : ℕ := myor.rec_on h (λ hp, 5) (λ hq, 10)

example (p q : Prop) (h : and p q) : ℕ  := and.rec_on h (λ hp hq, 5)

end inductive_propositions

section less_than_or_equal

open nat

/-!
We look at two defintions of `le`. The first is the standard defintion found in Lean. With this
definition, `le` is a family of inductive types.
-/
inductive le (a : ℕ) : ℕ → Prop
  | refl : le a
  | step : ∀ {b}, le b → le (succ b)

/-!
The definition above leads to an elimination principle. Suppose we have `h : x ≤ y` and we want
to prove `P y`, for some predicate `P`. We can do this by considering separately the two possible
ways in which `h` could have been constructed.

1. `h` could be the result of `refl`. In which case, `y` is `x` and we have to prove `P x`.
2. `h` could be the result of `step`. Then there is some `t` for which `y` is `succ t` and we know
`x ≤ t`. Thus, we need to prove for every `t : ℕ`, given `x ≤ t`, given `P t`, we have `P (succ t)`.
-/

/-!
Here's another definition `le'`.
-/
inductive le' : ℕ → ℕ → Prop
  | refl : ∀ (a : ℕ), le' a a
  | step : ∀ (a b : ℕ), le' a b → le' a (succ b)

example : le 5 5 := le.refl

example : le' 5 5 := le'.refl 5

local infix ` ≤ ` := le

#check @le.rec_on

#check eq.refl

#check @list.cons

example (a b c : ℕ) (hab : le a b) (hbc : le b c) : le a c := le.rec_on hbc hab
  (λ d hbd had, by { apply le.step, exact had})

/-
It took me ages to figure out the simple proof below. This is because I was trying induction on
`hab` rather than `hbc`.
-/
lemma le_trans (a b c : ℕ) (hab : a ≤ b) (hbc : b ≤ c) : a ≤ c :=
begin
  induction hbc with d hbd had,
  { exact hab, },
  { apply le.step, exact had, }
end

lemma le_rec (x : ℕ) (C : ℕ → Prop) (h1 : C x) (h2 : (∀ (y : ℕ), x ≤ y → C y → C y.succ)) :
 ∀ {n : ℕ}, x ≤ n → C n := λ n, (le.rec h1) h2

lemma le_rec' (x n : ℕ) (C : ℕ → Prop)
(h1 : C x) (h2 : (∀ (y : ℕ), x ≤ y → C y → C y.succ)) (h3 : x ≤ n) : C n :=
 (λ n, (le.rec h1) h2) n h3

lemma le_trans' (a b c : ℕ) : a ≤ b → b ≤ c → a ≤ c :=
begin
  let C := λ m, a ≤ m,
  intro hab,
  have h2 : ∀ (y : ℕ), b ≤ y → C y → C y.succ,
  { intros y hby hay,
    apply le.step,
    exact hay, },
  apply le_rec b C hab h2,
end

lemma le_trans'' (a b c : ℕ) : a ≤ b → b ≤ c → a ≤ c :=
begin
  let C := λ m, a ≤ m,
  intros h1 h3,
  have h2 : ∀ (y : ℕ), b ≤ y → C y → C y.succ,
  { intros y hby hay,
    apply le.step,
    exact hay, },
  exact le_rec' b c C h1 h2 h3,
end

end less_than_or_equal

section trees

inductive Tree
  | empty : Tree
  | node  : ℕ → Tree → Tree → Tree

open Tree

example : Tree := empty

def smalltree : Tree := node 3 (node 5 (node 7 empty empty) empty) (node 6 empty (node 2 empty empty))

namespace Tree

def sum (t : Tree) : ℕ :=
begin
  refine Tree.rec_on t 0 _,
  intros n ltree rtree ltree_sum rtree_sum,
  exact ltree_sum + n + rtree_sum,
end

lemma sum_empty : sum empty = 0 := rfl

lemma sum_node (n : ℕ) (lt rt : Tree) : sum (node n lt rt) = (sum lt) + n + (sum rt) := rfl

def left_combine (t s : Tree) : Tree :=
begin
  refine Tree.rec_on t s _,
  intros n lt rt ilt irt,
  exact node n ilt rt
end

lemma left_combine_empty (s : Tree) : left_combine empty s = s := rfl

lemma left_combine_node (n : ℕ) (lt rt s : Tree) :
left_combine (node n lt rt) s = node n (left_combine lt s) rt := rfl

def mytree : Tree := node 10 (node 50 empty empty) (node 20 (node 30 empty empty) empty)

lemma sum_add (t s : Tree) : sum (left_combine t s) = (sum t) + (sum s) :=
begin
  refine Tree.rec_on t _ _,
  { rw [sum_empty, left_combine_empty, zero_add], },
  { intros n lt rt ilt irt, simp only [sum_node, left_combine_node, ilt], linarith, }
end

inductive sorted_tree : Tree → Prop
  | srt_empty : sorted_tree empty
  | srt_neither {n : ℕ} : sorted_tree (node n empty empty)
  | srt_left {n m : ℕ} {lt rt : Tree} : m ≤ n → sorted_tree (node m lt rt)
    → sorted_tree (node n (node m lt rt) empty)
  | srt_right {n m : ℕ} {lt rt : Tree} : n ≤ m → sorted_tree (node m lt rt)
    → sorted_tree (node n empty (node m lt rt))
  | srt_both {n m p : ℕ} {ltm rtm ltp rtp : Tree} : m ≤ n → n ≤ p → sorted_tree (node m ltm rtm)
    → sorted_tree (node p ltp rtp) → sorted_tree (node n (node m ltm rtm) (node p ltp rtp))

open sorted_tree

example : sorted_tree (node 10 empty (node 20 empty empty)) :=
begin
  refine srt_right dec_trivial _,
  exact srt_neither,
end

def tree1 := node 10 (node 5 empty empty) (node 30 empty empty)

lemma test_srt1 : sorted_tree tree1 :=
by { refine srt_both dec_trivial dec_trivial _ _; exact srt_neither }

def tree2 := node 20 tree1 empty

lemma test_srt2 : sorted_tree tree2 :=
begin
  dsimp [tree2,tree1],
  refine srt_left dec_trivial _,
  exact test_srt1,
end

def tree_insert : ℕ → Tree → Tree
  | x empty := node x empty empty
  | x (node a left right) := if x = a then node a left right else
      if x <= a then node a (tree_insert x left) right else node a left (tree_insert x right)

def list_to_tree : list ℕ → Tree := list.foldr tree_insert empty

example : list_to_tree [1,2,3] = node 3 (node 2 (node 1 empty empty) empty) empty := rfl

def tree1' := list_to_tree [30,5,10]

example : tree1 = tree1' := rfl

def tree2' := list_to_tree [30,10,5,20]

#reduce tree2'

example (xs : list ℕ) (x : ℕ) : ∃ l r, list_to_tree (xs ++ [x]) = node x l r :=
begin
  induction xs with y ys ih,
  { use [empty, empty], refl, },
  { unfold list_to_tree at *, dsimp,
    rcases ih with ⟨l1, r1, ih⟩,
    rw ih,
    cases (lt_trichotomy x y) with h h,
    { use [l1, tree_insert y r1],
      dsimp [tree_insert],
      rw if_neg,
      { rw if_neg,
        { intro hle, apply lt_irrefl x, apply lt_of_lt_of_le h hle, }, },
      { intro heq, rw heq at h, exact (lt_irrefl x) h, }, },
    { cases h with h h,
      { use [l1, r1], dsimp [tree_insert], rw if_pos, symmetry, assumption, },
      use [tree_insert y l1, r1],
      dsimp [tree_insert],
      rw if_neg,
      { rw if_pos,
        { exact le_of_lt h }, },
      { intro heq, rw heq at h, exact (lt_irrefl x) h, }, }, }
end

/- lemma sorted_of_list (t : Tree) (h : sorted_tree t) : ∃ xs, t = list_to_tree xs :=
begin
  induction h,
  { use ([] : list ℕ), refl, },
  case srt_neither : x { use ([x] : list ℕ), refl, },
  case srt_left : n m l r hle hsrt ih
  { cases ih with xs ih,
    use (xs ++ [m]),
    dsimp [list_to_tree],
    rw [ih, list.foldr_append],

   },
  {   },
  {   },
end -/

end Tree

end trees

end exlean