/-
Copyright (c) 2018 Robert Y. Lewis. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Robert Y. Lewis

! This file was ported from Lean 3 source module topology.algebra.polynomial
! leanprover-community/mathlib commit 565eb991e264d0db702722b4bde52ee5173c9950
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Polynomial.AlgebraMap
import Mathbin.Data.Polynomial.Inductions
import Mathbin.Data.Polynomial.Splits
import Mathbin.RingTheory.Polynomial.Vieta
import Mathbin.Analysis.Normed.Field.Basic

/-!
# Polynomials and limits

In this file we prove the following lemmas.

* `polynomial.continuous_eval₂: `polynomial.eval₂` defines a continuous function.
* `polynomial.continuous_aeval: `polynomial.aeval` defines a continuous function;
  we also prove convenience lemmas `polynomial.continuous_at_aeval`,
  `polynomial.continuous_within_at_aeval`, `polynomial.continuous_on_aeval`.
* `polynomial.continuous`:  `polynomial.eval` defines a continuous functions;
  we also prove convenience lemmas `polynomial.continuous_at`, `polynomial.continuous_within_at`,
  `polynomial.continuous_on`.
* `polynomial.tendsto_norm_at_top`: `λ x, ‖polynomial.eval (z x) p‖` tends to infinity provided that
  `λ x, ‖z x‖` tends to infinity and `0 < degree p`;
* `polynomial.tendsto_abv_eval₂_at_top`, `polynomial.tendsto_abv_at_top`,
  `polynomial.tendsto_abv_aeval_at_top`: a few versions of the previous statement for
  `is_absolute_value abv` instead of norm.

## Tags

polynomial, continuity
-/

open IsAbsoluteValue Filter

namespace Polynomial

open Polynomial

section TopologicalSemiring

variable {R S : Type _} [Semiring R] [TopologicalSpace R] [TopologicalSemiring R] (p : R[X])

#print Polynomial.continuous_eval₂ /-
@[continuity]
protected theorem continuous_eval₂ [Semiring S] (p : S[X]) (f : S →+* R) :
    Continuous fun x => p.eval₂ f x :=
  by
  simp only [eval₂_eq_sum, Finsupp.sum]
  exact continuous_finset_sum _ fun c hc => continuous_const.mul (continuous_pow _)
#align polynomial.continuous_eval₂ Polynomial.continuous_eval₂
-/

#print Polynomial.continuous /-
@[continuity]
protected theorem continuous : Continuous fun x => p.eval x :=
  p.continuous_eval₂ _
#align polynomial.continuous Polynomial.continuous
-/

#print Polynomial.continuousAt /-
protected theorem continuousAt {a : R} : ContinuousAt (fun x => p.eval x) a :=
  p.Continuous.ContinuousAt
#align polynomial.continuous_at Polynomial.continuousAt
-/

#print Polynomial.continuousWithinAt /-
protected theorem continuousWithinAt {s a} : ContinuousWithinAt (fun x => p.eval x) s a :=
  p.Continuous.ContinuousWithinAt
#align polynomial.continuous_within_at Polynomial.continuousWithinAt
-/

#print Polynomial.continuousOn /-
protected theorem continuousOn {s} : ContinuousOn (fun x => p.eval x) s :=
  p.Continuous.ContinuousOn
#align polynomial.continuous_on Polynomial.continuousOn
-/

end TopologicalSemiring

section TopologicalAlgebra

variable {R A : Type _} [CommSemiring R] [Semiring A] [Algebra R A] [TopologicalSpace A]
  [TopologicalSemiring A] (p : R[X])

#print Polynomial.continuous_aeval /-
@[continuity]
protected theorem continuous_aeval : Continuous fun x : A => aeval x p :=
  p.continuous_eval₂ _
#align polynomial.continuous_aeval Polynomial.continuous_aeval
-/

#print Polynomial.continuousAt_aeval /-
protected theorem continuousAt_aeval {a : A} : ContinuousAt (fun x : A => aeval x p) a :=
  p.continuous_aeval.ContinuousAt
#align polynomial.continuous_at_aeval Polynomial.continuousAt_aeval
-/

#print Polynomial.continuousWithinAt_aeval /-
protected theorem continuousWithinAt_aeval {s a} :
    ContinuousWithinAt (fun x : A => aeval x p) s a :=
  p.continuous_aeval.ContinuousWithinAt
#align polynomial.continuous_within_at_aeval Polynomial.continuousWithinAt_aeval
-/

#print Polynomial.continuousOn_aeval /-
protected theorem continuousOn_aeval {s} : ContinuousOn (fun x : A => aeval x p) s :=
  p.continuous_aeval.ContinuousOn
#align polynomial.continuous_on_aeval Polynomial.continuousOn_aeval
-/

end TopologicalAlgebra

/- warning: polynomial.tendsto_abv_eval₂_at_top -> Polynomial.tendsto_abv_eval₂_atTop is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {S : Type.{u2}} {k : Type.{u3}} {α : Type.{u4}} [_inst_1 : Semiring.{u1} R] [_inst_2 : Ring.{u2} S] [_inst_3 : LinearOrderedField.{u3} k] (f : RingHom.{u1, u2} R S (Semiring.toNonAssocSemiring.{u1} R _inst_1) (NonAssocRing.toNonAssocSemiring.{u2} S (Ring.toNonAssocRing.{u2} S _inst_2))) (abv : S -> k) [_inst_4 : IsAbsoluteValue.{u3, u2} k (StrictOrderedSemiring.toOrderedSemiring.{u3} k (StrictOrderedRing.toStrictOrderedSemiring.{u3} k (LinearOrderedRing.toStrictOrderedRing.{u3} k (LinearOrderedCommRing.toLinearOrderedRing.{u3} k (LinearOrderedField.toLinearOrderedCommRing.{u3} k _inst_3))))) S (Ring.toSemiring.{u2} S _inst_2) abv] (p : Polynomial.{u1} R _inst_1), (LT.lt.{0} (WithBot.{0} Nat) (Preorder.toLT.{0} (WithBot.{0} Nat) (WithBot.preorder.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))))) (OfNat.ofNat.{0} (WithBot.{0} Nat) 0 (OfNat.mk.{0} (WithBot.{0} Nat) 0 (Zero.zero.{0} (WithBot.{0} Nat) (WithBot.hasZero.{0} Nat Nat.hasZero)))) (Polynomial.degree.{u1} R _inst_1 p)) -> (Ne.{succ u2} S (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (RingHom.{u1, u2} R S (Semiring.toNonAssocSemiring.{u1} R _inst_1) (NonAssocRing.toNonAssocSemiring.{u2} S (Ring.toNonAssocRing.{u2} S _inst_2))) (fun (_x : RingHom.{u1, u2} R S (Semiring.toNonAssocSemiring.{u1} R _inst_1) (NonAssocRing.toNonAssocSemiring.{u2} S (Ring.toNonAssocRing.{u2} S _inst_2))) => R -> S) (RingHom.hasCoeToFun.{u1, u2} R S (Semiring.toNonAssocSemiring.{u1} R _inst_1) (NonAssocRing.toNonAssocSemiring.{u2} S (Ring.toNonAssocRing.{u2} S _inst_2))) f (Polynomial.leadingCoeff.{u1} R _inst_1 p)) (OfNat.ofNat.{u2} S 0 (OfNat.mk.{u2} S 0 (Zero.zero.{u2} S (MulZeroClass.toHasZero.{u2} S (NonUnitalNonAssocSemiring.toMulZeroClass.{u2} S (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u2} S (NonAssocRing.toNonUnitalNonAssocRing.{u2} S (Ring.toNonAssocRing.{u2} S _inst_2))))))))) -> (forall {l : Filter.{u4} α} {z : α -> S}, (Filter.Tendsto.{u4, u3} α k (Function.comp.{succ u4, succ u2, succ u3} α S k abv z) l (Filter.atTop.{u3} k (PartialOrder.toPreorder.{u3} k (OrderedAddCommGroup.toPartialOrder.{u3} k (StrictOrderedRing.toOrderedAddCommGroup.{u3} k (LinearOrderedRing.toStrictOrderedRing.{u3} k (LinearOrderedCommRing.toLinearOrderedRing.{u3} k (LinearOrderedField.toLinearOrderedCommRing.{u3} k _inst_3)))))))) -> (Filter.Tendsto.{u4, u3} α k (fun (x : α) => abv (Polynomial.eval₂.{u1, u2} R S _inst_1 (Ring.toSemiring.{u2} S _inst_2) f (z x) p)) l (Filter.atTop.{u3} k (PartialOrder.toPreorder.{u3} k (OrderedAddCommGroup.toPartialOrder.{u3} k (StrictOrderedRing.toOrderedAddCommGroup.{u3} k (LinearOrderedRing.toStrictOrderedRing.{u3} k (LinearOrderedCommRing.toLinearOrderedRing.{u3} k (LinearOrderedField.toLinearOrderedCommRing.{u3} k _inst_3)))))))))
but is expected to have type
  forall {R : Type.{u4}} {S : Type.{u3}} {k : Type.{u2}} {α : Type.{u1}} [_inst_1 : Semiring.{u4} R] [_inst_2 : Ring.{u3} S] [_inst_3 : LinearOrderedField.{u2} k] (f : RingHom.{u4, u3} R S (Semiring.toNonAssocSemiring.{u4} R _inst_1) (NonAssocRing.toNonAssocSemiring.{u3} S (Ring.toNonAssocRing.{u3} S _inst_2))) (abv : S -> k) [_inst_4 : IsAbsoluteValue.{u2, u3} k (OrderedCommSemiring.toOrderedSemiring.{u2} k (StrictOrderedCommSemiring.toOrderedCommSemiring.{u2} k (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{u2} k (LinearOrderedSemifield.toLinearOrderedCommSemiring.{u2} k (LinearOrderedField.toLinearOrderedSemifield.{u2} k _inst_3))))) S (Ring.toSemiring.{u3} S _inst_2) abv] (p : Polynomial.{u4} R _inst_1), (LT.lt.{0} (WithBot.{0} Nat) (Preorder.toLT.{0} (WithBot.{0} Nat) (WithBot.preorder.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)))) (OfNat.ofNat.{0} (WithBot.{0} Nat) 0 (Zero.toOfNat0.{0} (WithBot.{0} Nat) (WithBot.zero.{0} Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)))) (Polynomial.degree.{u4} R _inst_1 p)) -> (Ne.{succ u3} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => S) (Polynomial.leadingCoeff.{u4} R _inst_1 p)) (FunLike.coe.{max (succ u4) (succ u3), succ u4, succ u3} (RingHom.{u4, u3} R S (Semiring.toNonAssocSemiring.{u4} R _inst_1) (NonAssocRing.toNonAssocSemiring.{u3} S (Ring.toNonAssocRing.{u3} S _inst_2))) R (fun (_x : R) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => S) _x) (MulHomClass.toFunLike.{max u4 u3, u4, u3} (RingHom.{u4, u3} R S (Semiring.toNonAssocSemiring.{u4} R _inst_1) (NonAssocRing.toNonAssocSemiring.{u3} S (Ring.toNonAssocRing.{u3} S _inst_2))) R S (NonUnitalNonAssocSemiring.toMul.{u4} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u4} R (Semiring.toNonAssocSemiring.{u4} R _inst_1))) (NonUnitalNonAssocSemiring.toMul.{u3} S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} S (NonAssocRing.toNonAssocSemiring.{u3} S (Ring.toNonAssocRing.{u3} S _inst_2)))) (NonUnitalRingHomClass.toMulHomClass.{max u4 u3, u4, u3} (RingHom.{u4, u3} R S (Semiring.toNonAssocSemiring.{u4} R _inst_1) (NonAssocRing.toNonAssocSemiring.{u3} S (Ring.toNonAssocRing.{u3} S _inst_2))) R S (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u4} R (Semiring.toNonAssocSemiring.{u4} R _inst_1)) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} S (NonAssocRing.toNonAssocSemiring.{u3} S (Ring.toNonAssocRing.{u3} S _inst_2))) (RingHomClass.toNonUnitalRingHomClass.{max u4 u3, u4, u3} (RingHom.{u4, u3} R S (Semiring.toNonAssocSemiring.{u4} R _inst_1) (NonAssocRing.toNonAssocSemiring.{u3} S (Ring.toNonAssocRing.{u3} S _inst_2))) R S (Semiring.toNonAssocSemiring.{u4} R _inst_1) (NonAssocRing.toNonAssocSemiring.{u3} S (Ring.toNonAssocRing.{u3} S _inst_2)) (RingHom.instRingHomClassRingHom.{u4, u3} R S (Semiring.toNonAssocSemiring.{u4} R _inst_1) (NonAssocRing.toNonAssocSemiring.{u3} S (Ring.toNonAssocRing.{u3} S _inst_2)))))) f (Polynomial.leadingCoeff.{u4} R _inst_1 p)) (OfNat.ofNat.{u3} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => S) (Polynomial.leadingCoeff.{u4} R _inst_1 p)) 0 (Zero.toOfNat0.{u3} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => S) (Polynomial.leadingCoeff.{u4} R _inst_1 p)) (MonoidWithZero.toZero.{u3} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => S) (Polynomial.leadingCoeff.{u4} R _inst_1 p)) (Semiring.toMonoidWithZero.{u3} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => S) (Polynomial.leadingCoeff.{u4} R _inst_1 p)) (Ring.toSemiring.{u3} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => S) (Polynomial.leadingCoeff.{u4} R _inst_1 p)) _inst_2)))))) -> (forall {l : Filter.{u1} α} {z : α -> S}, (Filter.Tendsto.{u1, u2} α k (Function.comp.{succ u1, succ u3, succ u2} α S k abv z) l (Filter.atTop.{u2} k (PartialOrder.toPreorder.{u2} k (StrictOrderedRing.toPartialOrder.{u2} k (LinearOrderedRing.toStrictOrderedRing.{u2} k (LinearOrderedCommRing.toLinearOrderedRing.{u2} k (LinearOrderedField.toLinearOrderedCommRing.{u2} k _inst_3))))))) -> (Filter.Tendsto.{u1, u2} α k (fun (x : α) => abv (Polynomial.eval₂.{u4, u3} R S _inst_1 (Ring.toSemiring.{u3} S _inst_2) f (z x) p)) l (Filter.atTop.{u2} k (PartialOrder.toPreorder.{u2} k (StrictOrderedRing.toPartialOrder.{u2} k (LinearOrderedRing.toStrictOrderedRing.{u2} k (LinearOrderedCommRing.toLinearOrderedRing.{u2} k (LinearOrderedField.toLinearOrderedCommRing.{u2} k _inst_3))))))))
Case conversion may be inaccurate. Consider using '#align polynomial.tendsto_abv_eval₂_at_top Polynomial.tendsto_abv_eval₂_atTopₓ'. -/
theorem tendsto_abv_eval₂_atTop {R S k α : Type _} [Semiring R] [Ring S] [LinearOrderedField k]
    (f : R →+* S) (abv : S → k) [IsAbsoluteValue abv] (p : R[X]) (hd : 0 < degree p)
    (hf : f p.leadingCoeff ≠ 0) {l : Filter α} {z : α → S} (hz : Tendsto (abv ∘ z) l atTop) :
    Tendsto (fun x => abv (p.eval₂ f (z x))) l atTop :=
  by
  revert hf; refine' degree_pos_induction_on p hd _ _ _ <;> clear hd p
  · rintro c - hc
    rw [leading_coeff_mul_X, leading_coeff_C] at hc
    simpa [abv_mul abv] using hz.const_mul_at_top ((abv_pos abv).2 hc)
  · intro p hpd ihp hf
    rw [leading_coeff_mul_X] at hf
    simpa [abv_mul abv] using (ihp hf).atTop_mul_atTop hz
  · intro p a hd ihp hf
    rw [add_comm, leading_coeff_add_of_degree_lt (degree_C_le.trans_lt hd)] at hf
    refine' tendsto_at_top_of_add_const_right (abv (-f a)) _
    refine' tendsto_at_top_mono (fun _ => abv_add abv _ _) _
    simpa using ihp hf
#align polynomial.tendsto_abv_eval₂_at_top Polynomial.tendsto_abv_eval₂_atTop

/- warning: polynomial.tendsto_abv_at_top -> Polynomial.tendsto_abv_atTop is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {k : Type.{u2}} {α : Type.{u3}} [_inst_1 : Ring.{u1} R] [_inst_2 : LinearOrderedField.{u2} k] (abv : R -> k) [_inst_3 : IsAbsoluteValue.{u2, u1} k (StrictOrderedSemiring.toOrderedSemiring.{u2} k (StrictOrderedRing.toStrictOrderedSemiring.{u2} k (LinearOrderedRing.toStrictOrderedRing.{u2} k (LinearOrderedCommRing.toLinearOrderedRing.{u2} k (LinearOrderedField.toLinearOrderedCommRing.{u2} k _inst_2))))) R (Ring.toSemiring.{u1} R _inst_1) abv] (p : Polynomial.{u1} R (Ring.toSemiring.{u1} R _inst_1)), (LT.lt.{0} (WithBot.{0} Nat) (Preorder.toLT.{0} (WithBot.{0} Nat) (WithBot.preorder.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))))) (OfNat.ofNat.{0} (WithBot.{0} Nat) 0 (OfNat.mk.{0} (WithBot.{0} Nat) 0 (Zero.zero.{0} (WithBot.{0} Nat) (WithBot.hasZero.{0} Nat Nat.hasZero)))) (Polynomial.degree.{u1} R (Ring.toSemiring.{u1} R _inst_1) p)) -> (forall {l : Filter.{u3} α} {z : α -> R}, (Filter.Tendsto.{u3, u2} α k (Function.comp.{succ u3, succ u1, succ u2} α R k abv z) l (Filter.atTop.{u2} k (PartialOrder.toPreorder.{u2} k (OrderedAddCommGroup.toPartialOrder.{u2} k (StrictOrderedRing.toOrderedAddCommGroup.{u2} k (LinearOrderedRing.toStrictOrderedRing.{u2} k (LinearOrderedCommRing.toLinearOrderedRing.{u2} k (LinearOrderedField.toLinearOrderedCommRing.{u2} k _inst_2)))))))) -> (Filter.Tendsto.{u3, u2} α k (fun (x : α) => abv (Polynomial.eval.{u1} R (Ring.toSemiring.{u1} R _inst_1) (z x) p)) l (Filter.atTop.{u2} k (PartialOrder.toPreorder.{u2} k (OrderedAddCommGroup.toPartialOrder.{u2} k (StrictOrderedRing.toOrderedAddCommGroup.{u2} k (LinearOrderedRing.toStrictOrderedRing.{u2} k (LinearOrderedCommRing.toLinearOrderedRing.{u2} k (LinearOrderedField.toLinearOrderedCommRing.{u2} k _inst_2)))))))))
but is expected to have type
  forall {R : Type.{u3}} {k : Type.{u2}} {α : Type.{u1}} [_inst_1 : Ring.{u3} R] [_inst_2 : LinearOrderedField.{u2} k] (abv : R -> k) [_inst_3 : IsAbsoluteValue.{u2, u3} k (OrderedCommSemiring.toOrderedSemiring.{u2} k (StrictOrderedCommSemiring.toOrderedCommSemiring.{u2} k (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{u2} k (LinearOrderedSemifield.toLinearOrderedCommSemiring.{u2} k (LinearOrderedField.toLinearOrderedSemifield.{u2} k _inst_2))))) R (Ring.toSemiring.{u3} R _inst_1) abv] (p : Polynomial.{u3} R (Ring.toSemiring.{u3} R _inst_1)), (LT.lt.{0} (WithBot.{0} Nat) (Preorder.toLT.{0} (WithBot.{0} Nat) (WithBot.preorder.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)))) (OfNat.ofNat.{0} (WithBot.{0} Nat) 0 (Zero.toOfNat0.{0} (WithBot.{0} Nat) (WithBot.zero.{0} Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)))) (Polynomial.degree.{u3} R (Ring.toSemiring.{u3} R _inst_1) p)) -> (forall {l : Filter.{u1} α} {z : α -> R}, (Filter.Tendsto.{u1, u2} α k (Function.comp.{succ u1, succ u3, succ u2} α R k abv z) l (Filter.atTop.{u2} k (PartialOrder.toPreorder.{u2} k (StrictOrderedRing.toPartialOrder.{u2} k (LinearOrderedRing.toStrictOrderedRing.{u2} k (LinearOrderedCommRing.toLinearOrderedRing.{u2} k (LinearOrderedField.toLinearOrderedCommRing.{u2} k _inst_2))))))) -> (Filter.Tendsto.{u1, u2} α k (fun (x : α) => abv (Polynomial.eval.{u3} R (Ring.toSemiring.{u3} R _inst_1) (z x) p)) l (Filter.atTop.{u2} k (PartialOrder.toPreorder.{u2} k (StrictOrderedRing.toPartialOrder.{u2} k (LinearOrderedRing.toStrictOrderedRing.{u2} k (LinearOrderedCommRing.toLinearOrderedRing.{u2} k (LinearOrderedField.toLinearOrderedCommRing.{u2} k _inst_2))))))))
Case conversion may be inaccurate. Consider using '#align polynomial.tendsto_abv_at_top Polynomial.tendsto_abv_atTopₓ'. -/
theorem tendsto_abv_atTop {R k α : Type _} [Ring R] [LinearOrderedField k] (abv : R → k)
    [IsAbsoluteValue abv] (p : R[X]) (h : 0 < degree p) {l : Filter α} {z : α → R}
    (hz : Tendsto (abv ∘ z) l atTop) : Tendsto (fun x => abv (p.eval (z x))) l atTop :=
  tendsto_abv_eval₂_atTop _ _ _ h (mt leadingCoeff_eq_zero.1 <| ne_zero_of_degree_gt h) hz
#align polynomial.tendsto_abv_at_top Polynomial.tendsto_abv_atTop

/- warning: polynomial.tendsto_abv_aeval_at_top -> Polynomial.tendsto_abv_aeval_atTop is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} {A : Type.{u2}} {k : Type.{u3}} {α : Type.{u4}} [_inst_1 : CommSemiring.{u1} R] [_inst_2 : Ring.{u2} A] [_inst_3 : Algebra.{u1, u2} R A _inst_1 (Ring.toSemiring.{u2} A _inst_2)] [_inst_4 : LinearOrderedField.{u3} k] (abv : A -> k) [_inst_5 : IsAbsoluteValue.{u3, u2} k (StrictOrderedSemiring.toOrderedSemiring.{u3} k (StrictOrderedRing.toStrictOrderedSemiring.{u3} k (LinearOrderedRing.toStrictOrderedRing.{u3} k (LinearOrderedCommRing.toLinearOrderedRing.{u3} k (LinearOrderedField.toLinearOrderedCommRing.{u3} k _inst_4))))) A (Ring.toSemiring.{u2} A _inst_2) abv] (p : Polynomial.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)), (LT.lt.{0} (WithBot.{0} Nat) (Preorder.toLT.{0} (WithBot.{0} Nat) (WithBot.preorder.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))))) (OfNat.ofNat.{0} (WithBot.{0} Nat) 0 (OfNat.mk.{0} (WithBot.{0} Nat) 0 (Zero.zero.{0} (WithBot.{0} Nat) (WithBot.hasZero.{0} Nat Nat.hasZero)))) (Polynomial.degree.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1) p)) -> (Ne.{succ u2} A (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (RingHom.{u1, u2} R A (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{u2} A (Ring.toSemiring.{u2} A _inst_2))) (fun (_x : RingHom.{u1, u2} R A (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{u2} A (Ring.toSemiring.{u2} A _inst_2))) => R -> A) (RingHom.hasCoeToFun.{u1, u2} R A (Semiring.toNonAssocSemiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Semiring.toNonAssocSemiring.{u2} A (Ring.toSemiring.{u2} A _inst_2))) (algebraMap.{u1, u2} R A _inst_1 (Ring.toSemiring.{u2} A _inst_2) _inst_3) (Polynomial.leadingCoeff.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1) p)) (OfNat.ofNat.{u2} A 0 (OfNat.mk.{u2} A 0 (Zero.zero.{u2} A (MulZeroClass.toHasZero.{u2} A (NonUnitalNonAssocSemiring.toMulZeroClass.{u2} A (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u2} A (NonAssocRing.toNonUnitalNonAssocRing.{u2} A (Ring.toNonAssocRing.{u2} A _inst_2))))))))) -> (forall {l : Filter.{u4} α} {z : α -> A}, (Filter.Tendsto.{u4, u3} α k (Function.comp.{succ u4, succ u2, succ u3} α A k abv z) l (Filter.atTop.{u3} k (PartialOrder.toPreorder.{u3} k (OrderedAddCommGroup.toPartialOrder.{u3} k (StrictOrderedRing.toOrderedAddCommGroup.{u3} k (LinearOrderedRing.toStrictOrderedRing.{u3} k (LinearOrderedCommRing.toLinearOrderedRing.{u3} k (LinearOrderedField.toLinearOrderedCommRing.{u3} k _inst_4)))))))) -> (Filter.Tendsto.{u4, u3} α k (fun (x : α) => abv (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (AlgHom.{u1, u1, u2} R (Polynomial.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) A _inst_1 (Polynomial.semiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Ring.toSemiring.{u2} A _inst_2) (Polynomial.algebraOfAlgebra.{u1, u1} R R _inst_1 (CommSemiring.toSemiring.{u1} R _inst_1) (Algebra.id.{u1} R _inst_1)) _inst_3) (fun (_x : AlgHom.{u1, u1, u2} R (Polynomial.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) A _inst_1 (Polynomial.semiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Ring.toSemiring.{u2} A _inst_2) (Polynomial.algebraOfAlgebra.{u1, u1} R R _inst_1 (CommSemiring.toSemiring.{u1} R _inst_1) (Algebra.id.{u1} R _inst_1)) _inst_3) => (Polynomial.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) -> A) ([anonymous].{u1, u1, u2} R (Polynomial.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) A _inst_1 (Polynomial.semiring.{u1} R (CommSemiring.toSemiring.{u1} R _inst_1)) (Ring.toSemiring.{u2} A _inst_2) (Polynomial.algebraOfAlgebra.{u1, u1} R R _inst_1 (CommSemiring.toSemiring.{u1} R _inst_1) (Algebra.id.{u1} R _inst_1)) _inst_3) (Polynomial.aeval.{u1, u2} R A _inst_1 (Ring.toSemiring.{u2} A _inst_2) _inst_3 (z x)) p)) l (Filter.atTop.{u3} k (PartialOrder.toPreorder.{u3} k (OrderedAddCommGroup.toPartialOrder.{u3} k (StrictOrderedRing.toOrderedAddCommGroup.{u3} k (LinearOrderedRing.toStrictOrderedRing.{u3} k (LinearOrderedCommRing.toLinearOrderedRing.{u3} k (LinearOrderedField.toLinearOrderedCommRing.{u3} k _inst_4)))))))))
but is expected to have type
  forall {R : Type.{u4}} {A : Type.{u3}} {k : Type.{u2}} {α : Type.{u1}} [_inst_1 : CommSemiring.{u4} R] [_inst_2 : Ring.{u3} A] [_inst_3 : Algebra.{u4, u3} R A _inst_1 (Ring.toSemiring.{u3} A _inst_2)] [_inst_4 : LinearOrderedField.{u2} k] (abv : A -> k) [_inst_5 : IsAbsoluteValue.{u2, u3} k (OrderedCommSemiring.toOrderedSemiring.{u2} k (StrictOrderedCommSemiring.toOrderedCommSemiring.{u2} k (LinearOrderedCommSemiring.toStrictOrderedCommSemiring.{u2} k (LinearOrderedSemifield.toLinearOrderedCommSemiring.{u2} k (LinearOrderedField.toLinearOrderedSemifield.{u2} k _inst_4))))) A (Ring.toSemiring.{u3} A _inst_2) abv] (p : Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)), (LT.lt.{0} (WithBot.{0} Nat) (Preorder.toLT.{0} (WithBot.{0} Nat) (WithBot.preorder.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)))) (OfNat.ofNat.{0} (WithBot.{0} Nat) 0 (Zero.toOfNat0.{0} (WithBot.{0} Nat) (WithBot.zero.{0} Nat (LinearOrderedCommMonoidWithZero.toZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)))) (Polynomial.degree.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1) p)) -> (Ne.{succ u3} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => A) (Polynomial.leadingCoeff.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1) p)) (FunLike.coe.{max (succ u4) (succ u3), succ u4, succ u3} (RingHom.{u4, u3} R A (Semiring.toNonAssocSemiring.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Semiring.toNonAssocSemiring.{u3} A (Ring.toSemiring.{u3} A _inst_2))) R (fun (_x : R) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => A) _x) (MulHomClass.toFunLike.{max u4 u3, u4, u3} (RingHom.{u4, u3} R A (Semiring.toNonAssocSemiring.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Semiring.toNonAssocSemiring.{u3} A (Ring.toSemiring.{u3} A _inst_2))) R A (NonUnitalNonAssocSemiring.toMul.{u4} R (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u4} R (Semiring.toNonAssocSemiring.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)))) (NonUnitalNonAssocSemiring.toMul.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A (Ring.toSemiring.{u3} A _inst_2)))) (NonUnitalRingHomClass.toMulHomClass.{max u4 u3, u4, u3} (RingHom.{u4, u3} R A (Semiring.toNonAssocSemiring.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Semiring.toNonAssocSemiring.{u3} A (Ring.toSemiring.{u3} A _inst_2))) R A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u4} R (Semiring.toNonAssocSemiring.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1))) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A (Ring.toSemiring.{u3} A _inst_2))) (RingHomClass.toNonUnitalRingHomClass.{max u4 u3, u4, u3} (RingHom.{u4, u3} R A (Semiring.toNonAssocSemiring.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Semiring.toNonAssocSemiring.{u3} A (Ring.toSemiring.{u3} A _inst_2))) R A (Semiring.toNonAssocSemiring.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Semiring.toNonAssocSemiring.{u3} A (Ring.toSemiring.{u3} A _inst_2)) (RingHom.instRingHomClassRingHom.{u4, u3} R A (Semiring.toNonAssocSemiring.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Semiring.toNonAssocSemiring.{u3} A (Ring.toSemiring.{u3} A _inst_2)))))) (algebraMap.{u4, u3} R A _inst_1 (Ring.toSemiring.{u3} A _inst_2) _inst_3) (Polynomial.leadingCoeff.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1) p)) (OfNat.ofNat.{u3} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => A) (Polynomial.leadingCoeff.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1) p)) 0 (Zero.toOfNat0.{u3} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => A) (Polynomial.leadingCoeff.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1) p)) (MonoidWithZero.toZero.{u3} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => A) (Polynomial.leadingCoeff.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1) p)) (Semiring.toMonoidWithZero.{u3} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => A) (Polynomial.leadingCoeff.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1) p)) (Ring.toSemiring.{u3} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : R) => A) (Polynomial.leadingCoeff.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1) p)) _inst_2)))))) -> (forall {l : Filter.{u1} α} {z : α -> A}, (Filter.Tendsto.{u1, u2} α k (Function.comp.{succ u1, succ u3, succ u2} α A k abv z) l (Filter.atTop.{u2} k (PartialOrder.toPreorder.{u2} k (StrictOrderedRing.toPartialOrder.{u2} k (LinearOrderedRing.toStrictOrderedRing.{u2} k (LinearOrderedCommRing.toLinearOrderedRing.{u2} k (LinearOrderedField.toLinearOrderedCommRing.{u2} k _inst_4))))))) -> (Filter.Tendsto.{u1, u2} α k (fun (x : α) => abv (FunLike.coe.{max (succ u3) (succ u4), succ u4, succ u3} (AlgHom.{u4, u4, u3} R (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) A _inst_1 (Polynomial.semiring.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Ring.toSemiring.{u3} A _inst_2) (Polynomial.algebraOfAlgebra.{u4, u4} R R _inst_1 (CommSemiring.toSemiring.{u4} R _inst_1) (Algebra.id.{u4} R _inst_1)) _inst_3) (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (fun (_x : Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) => (fun (x._@.Mathlib.Algebra.Hom.GroupAction._hyg.2186 : Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) => A) _x) (SMulHomClass.toFunLike.{max u3 u4, u4, u4, u3} (AlgHom.{u4, u4, u3} R (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) A _inst_1 (Polynomial.semiring.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Ring.toSemiring.{u3} A _inst_2) (Polynomial.algebraOfAlgebra.{u4, u4} R R _inst_1 (CommSemiring.toSemiring.{u4} R _inst_1) (Algebra.id.{u4} R _inst_1)) _inst_3) R (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) A (SMulZeroClass.toSMul.{u4, u4} R (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (AddMonoid.toZero.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (AddCommMonoid.toAddMonoid.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (NonUnitalNonAssocSemiring.toAddCommMonoid.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Semiring.toNonAssocSemiring.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Polynomial.semiring.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1))))))) (DistribSMul.toSMulZeroClass.{u4, u4} R (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (AddMonoid.toAddZeroClass.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (AddCommMonoid.toAddMonoid.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (NonUnitalNonAssocSemiring.toAddCommMonoid.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Semiring.toNonAssocSemiring.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Polynomial.semiring.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1))))))) (DistribMulAction.toDistribSMul.{u4, u4} R (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (MonoidWithZero.toMonoid.{u4} R (Semiring.toMonoidWithZero.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1))) (AddCommMonoid.toAddMonoid.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (NonUnitalNonAssocSemiring.toAddCommMonoid.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Semiring.toNonAssocSemiring.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Polynomial.semiring.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)))))) (Module.toDistribMulAction.{u4, u4} R (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (CommSemiring.toSemiring.{u4} R _inst_1) (NonUnitalNonAssocSemiring.toAddCommMonoid.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Semiring.toNonAssocSemiring.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Polynomial.semiring.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1))))) (Algebra.toModule.{u4, u4} R (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) _inst_1 (Polynomial.semiring.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Polynomial.algebraOfAlgebra.{u4, u4} R R _inst_1 (CommSemiring.toSemiring.{u4} R _inst_1) (Algebra.id.{u4} R _inst_1))))))) (SMulZeroClass.toSMul.{u4, u3} R A (AddMonoid.toZero.{u3} A (AddCommMonoid.toAddMonoid.{u3} A (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A (Ring.toSemiring.{u3} A _inst_2)))))) (DistribSMul.toSMulZeroClass.{u4, u3} R A (AddMonoid.toAddZeroClass.{u3} A (AddCommMonoid.toAddMonoid.{u3} A (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A (Ring.toSemiring.{u3} A _inst_2)))))) (DistribMulAction.toDistribSMul.{u4, u3} R A (MonoidWithZero.toMonoid.{u4} R (Semiring.toMonoidWithZero.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1))) (AddCommMonoid.toAddMonoid.{u3} A (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A (Ring.toSemiring.{u3} A _inst_2))))) (Module.toDistribMulAction.{u4, u3} R A (CommSemiring.toSemiring.{u4} R _inst_1) (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A (Ring.toSemiring.{u3} A _inst_2)))) (Algebra.toModule.{u4, u3} R A _inst_1 (Ring.toSemiring.{u3} A _inst_2) _inst_3))))) (DistribMulActionHomClass.toSMulHomClass.{max u3 u4, u4, u4, u3} (AlgHom.{u4, u4, u3} R (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) A _inst_1 (Polynomial.semiring.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Ring.toSemiring.{u3} A _inst_2) (Polynomial.algebraOfAlgebra.{u4, u4} R R _inst_1 (CommSemiring.toSemiring.{u4} R _inst_1) (Algebra.id.{u4} R _inst_1)) _inst_3) R (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) A (MonoidWithZero.toMonoid.{u4} R (Semiring.toMonoidWithZero.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1))) (AddCommMonoid.toAddMonoid.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (NonUnitalNonAssocSemiring.toAddCommMonoid.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Semiring.toNonAssocSemiring.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Polynomial.semiring.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)))))) (AddCommMonoid.toAddMonoid.{u3} A (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A (Ring.toSemiring.{u3} A _inst_2))))) (Module.toDistribMulAction.{u4, u4} R (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (CommSemiring.toSemiring.{u4} R _inst_1) (NonUnitalNonAssocSemiring.toAddCommMonoid.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Semiring.toNonAssocSemiring.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Polynomial.semiring.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1))))) (Algebra.toModule.{u4, u4} R (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) _inst_1 (Polynomial.semiring.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Polynomial.algebraOfAlgebra.{u4, u4} R R _inst_1 (CommSemiring.toSemiring.{u4} R _inst_1) (Algebra.id.{u4} R _inst_1)))) (Module.toDistribMulAction.{u4, u3} R A (CommSemiring.toSemiring.{u4} R _inst_1) (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A (Ring.toSemiring.{u3} A _inst_2)))) (Algebra.toModule.{u4, u3} R A _inst_1 (Ring.toSemiring.{u3} A _inst_2) _inst_3)) (NonUnitalAlgHomClass.toDistribMulActionHomClass.{max u3 u4, u4, u4, u3} (AlgHom.{u4, u4, u3} R (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) A _inst_1 (Polynomial.semiring.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Ring.toSemiring.{u3} A _inst_2) (Polynomial.algebraOfAlgebra.{u4, u4} R R _inst_1 (CommSemiring.toSemiring.{u4} R _inst_1) (Algebra.id.{u4} R _inst_1)) _inst_3) R (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) A (MonoidWithZero.toMonoid.{u4} R (Semiring.toMonoidWithZero.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1))) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Semiring.toNonAssocSemiring.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Polynomial.semiring.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)))) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A (Ring.toSemiring.{u3} A _inst_2))) (Module.toDistribMulAction.{u4, u4} R (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (CommSemiring.toSemiring.{u4} R _inst_1) (NonUnitalNonAssocSemiring.toAddCommMonoid.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Semiring.toNonAssocSemiring.{u4} (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Polynomial.semiring.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1))))) (Algebra.toModule.{u4, u4} R (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) _inst_1 (Polynomial.semiring.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Polynomial.algebraOfAlgebra.{u4, u4} R R _inst_1 (CommSemiring.toSemiring.{u4} R _inst_1) (Algebra.id.{u4} R _inst_1)))) (Module.toDistribMulAction.{u4, u3} R A (CommSemiring.toSemiring.{u4} R _inst_1) (NonUnitalNonAssocSemiring.toAddCommMonoid.{u3} A (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} A (Semiring.toNonAssocSemiring.{u3} A (Ring.toSemiring.{u3} A _inst_2)))) (Algebra.toModule.{u4, u3} R A _inst_1 (Ring.toSemiring.{u3} A _inst_2) _inst_3)) (AlgHom.instNonUnitalAlgHomClassToMonoidToMonoidWithZeroToSemiringToNonUnitalNonAssocSemiringToNonAssocSemiringToNonUnitalNonAssocSemiringToNonAssocSemiringToDistribMulActionToAddCommMonoidToModuleToDistribMulActionToAddCommMonoidToModule.{u4, u4, u3, max u3 u4} R (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) A _inst_1 (Polynomial.semiring.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Ring.toSemiring.{u3} A _inst_2) (Polynomial.algebraOfAlgebra.{u4, u4} R R _inst_1 (CommSemiring.toSemiring.{u4} R _inst_1) (Algebra.id.{u4} R _inst_1)) _inst_3 (AlgHom.{u4, u4, u3} R (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) A _inst_1 (Polynomial.semiring.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Ring.toSemiring.{u3} A _inst_2) (Polynomial.algebraOfAlgebra.{u4, u4} R R _inst_1 (CommSemiring.toSemiring.{u4} R _inst_1) (Algebra.id.{u4} R _inst_1)) _inst_3) (AlgHom.algHomClass.{u4, u4, u3} R (Polynomial.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) A _inst_1 (Polynomial.semiring.{u4} R (CommSemiring.toSemiring.{u4} R _inst_1)) (Ring.toSemiring.{u3} A _inst_2) (Polynomial.algebraOfAlgebra.{u4, u4} R R _inst_1 (CommSemiring.toSemiring.{u4} R _inst_1) (Algebra.id.{u4} R _inst_1)) _inst_3))))) (Polynomial.aeval.{u4, u3} R A _inst_1 (Ring.toSemiring.{u3} A _inst_2) _inst_3 (z x)) p)) l (Filter.atTop.{u2} k (PartialOrder.toPreorder.{u2} k (StrictOrderedRing.toPartialOrder.{u2} k (LinearOrderedRing.toStrictOrderedRing.{u2} k (LinearOrderedCommRing.toLinearOrderedRing.{u2} k (LinearOrderedField.toLinearOrderedCommRing.{u2} k _inst_4))))))))
Case conversion may be inaccurate. Consider using '#align polynomial.tendsto_abv_aeval_at_top Polynomial.tendsto_abv_aeval_atTopₓ'. -/
theorem tendsto_abv_aeval_atTop {R A k α : Type _} [CommSemiring R] [Ring A] [Algebra R A]
    [LinearOrderedField k] (abv : A → k) [IsAbsoluteValue abv] (p : R[X]) (hd : 0 < degree p)
    (h₀ : algebraMap R A p.leadingCoeff ≠ 0) {l : Filter α} {z : α → A}
    (hz : Tendsto (abv ∘ z) l atTop) : Tendsto (fun x => abv (aeval (z x) p)) l atTop :=
  tendsto_abv_eval₂_atTop _ abv p hd h₀ hz
#align polynomial.tendsto_abv_aeval_at_top Polynomial.tendsto_abv_aeval_atTop

variable {α R : Type _} [NormedRing R] [IsAbsoluteValue (norm : R → ℝ)]

#print Polynomial.tendsto_norm_atTop /-
theorem tendsto_norm_atTop (p : R[X]) (h : 0 < degree p) {l : Filter α} {z : α → R}
    (hz : Tendsto (fun x => ‖z x‖) l atTop) : Tendsto (fun x => ‖p.eval (z x)‖) l atTop :=
  p.tendsto_abv_atTop norm h hz
#align polynomial.tendsto_norm_at_top Polynomial.tendsto_norm_atTop
-/

/- warning: polynomial.exists_forall_norm_le -> Polynomial.exists_forall_norm_le is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : NormedRing.{u1} R] [_inst_2 : IsAbsoluteValue.{0, u1} Real Real.orderedSemiring R (Ring.toSemiring.{u1} R (NormedRing.toRing.{u1} R _inst_1)) (Norm.norm.{u1} R (NormedRing.toHasNorm.{u1} R _inst_1))] [_inst_3 : ProperSpace.{u1} R (SeminormedRing.toPseudoMetricSpace.{u1} R (NormedRing.toSeminormedRing.{u1} R _inst_1))] (p : Polynomial.{u1} R (Ring.toSemiring.{u1} R (NormedRing.toRing.{u1} R _inst_1))), Exists.{succ u1} R (fun (x : R) => forall (y : R), LE.le.{0} Real Real.hasLe (Norm.norm.{u1} R (NormedRing.toHasNorm.{u1} R _inst_1) (Polynomial.eval.{u1} R (Ring.toSemiring.{u1} R (NormedRing.toRing.{u1} R _inst_1)) x p)) (Norm.norm.{u1} R (NormedRing.toHasNorm.{u1} R _inst_1) (Polynomial.eval.{u1} R (Ring.toSemiring.{u1} R (NormedRing.toRing.{u1} R _inst_1)) y p)))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : NormedRing.{u1} R] [_inst_2 : IsAbsoluteValue.{0, u1} Real Real.orderedSemiring R (Ring.toSemiring.{u1} R (NormedRing.toRing.{u1} R _inst_1)) (Norm.norm.{u1} R (NormedRing.toNorm.{u1} R _inst_1))] [_inst_3 : ProperSpace.{u1} R (SeminormedRing.toPseudoMetricSpace.{u1} R (NormedRing.toSeminormedRing.{u1} R _inst_1))] (p : Polynomial.{u1} R (Ring.toSemiring.{u1} R (NormedRing.toRing.{u1} R _inst_1))), Exists.{succ u1} R (fun (x : R) => forall (y : R), LE.le.{0} Real Real.instLEReal (Norm.norm.{u1} R (NormedRing.toNorm.{u1} R _inst_1) (Polynomial.eval.{u1} R (Ring.toSemiring.{u1} R (NormedRing.toRing.{u1} R _inst_1)) x p)) (Norm.norm.{u1} R (NormedRing.toNorm.{u1} R _inst_1) (Polynomial.eval.{u1} R (Ring.toSemiring.{u1} R (NormedRing.toRing.{u1} R _inst_1)) y p)))
Case conversion may be inaccurate. Consider using '#align polynomial.exists_forall_norm_le Polynomial.exists_forall_norm_leₓ'. -/
theorem exists_forall_norm_le [ProperSpace R] (p : R[X]) : ∃ x, ∀ y, ‖p.eval x‖ ≤ ‖p.eval y‖ :=
  if hp0 : 0 < degree p then
    p.Continuous.norm.exists_forall_le <| p.tendsto_norm_atTop hp0 tendsto_norm_cocompact_atTop
  else ⟨p.coeff 0, by rw [eq_C_of_degree_le_zero (le_of_not_gt hp0)] <;> simp⟩
#align polynomial.exists_forall_norm_le Polynomial.exists_forall_norm_le

section Roots

open Polynomial NNReal

variable {F K : Type _} [CommRing F] [NormedField K]

open Multiset

/- warning: polynomial.eq_one_of_roots_le -> Polynomial.eq_one_of_roots_le is a dubious translation:
lean 3 declaration is
  forall {F : Type.{u1}} {K : Type.{u2}} [_inst_3 : CommRing.{u1} F] [_inst_4 : NormedField.{u2} K] {p : Polynomial.{u1} F (Ring.toSemiring.{u1} F (CommRing.toRing.{u1} F _inst_3))} {f : RingHom.{u1, u2} F K (NonAssocRing.toNonAssocSemiring.{u1} F (Ring.toNonAssocRing.{u1} F (CommRing.toRing.{u1} F _inst_3))) (NonAssocRing.toNonAssocSemiring.{u2} K (Ring.toNonAssocRing.{u2} K (NormedRing.toRing.{u2} K (NormedCommRing.toNormedRing.{u2} K (NormedField.toNormedCommRing.{u2} K _inst_4)))))} {B : Real}, (LT.lt.{0} Real Real.hasLt B (OfNat.ofNat.{0} Real 0 (OfNat.mk.{0} Real 0 (Zero.zero.{0} Real Real.hasZero)))) -> (Polynomial.Monic.{u1} F (Ring.toSemiring.{u1} F (CommRing.toRing.{u1} F _inst_3)) p) -> (Polynomial.Splits.{u1, u2} F K _inst_3 (NormedField.toField.{u2} K _inst_4) f p) -> (forall (z : K), (Membership.Mem.{u2, u2} K (Multiset.{u2} K) (Multiset.hasMem.{u2} K) z (Polynomial.roots.{u2} K (SeminormedCommRing.toCommRing.{u2} K (NormedCommRing.toSeminormedCommRing.{u2} K (NormedField.toNormedCommRing.{u2} K _inst_4))) (Field.isDomain.{u2} K (NormedField.toField.{u2} K _inst_4)) (Polynomial.map.{u1, u2} F K (Ring.toSemiring.{u1} F (CommRing.toRing.{u1} F _inst_3)) (Ring.toSemiring.{u2} K (NormedRing.toRing.{u2} K (NormedCommRing.toNormedRing.{u2} K (NormedField.toNormedCommRing.{u2} K _inst_4)))) f p))) -> (LE.le.{0} Real Real.hasLe (Norm.norm.{u2} K (NormedField.toHasNorm.{u2} K _inst_4) z) B)) -> (Eq.{succ u1} (Polynomial.{u1} F (Ring.toSemiring.{u1} F (CommRing.toRing.{u1} F _inst_3))) p (OfNat.ofNat.{u1} (Polynomial.{u1} F (Ring.toSemiring.{u1} F (CommRing.toRing.{u1} F _inst_3))) 1 (OfNat.mk.{u1} (Polynomial.{u1} F (Ring.toSemiring.{u1} F (CommRing.toRing.{u1} F _inst_3))) 1 (One.one.{u1} (Polynomial.{u1} F (Ring.toSemiring.{u1} F (CommRing.toRing.{u1} F _inst_3))) (Polynomial.hasOne.{u1} F (Ring.toSemiring.{u1} F (CommRing.toRing.{u1} F _inst_3)))))))
but is expected to have type
  forall {F : Type.{u2}} {K : Type.{u1}} [_inst_3 : CommRing.{u2} F] [_inst_4 : NormedField.{u1} K] {p : Polynomial.{u2} F (Ring.toSemiring.{u2} F (CommRing.toRing.{u2} F _inst_3))} {f : RingHom.{u2, u1} F K (NonAssocRing.toNonAssocSemiring.{u2} F (Ring.toNonAssocRing.{u2} F (CommRing.toRing.{u2} F _inst_3))) (NonAssocRing.toNonAssocSemiring.{u1} K (Ring.toNonAssocRing.{u1} K (NormedRing.toRing.{u1} K (NormedCommRing.toNormedRing.{u1} K (NormedField.toNormedCommRing.{u1} K _inst_4)))))} {B : Real}, (LT.lt.{0} Real Real.instLTReal B (OfNat.ofNat.{0} Real 0 (Zero.toOfNat0.{0} Real Real.instZeroReal))) -> (Polynomial.Monic.{u2} F (Ring.toSemiring.{u2} F (CommRing.toRing.{u2} F _inst_3)) p) -> (Polynomial.Splits.{u2, u1} F K _inst_3 (NormedField.toField.{u1} K _inst_4) f p) -> (forall (z : K), (Membership.mem.{u1, u1} K (Multiset.{u1} K) (Multiset.instMembershipMultiset.{u1} K) z (Polynomial.roots.{u1} K (EuclideanDomain.toCommRing.{u1} K (Field.toEuclideanDomain.{u1} K (NormedField.toField.{u1} K _inst_4))) (EuclideanDomain.instIsDomainToSemiringToRingToCommRing.{u1} K (Field.toEuclideanDomain.{u1} K (NormedField.toField.{u1} K _inst_4))) (Polynomial.map.{u2, u1} F K (Ring.toSemiring.{u2} F (CommRing.toRing.{u2} F _inst_3)) (DivisionSemiring.toSemiring.{u1} K (Semifield.toDivisionSemiring.{u1} K (Field.toSemifield.{u1} K (NormedField.toField.{u1} K _inst_4)))) f p))) -> (LE.le.{0} Real Real.instLEReal (Norm.norm.{u1} K (NormedField.toNorm.{u1} K _inst_4) z) B)) -> (Eq.{succ u2} (Polynomial.{u2} F (Ring.toSemiring.{u2} F (CommRing.toRing.{u2} F _inst_3))) p (OfNat.ofNat.{u2} (Polynomial.{u2} F (Ring.toSemiring.{u2} F (CommRing.toRing.{u2} F _inst_3))) 1 (One.toOfNat1.{u2} (Polynomial.{u2} F (Ring.toSemiring.{u2} F (CommRing.toRing.{u2} F _inst_3))) (Polynomial.one.{u2} F (Ring.toSemiring.{u2} F (CommRing.toRing.{u2} F _inst_3))))))
Case conversion may be inaccurate. Consider using '#align polynomial.eq_one_of_roots_le Polynomial.eq_one_of_roots_leₓ'. -/
theorem eq_one_of_roots_le {p : F[X]} {f : F →+* K} {B : ℝ} (hB : B < 0) (h1 : p.Monic)
    (h2 : Splits f p) (h3 : ∀ z ∈ (map f p).roots, ‖z‖ ≤ B) : p = 1 :=
  h1.natDegree_eq_zero_iff_eq_one.mp
    (by
      contrapose! hB
      rw [← h1.nat_degree_map f, nat_degree_eq_card_roots' h2] at hB
      obtain ⟨z, hz⟩ := card_pos_iff_exists_mem.mp (zero_lt_iff.mpr hB)
      exact le_trans (norm_nonneg _) (h3 z hz))
#align polynomial.eq_one_of_roots_le Polynomial.eq_one_of_roots_le

/- warning: polynomial.coeff_le_of_roots_le -> Polynomial.coeff_le_of_roots_le is a dubious translation:
lean 3 declaration is
  forall {F : Type.{u1}} {K : Type.{u2}} [_inst_3 : CommRing.{u1} F] [_inst_4 : NormedField.{u2} K] {p : Polynomial.{u1} F (Ring.toSemiring.{u1} F (CommRing.toRing.{u1} F _inst_3))} {f : RingHom.{u1, u2} F K (NonAssocRing.toNonAssocSemiring.{u1} F (Ring.toNonAssocRing.{u1} F (CommRing.toRing.{u1} F _inst_3))) (NonAssocRing.toNonAssocSemiring.{u2} K (Ring.toNonAssocRing.{u2} K (NormedRing.toRing.{u2} K (NormedCommRing.toNormedRing.{u2} K (NormedField.toNormedCommRing.{u2} K _inst_4)))))} {B : Real} (i : Nat), (Polynomial.Monic.{u1} F (Ring.toSemiring.{u1} F (CommRing.toRing.{u1} F _inst_3)) p) -> (Polynomial.Splits.{u1, u2} F K _inst_3 (NormedField.toField.{u2} K _inst_4) f p) -> (forall (z : K), (Membership.Mem.{u2, u2} K (Multiset.{u2} K) (Multiset.hasMem.{u2} K) z (Polynomial.roots.{u2} K (SeminormedCommRing.toCommRing.{u2} K (NormedCommRing.toSeminormedCommRing.{u2} K (NormedField.toNormedCommRing.{u2} K _inst_4))) (Field.isDomain.{u2} K (NormedField.toField.{u2} K _inst_4)) (Polynomial.map.{u1, u2} F K (Ring.toSemiring.{u1} F (CommRing.toRing.{u1} F _inst_3)) (Ring.toSemiring.{u2} K (NormedRing.toRing.{u2} K (NormedCommRing.toNormedRing.{u2} K (NormedField.toNormedCommRing.{u2} K _inst_4)))) f p))) -> (LE.le.{0} Real Real.hasLe (Norm.norm.{u2} K (NormedField.toHasNorm.{u2} K _inst_4) z) B)) -> (LE.le.{0} Real Real.hasLe (Norm.norm.{u2} K (NormedField.toHasNorm.{u2} K _inst_4) (Polynomial.coeff.{u2} K (Ring.toSemiring.{u2} K (NormedRing.toRing.{u2} K (NormedCommRing.toNormedRing.{u2} K (NormedField.toNormedCommRing.{u2} K _inst_4)))) (Polynomial.map.{u1, u2} F K (Ring.toSemiring.{u1} F (CommRing.toRing.{u1} F _inst_3)) (Ring.toSemiring.{u2} K (NormedRing.toRing.{u2} K (NormedCommRing.toNormedRing.{u2} K (NormedField.toNormedCommRing.{u2} K _inst_4)))) f p) i)) (HMul.hMul.{0, 0, 0} Real Real Real (instHMul.{0} Real Real.hasMul) (HPow.hPow.{0, 0, 0} Real Nat Real (instHPow.{0, 0} Real Nat (Monoid.Pow.{0} Real Real.monoid)) B (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) (Polynomial.natDegree.{u1} F (Ring.toSemiring.{u1} F (CommRing.toRing.{u1} F _inst_3)) p) i)) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Real (HasLiftT.mk.{1, 1} Nat Real (CoeTCₓ.coe.{1, 1} Nat Real (Nat.castCoe.{0} Real Real.hasNatCast))) (Nat.choose (Polynomial.natDegree.{u1} F (Ring.toSemiring.{u1} F (CommRing.toRing.{u1} F _inst_3)) p) i))))
but is expected to have type
  forall {F : Type.{u2}} {K : Type.{u1}} [_inst_3 : CommRing.{u2} F] [_inst_4 : NormedField.{u1} K] {p : Polynomial.{u2} F (Ring.toSemiring.{u2} F (CommRing.toRing.{u2} F _inst_3))} {f : RingHom.{u2, u1} F K (NonAssocRing.toNonAssocSemiring.{u2} F (Ring.toNonAssocRing.{u2} F (CommRing.toRing.{u2} F _inst_3))) (NonAssocRing.toNonAssocSemiring.{u1} K (Ring.toNonAssocRing.{u1} K (NormedRing.toRing.{u1} K (NormedCommRing.toNormedRing.{u1} K (NormedField.toNormedCommRing.{u1} K _inst_4)))))} {B : Real} (i : Nat), (Polynomial.Monic.{u2} F (Ring.toSemiring.{u2} F (CommRing.toRing.{u2} F _inst_3)) p) -> (Polynomial.Splits.{u2, u1} F K _inst_3 (NormedField.toField.{u1} K _inst_4) f p) -> (forall (z : K), (Membership.mem.{u1, u1} K (Multiset.{u1} K) (Multiset.instMembershipMultiset.{u1} K) z (Polynomial.roots.{u1} K (EuclideanDomain.toCommRing.{u1} K (Field.toEuclideanDomain.{u1} K (NormedField.toField.{u1} K _inst_4))) (EuclideanDomain.instIsDomainToSemiringToRingToCommRing.{u1} K (Field.toEuclideanDomain.{u1} K (NormedField.toField.{u1} K _inst_4))) (Polynomial.map.{u2, u1} F K (Ring.toSemiring.{u2} F (CommRing.toRing.{u2} F _inst_3)) (DivisionSemiring.toSemiring.{u1} K (Semifield.toDivisionSemiring.{u1} K (Field.toSemifield.{u1} K (NormedField.toField.{u1} K _inst_4)))) f p))) -> (LE.le.{0} Real Real.instLEReal (Norm.norm.{u1} K (NormedField.toNorm.{u1} K _inst_4) z) B)) -> (LE.le.{0} Real Real.instLEReal (Norm.norm.{u1} K (NormedField.toNorm.{u1} K _inst_4) (Polynomial.coeff.{u1} K (DivisionSemiring.toSemiring.{u1} K (Semifield.toDivisionSemiring.{u1} K (Field.toSemifield.{u1} K (NormedField.toField.{u1} K _inst_4)))) (Polynomial.map.{u2, u1} F K (Ring.toSemiring.{u2} F (CommRing.toRing.{u2} F _inst_3)) (DivisionSemiring.toSemiring.{u1} K (Semifield.toDivisionSemiring.{u1} K (Field.toSemifield.{u1} K (NormedField.toField.{u1} K _inst_4)))) f p) i)) (HMul.hMul.{0, 0, 0} Real Real Real (instHMul.{0} Real Real.instMulReal) (HPow.hPow.{0, 0, 0} Real Nat Real (instHPow.{0, 0} Real Nat (Monoid.Pow.{0} Real Real.instMonoidReal)) B (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (Polynomial.natDegree.{u2} F (Ring.toSemiring.{u2} F (CommRing.toRing.{u2} F _inst_3)) p) i)) (Nat.cast.{0} Real Real.natCast (Nat.choose (Polynomial.natDegree.{u2} F (Ring.toSemiring.{u2} F (CommRing.toRing.{u2} F _inst_3)) p) i))))
Case conversion may be inaccurate. Consider using '#align polynomial.coeff_le_of_roots_le Polynomial.coeff_le_of_roots_leₓ'. -/
theorem coeff_le_of_roots_le {p : F[X]} {f : F →+* K} {B : ℝ} (i : ℕ) (h1 : p.Monic)
    (h2 : Splits f p) (h3 : ∀ z ∈ (map f p).roots, ‖z‖ ≤ B) :
    ‖(map f p).coeff i‖ ≤ B ^ (p.natDegree - i) * p.natDegree.choose i :=
  by
  obtain hB | hB := lt_or_le B 0
  · rw [eq_one_of_roots_le hB h1 h2 h3, Polynomial.map_one, nat_degree_one, zero_tsub, pow_zero,
      one_mul, coeff_one]
    split_ifs <;> norm_num [h]
  rw [← h1.nat_degree_map f]
  obtain hi | hi := lt_or_le (map f p).natDegree i
  · rw [coeff_eq_zero_of_nat_degree_lt hi, norm_zero]
    positivity
  rw [coeff_eq_esymm_roots_of_splits ((splits_id_iff_splits f).2 h2) hi, (h1.map _).leadingCoeff,
    one_mul, norm_mul, norm_pow, norm_neg, norm_one, one_pow, one_mul]
  apply ((norm_multiset_sum_le _).trans <| sum_le_card_nsmul _ _ fun r hr => _).trans
  ·
    rw [Multiset.map_map, card_map, card_powerset_len, ← nat_degree_eq_card_roots' h2,
      Nat.choose_symm hi, mul_comm, nsmul_eq_mul]
  simp_rw [Multiset.mem_map] at hr
  obtain ⟨_, ⟨s, hs, rfl⟩, rfl⟩ := hr
  rw [mem_powerset_len] at hs
  lift B to ℝ≥0 using hB
  rw [← coe_nnnorm, ← NNReal.coe_pow, NNReal.coe_le_coe, ← nnnormHom_apply, ← MonoidHom.coe_coe,
    MonoidHom.map_multiset_prod]
  refine' (prod_le_pow_card _ B fun x hx => _).trans_eq (by rw [card_map, hs.2])
  obtain ⟨z, hz, rfl⟩ := Multiset.mem_map.1 hx
  exact h3 z (mem_of_le hs.1 hz)
#align polynomial.coeff_le_of_roots_le Polynomial.coeff_le_of_roots_le

/- warning: polynomial.coeff_bdd_of_roots_le -> Polynomial.coeff_bdd_of_roots_le is a dubious translation:
lean 3 declaration is
  forall {F : Type.{u1}} {K : Type.{u2}} [_inst_3 : CommRing.{u1} F] [_inst_4 : NormedField.{u2} K] {B : Real} {d : Nat} (f : RingHom.{u1, u2} F K (NonAssocRing.toNonAssocSemiring.{u1} F (Ring.toNonAssocRing.{u1} F (CommRing.toRing.{u1} F _inst_3))) (NonAssocRing.toNonAssocSemiring.{u2} K (Ring.toNonAssocRing.{u2} K (NormedRing.toRing.{u2} K (NormedCommRing.toNormedRing.{u2} K (NormedField.toNormedCommRing.{u2} K _inst_4)))))) {p : Polynomial.{u1} F (Ring.toSemiring.{u1} F (CommRing.toRing.{u1} F _inst_3))}, (Polynomial.Monic.{u1} F (Ring.toSemiring.{u1} F (CommRing.toRing.{u1} F _inst_3)) p) -> (Polynomial.Splits.{u1, u2} F K _inst_3 (NormedField.toField.{u2} K _inst_4) f p) -> (LE.le.{0} Nat Nat.hasLe (Polynomial.natDegree.{u1} F (Ring.toSemiring.{u1} F (CommRing.toRing.{u1} F _inst_3)) p) d) -> (forall (z : K), (Membership.Mem.{u2, u2} K (Multiset.{u2} K) (Multiset.hasMem.{u2} K) z (Polynomial.roots.{u2} K (SeminormedCommRing.toCommRing.{u2} K (NormedCommRing.toSeminormedCommRing.{u2} K (NormedField.toNormedCommRing.{u2} K _inst_4))) (Field.isDomain.{u2} K (NormedField.toField.{u2} K _inst_4)) (Polynomial.map.{u1, u2} F K (Ring.toSemiring.{u1} F (CommRing.toRing.{u1} F _inst_3)) (Ring.toSemiring.{u2} K (NormedRing.toRing.{u2} K (NormedCommRing.toNormedRing.{u2} K (NormedField.toNormedCommRing.{u2} K _inst_4)))) f p))) -> (LE.le.{0} Real Real.hasLe (Norm.norm.{u2} K (NormedField.toHasNorm.{u2} K _inst_4) z) B)) -> (forall (i : Nat), LE.le.{0} Real Real.hasLe (Norm.norm.{u2} K (NormedField.toHasNorm.{u2} K _inst_4) (Polynomial.coeff.{u2} K (Ring.toSemiring.{u2} K (NormedRing.toRing.{u2} K (NormedCommRing.toNormedRing.{u2} K (NormedField.toNormedCommRing.{u2} K _inst_4)))) (Polynomial.map.{u1, u2} F K (Ring.toSemiring.{u1} F (CommRing.toRing.{u1} F _inst_3)) (Ring.toSemiring.{u2} K (NormedRing.toRing.{u2} K (NormedCommRing.toNormedRing.{u2} K (NormedField.toNormedCommRing.{u2} K _inst_4)))) f p) i)) (HMul.hMul.{0, 0, 0} Real Real Real (instHMul.{0} Real Real.hasMul) (HPow.hPow.{0, 0, 0} Real Nat Real (instHPow.{0, 0} Real Nat (Monoid.Pow.{0} Real Real.monoid)) (LinearOrder.max.{0} Real Real.linearOrder B (OfNat.ofNat.{0} Real 1 (OfNat.mk.{0} Real 1 (One.one.{0} Real Real.hasOne)))) d) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Real (HasLiftT.mk.{1, 1} Nat Real (CoeTCₓ.coe.{1, 1} Nat Real (Nat.castCoe.{0} Real Real.hasNatCast))) (Nat.choose d (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.hasDiv) d (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne)))))))))
but is expected to have type
  forall {F : Type.{u2}} {K : Type.{u1}} [_inst_3 : CommRing.{u2} F] [_inst_4 : NormedField.{u1} K] {B : Real} {d : Nat} (f : RingHom.{u2, u1} F K (NonAssocRing.toNonAssocSemiring.{u2} F (Ring.toNonAssocRing.{u2} F (CommRing.toRing.{u2} F _inst_3))) (NonAssocRing.toNonAssocSemiring.{u1} K (Ring.toNonAssocRing.{u1} K (NormedRing.toRing.{u1} K (NormedCommRing.toNormedRing.{u1} K (NormedField.toNormedCommRing.{u1} K _inst_4)))))) {p : Polynomial.{u2} F (Ring.toSemiring.{u2} F (CommRing.toRing.{u2} F _inst_3))}, (Polynomial.Monic.{u2} F (Ring.toSemiring.{u2} F (CommRing.toRing.{u2} F _inst_3)) p) -> (Polynomial.Splits.{u2, u1} F K _inst_3 (NormedField.toField.{u1} K _inst_4) f p) -> (LE.le.{0} Nat instLENat (Polynomial.natDegree.{u2} F (Ring.toSemiring.{u2} F (CommRing.toRing.{u2} F _inst_3)) p) d) -> (forall (z : K), (Membership.mem.{u1, u1} K (Multiset.{u1} K) (Multiset.instMembershipMultiset.{u1} K) z (Polynomial.roots.{u1} K (EuclideanDomain.toCommRing.{u1} K (Field.toEuclideanDomain.{u1} K (NormedField.toField.{u1} K _inst_4))) (EuclideanDomain.instIsDomainToSemiringToRingToCommRing.{u1} K (Field.toEuclideanDomain.{u1} K (NormedField.toField.{u1} K _inst_4))) (Polynomial.map.{u2, u1} F K (Ring.toSemiring.{u2} F (CommRing.toRing.{u2} F _inst_3)) (DivisionSemiring.toSemiring.{u1} K (Semifield.toDivisionSemiring.{u1} K (Field.toSemifield.{u1} K (NormedField.toField.{u1} K _inst_4)))) f p))) -> (LE.le.{0} Real Real.instLEReal (Norm.norm.{u1} K (NormedField.toNorm.{u1} K _inst_4) z) B)) -> (forall (i : Nat), LE.le.{0} Real Real.instLEReal (Norm.norm.{u1} K (NormedField.toNorm.{u1} K _inst_4) (Polynomial.coeff.{u1} K (DivisionSemiring.toSemiring.{u1} K (Semifield.toDivisionSemiring.{u1} K (Field.toSemifield.{u1} K (NormedField.toField.{u1} K _inst_4)))) (Polynomial.map.{u2, u1} F K (Ring.toSemiring.{u2} F (CommRing.toRing.{u2} F _inst_3)) (DivisionSemiring.toSemiring.{u1} K (Semifield.toDivisionSemiring.{u1} K (Field.toSemifield.{u1} K (NormedField.toField.{u1} K _inst_4)))) f p) i)) (HMul.hMul.{0, 0, 0} Real Real Real (instHMul.{0} Real Real.instMulReal) (HPow.hPow.{0, 0, 0} Real Nat Real (instHPow.{0, 0} Real Nat (Monoid.Pow.{0} Real Real.instMonoidReal)) (Max.max.{0} Real (LinearOrderedRing.toMax.{0} Real Real.instLinearOrderedRingReal) B (OfNat.ofNat.{0} Real 1 (One.toOfNat1.{0} Real Real.instOneReal))) d) (Nat.cast.{0} Real Real.natCast (Nat.choose d (HDiv.hDiv.{0, 0, 0} Nat Nat Nat (instHDiv.{0} Nat Nat.instDivNat) d (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2)))))))
Case conversion may be inaccurate. Consider using '#align polynomial.coeff_bdd_of_roots_le Polynomial.coeff_bdd_of_roots_leₓ'. -/
/-- The coefficients of the monic polynomials of bounded degree with bounded roots are
uniformely bounded. -/
theorem coeff_bdd_of_roots_le {B : ℝ} {d : ℕ} (f : F →+* K) {p : F[X]} (h1 : p.Monic)
    (h2 : Splits f p) (h3 : p.natDegree ≤ d) (h4 : ∀ z ∈ (map f p).roots, ‖z‖ ≤ B) (i : ℕ) :
    ‖(map f p).coeff i‖ ≤ max B 1 ^ d * d.choose (d / 2) :=
  by
  obtain hB | hB := le_or_lt 0 B
  · apply (coeff_le_of_roots_le i h1 h2 h4).trans
    calc
      _ ≤ max B 1 ^ (p.nat_degree - i) * p.nat_degree.choose i :=
        mul_le_mul_of_nonneg_right (pow_le_pow_of_le_left hB (le_max_left _ _) _) _
      _ ≤ max B 1 ^ d * p.nat_degree.choose i :=
        (mul_le_mul_of_nonneg_right ((pow_mono (le_max_right _ _)) (le_trans (Nat.sub_le _ _) h3))
          _)
      _ ≤ max B 1 ^ d * d.choose (d / 2) :=
        mul_le_mul_of_nonneg_left
          (nat.cast_le.mpr ((i.choose_mono h3).trans (i.choose_le_middle d))) _

    all_goals positivity
  · rw [eq_one_of_roots_le hB h1 h2 h4, Polynomial.map_one, coeff_one]
    refine' trans _ (one_le_mul_of_one_le_of_one_le (one_le_pow_of_one_le (le_max_right B 1) d) _)
    · split_ifs <;> norm_num
    · exact_mod_cast nat.succ_le_iff.mpr (Nat.choose_pos (d.div_le_self 2))
#align polynomial.coeff_bdd_of_roots_le Polynomial.coeff_bdd_of_roots_le

end Roots

end Polynomial


/-
Copyright (c) 2021 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov

! This file was ported from Lean 3 source module data.ulift
! leanprover-community/mathlib commit 448144f7ae193a8990cb7473c9e9a01990f64ac7
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Logic.Equiv.Basic

/-!
# Extra lemmas about `ulift` and `plift`

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

In this file we provide `subsingleton`, `unique`, `decidable_eq`, and `is_empty` instances for
`ulift α` and `plift α`. We also prove `ulift.forall`, `ulift.exists`, `plift.forall`, and
`plift.exists`.
-/

universe u v

open Function

namespace PLift

variable {α : Sort u} {β : Sort v}

instance [Subsingleton α] : Subsingleton (PLift α) :=
  Equiv.plift.Subsingleton

instance [Nonempty α] : Nonempty (PLift α) :=
  Equiv.plift.Nonempty

instance [Unique α] : Unique (PLift α) :=
  Equiv.plift.unique

instance [DecidableEq α] : DecidableEq (PLift α) :=
  Equiv.plift.DecidableEq

instance [IsEmpty α] : IsEmpty (PLift α) :=
  Equiv.plift.isEmpty

#print PLift.up_injective /-
theorem up_injective : Injective (@up α) :=
  Equiv.plift.symm.Injective
#align plift.up_injective PLift.up_injective
-/

#print PLift.up_surjective /-
theorem up_surjective : Surjective (@up α) :=
  Equiv.plift.symm.Surjective
#align plift.up_surjective PLift.up_surjective
-/

#print PLift.up_bijective /-
theorem up_bijective : Bijective (@up α) :=
  Equiv.plift.symm.Bijective
#align plift.up_bijective PLift.up_bijective
-/

#print PLift.up_inj /-
@[simp]
theorem up_inj {x y : α} : up x = up y ↔ x = y :=
  up_injective.eq_iff
#align plift.up_inj PLift.up_inj
-/

#print PLift.down_surjective /-
theorem down_surjective : Surjective (@down α) :=
  Equiv.plift.Surjective
#align plift.down_surjective PLift.down_surjective
-/

#print PLift.down_bijective /-
theorem down_bijective : Bijective (@down α) :=
  Equiv.plift.Bijective
#align plift.down_bijective PLift.down_bijective
-/

#print PLift.forall /-
@[simp]
theorem forall {p : PLift α → Prop} : (∀ x, p x) ↔ ∀ x : α, p (PLift.up x) :=
  up_surjective.forall
#align plift.forall PLift.forall
-/

#print PLift.exists /-
@[simp]
theorem exists {p : PLift α → Prop} : (∃ x, p x) ↔ ∃ x : α, p (PLift.up x) :=
  up_surjective.exists
#align plift.exists PLift.exists
-/

end PLift

namespace ULift

variable {α : Type u} {β : Type v}

instance [Subsingleton α] : Subsingleton (ULift α) :=
  Equiv.ulift.Subsingleton

instance [Nonempty α] : Nonempty (ULift α) :=
  Equiv.ulift.Nonempty

instance [Unique α] : Unique (ULift α) :=
  Equiv.ulift.unique

instance [DecidableEq α] : DecidableEq (ULift α) :=
  Equiv.ulift.DecidableEq

instance [IsEmpty α] : IsEmpty (ULift α) :=
  Equiv.ulift.isEmpty

/- warning: ulift.up_injective -> ULift.up_injective is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}}, Function.Injective.{succ u1, succ (max u1 u2)} α (ULift.{u2, u1} α) (ULift.up.{u2, u1} α)
but is expected to have type
  forall {α : Type.{u2}}, Function.Injective.{succ u2, max (succ u2) (succ u1)} α (ULift.{u1, u2} α) (ULift.up.{u1, u2} α)
Case conversion may be inaccurate. Consider using '#align ulift.up_injective ULift.up_injectiveₓ'. -/
theorem up_injective : Injective (@up α) :=
  Equiv.ulift.symm.Injective
#align ulift.up_injective ULift.up_injective

/- warning: ulift.up_surjective -> ULift.up_surjective is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}}, Function.Surjective.{succ u1, succ (max u1 u2)} α (ULift.{u2, u1} α) (ULift.up.{u2, u1} α)
but is expected to have type
  forall {α : Type.{u2}}, Function.Surjective.{succ u2, max (succ u2) (succ u1)} α (ULift.{u1, u2} α) (ULift.up.{u1, u2} α)
Case conversion may be inaccurate. Consider using '#align ulift.up_surjective ULift.up_surjectiveₓ'. -/
theorem up_surjective : Surjective (@up α) :=
  Equiv.ulift.symm.Surjective
#align ulift.up_surjective ULift.up_surjective

/- warning: ulift.up_bijective -> ULift.up_bijective is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}}, Function.Bijective.{succ u1, succ (max u1 u2)} α (ULift.{u2, u1} α) (ULift.up.{u2, u1} α)
but is expected to have type
  forall {α : Type.{u2}}, Function.Bijective.{succ u2, max (succ u2) (succ u1)} α (ULift.{u1, u2} α) (ULift.up.{u1, u2} α)
Case conversion may be inaccurate. Consider using '#align ulift.up_bijective ULift.up_bijectiveₓ'. -/
theorem up_bijective : Bijective (@up α) :=
  Equiv.ulift.symm.Bijective
#align ulift.up_bijective ULift.up_bijective

/- warning: ulift.up_inj -> ULift.up_inj is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {x : α} {y : α}, Iff (Eq.{succ (max u1 u2)} (ULift.{u2, u1} α) (ULift.up.{u2, u1} α x) (ULift.up.{u2, u1} α y)) (Eq.{succ u1} α x y)
but is expected to have type
  forall {α : Type.{u2}} {x : α} {y : α}, Iff (Eq.{max (succ u2) (succ u1)} (ULift.{u1, u2} α) (ULift.up.{u1, u2} α x) (ULift.up.{u1, u2} α y)) (Eq.{succ u2} α x y)
Case conversion may be inaccurate. Consider using '#align ulift.up_inj ULift.up_injₓ'. -/
@[simp]
theorem up_inj {x y : α} : up x = up y ↔ x = y :=
  up_injective.eq_iff
#align ulift.up_inj ULift.up_inj

/- warning: ulift.down_surjective -> ULift.down_surjective is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}}, Function.Surjective.{succ (max u1 u2), succ u1} (ULift.{u2, u1} α) α (ULift.down.{u2, u1} α)
but is expected to have type
  forall {α : Type.{u2}}, Function.Surjective.{max (succ u2) (succ u1), succ u2} (ULift.{u1, u2} α) α (ULift.down.{u1, u2} α)
Case conversion may be inaccurate. Consider using '#align ulift.down_surjective ULift.down_surjectiveₓ'. -/
theorem down_surjective : Surjective (@down α) :=
  Equiv.ulift.Surjective
#align ulift.down_surjective ULift.down_surjective

/- warning: ulift.down_bijective -> ULift.down_bijective is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}}, Function.Bijective.{succ (max u1 u2), succ u1} (ULift.{u2, u1} α) α (ULift.down.{u2, u1} α)
but is expected to have type
  forall {α : Type.{u2}}, Function.Bijective.{max (succ u2) (succ u1), succ u2} (ULift.{u1, u2} α) α (ULift.down.{u1, u2} α)
Case conversion may be inaccurate. Consider using '#align ulift.down_bijective ULift.down_bijectiveₓ'. -/
theorem down_bijective : Bijective (@down α) :=
  Equiv.ulift.Bijective
#align ulift.down_bijective ULift.down_bijective

/- warning: ulift.forall -> ULift.forall is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {p : (ULift.{u2, u1} α) -> Prop}, Iff (forall (x : ULift.{u2, u1} α), p x) (forall (x : α), p (ULift.up.{u2, u1} α x))
but is expected to have type
  forall {α : Type.{u2}} {p : (ULift.{u1, u2} α) -> Prop}, Iff (forall (x : ULift.{u1, u2} α), p x) (forall (x : α), p (ULift.up.{u1, u2} α x))
Case conversion may be inaccurate. Consider using '#align ulift.forall ULift.forallₓ'. -/
@[simp]
theorem forall {p : ULift α → Prop} : (∀ x, p x) ↔ ∀ x : α, p (ULift.up x) :=
  up_surjective.forall
#align ulift.forall ULift.forall

/- warning: ulift.exists -> ULift.exists is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {p : (ULift.{u2, u1} α) -> Prop}, Iff (Exists.{succ (max u1 u2)} (ULift.{u2, u1} α) (fun (x : ULift.{u2, u1} α) => p x)) (Exists.{succ u1} α (fun (x : α) => p (ULift.up.{u2, u1} α x)))
but is expected to have type
  forall {α : Type.{u2}} {p : (ULift.{u1, u2} α) -> Prop}, Iff (Exists.{max (succ u2) (succ u1)} (ULift.{u1, u2} α) (fun (x : ULift.{u1, u2} α) => p x)) (Exists.{succ u2} α (fun (x : α) => p (ULift.up.{u1, u2} α x)))
Case conversion may be inaccurate. Consider using '#align ulift.exists ULift.existsₓ'. -/
@[simp]
theorem exists {p : ULift α → Prop} : (∃ x, p x) ↔ ∃ x : α, p (ULift.up x) :=
  up_surjective.exists
#align ulift.exists ULift.exists

end ULift


/-
Copyright (c) 2017 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Yury G. Kudryashov

! This file was ported from Lean 3 source module data.sum.basic
! leanprover-community/mathlib commit bd9851ca476957ea4549eb19b40e7b5ade9428cc
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Logic.Function.Basic
import Mathlib.Mathport.Rename

/-!
# Disjoint union of types

This file proves basic results about the sum type `α ⊕ β`.

`α ⊕ β` is the type made of a copy of `α` and a copy of `β`. It is also called *disjoint union*.

## Main declarations

* `Sum.getLeft`: Retrieves the left content of `x : α ⊕ β` or returns `none` if it's coming from
  the right.
* `Sum.getRight`: Retrieves the right content of `x : α ⊕ β` or returns `none` if it's coming from
  the left.
* `Sum.isLeft`: Returns whether `x : α ⊕ β` comes from the left component or not.
* `Sum.isRight`: Returns whether `x : α ⊕ β` comes from the right component or not.
* `Sum.map`: Maps `α ⊕ β` to `γ ⊕ δ` component-wise.
* `Sum.elim`: Nondependent eliminator/induction principle for `α ⊕ β`.
* `Sum.swap`: Maps `α ⊕ β` to `β ⊕ α` by swapping components.
* `Sum.Lex`: Lexicographic order on `α ⊕ β` induced by a relation on `α` and a relation on `β`.

## Notes

The definition of `Sum` takes values in `Type _`. This effectively forbids `Prop`- valued sum types.
To this effect, we have `PSum`, which takes value in `Sort*` and carries a more complicated
universe signature in consequence. The `Prop` version is `or`.
-/

universe u v w x

variable {α : Type u} {α' : Type w} {β : Type v} {β' : Type x} {γ δ : Type _}

namespace Sum

deriving instance DecidableEq for Sum
deriving instance BEq for Sum

@[simp]
theorem «forall» {p : Sum α β → Prop} : (∀ x, p x) ↔ (∀ a, p (inl a)) ∧ ∀ b, p (inr b) :=
  ⟨fun h ↦ ⟨fun _ ↦ h _, fun _ ↦ h _⟩, fun ⟨h₁, h₂⟩ ↦ Sum.rec h₁ h₂⟩
#align sum.forall Sum.forall

@[simp]
theorem «exists» {p : Sum α β → Prop} : (∃ x, p x) ↔ (∃ a, p (inl a)) ∨ ∃ b, p (inr b) :=
  ⟨ fun
    | ⟨inl a, h⟩ => Or.inl ⟨a, h⟩
    | ⟨inr b, h⟩ => Or.inr ⟨b, h⟩,
    fun
    | Or.inl ⟨a, h⟩ => ⟨inl a, h⟩
    | Or.inr ⟨b, h⟩ => ⟨inr b, h⟩⟩
#align sum.exists Sum.exists

theorem inl_injective : Function.Injective (inl : α → Sum α β) := fun _ _ ↦ inl.inj
#align sum.inl_injective Sum.inl_injective

theorem inr_injective : Function.Injective (inr : β → Sum α β) := fun _ _ ↦ inr.inj
#align sum.inr_injective Sum.inr_injective

section get

/-- Check if a sum is `inl` and if so, retrieve its contents. -/
def getLeft : Sum α β → Option α
  | inl a => some a
  | inr _ => none
#align sum.get_left Sum.getLeft

/-- Check if a sum is `inr` and if so, retrieve its contents. -/
def getRight : Sum α β → Option β
  | inr b => some b
  | inl _ => none
#align sum.get_right Sum.getRight

/-- Check if a sum is `inl`. -/
def isLeft : Sum α β → Bool
  | inl _ => true
  | inr _ => false
#align sum.is_left Sum.isLeft

/-- Check if a sum is `inr`. -/
def isRight : Sum α β → Bool
  | inl _ => false
  | inr _ => true
#align sum.is_right Sum.isRight

variable {x y : Sum α β}

@[simp] theorem getLeft_inl (x : α) : (inl x : α ⊕ β).getLeft = some x := rfl
@[simp] theorem getLeft_inr (x : β) : (inr x : α ⊕ β).getLeft = none := rfl
@[simp] theorem getRight_inl (x : α) : (inl x : α ⊕ β).getRight = none := rfl
@[simp] theorem getRight_inr (x : β) : (inr x : α ⊕ β).getRight = some x := rfl

@[simp] theorem isLeft_inl (x : α) : (inl x : α ⊕ β).isLeft = true := rfl
@[simp] theorem isLeft_inr (x : β) : (inr x : α ⊕ β).isLeft = false := rfl
@[simp] theorem isRight_inl (x : α) : (inl x : α ⊕ β).isRight = false := rfl
@[simp] theorem isRight_inr (x : β) : (inr x : α ⊕ β).isRight = true := rfl

@[simp] theorem getLeft_eq_none_iff : x.getLeft = none ↔ x.isRight := by
  cases x <;> simp only [getLeft, isRight, eq_self_iff_true]

#align sum.get_left_eq_none_iff Sum.getLeft_eq_none_iff

@[simp] theorem getRight_eq_none_iff : x.getRight = none ↔ x.isLeft := by
  cases x <;> simp only [getRight, isLeft, eq_self_iff_true]

#align sum.get_right_eq_none_iff Sum.getRight_eq_none_iff

@[simp] lemma getLeft_eq_some_iff {a : α} : x.getLeft = a ↔ x = inl a := by
  cases x <;> simp only [getLeft, Option.some.injEq, inl.injEq]
#align sum.get_left_eq_some_iff Sum.getLeft_eq_some_iff

@[simp] lemma getRight_eq_some_iff {b : β} : x.getRight = b ↔ x = inr b := by
  cases x <;> simp only [getRight, Option.some.injEq, inr.injEq]
#align sum.get_right_eq_some_iff Sum.getRight_eq_some_iff

@[simp]
theorem not_isLeft (x : Sum α β) : not x.isLeft = x.isRight := by cases x <;> rfl
#align sum.bnot_is_left Sum.not_isLeft

@[simp]
theorem isLeft_eq_false : x.isLeft = false ↔ x.isRight := by cases x <;> simp
#align sum.is_left_eq_ff Sum.isLeft_eq_false

theorem Not_isLeft : ¬x.isLeft ↔ x.isRight := by simp
#align sum.not_is_left Sum.Not_isLeft

@[simp]
theorem not_isRight (x : Sum α β) : !x.isRight = x.isLeft := by cases x <;> rfl
#align sum.bnot_is_right Sum.not_isRight

@[simp]
theorem isRight_eq_false : x.isRight = false ↔ x.isLeft := by cases x <;> simp
#align sum.is_right_eq_ff Sum.isRight_eq_false

theorem Not_isRight : ¬x.isRight ↔ x.isLeft := by simp
#align sum.not_is_right Sum.Not_isRight

theorem isLeft_iff : x.isLeft ↔ ∃ y, x = Sum.inl y := by cases x <;> simp
#align sum.is_left_iff Sum.isLeft_iff

theorem isRight_iff : x.isRight ↔ ∃ y, x = Sum.inr y := by cases x <;> simp
#align sum.is_right_iff Sum.isRight_iff

end get

theorem inl.inj_iff {a b} : (inl a : Sum α β) = inl b ↔ a = b :=
  ⟨inl.inj, congr_arg _⟩
#align sum.inl.inj_iff Sum.inl.inj_iff

theorem inr.inj_iff {a b} : (inr a : Sum α β) = inr b ↔ a = b :=
  ⟨inr.inj, congr_arg _⟩
#align sum.inr.inj_iff Sum.inr.inj_iff

theorem inl_ne_inr {a : α} {b : β} : inl a ≠ inr b :=
  fun.
#align sum.inl_ne_inr Sum.inl_ne_inr

theorem inr_ne_inl {a : α} {b : β} : inr b ≠ inl a :=
  fun.
#align sum.inr_ne_inl Sum.inr_ne_inl

/-- Define a function on `α ⊕ β` by giving separate definitions on `α` and `β`. -/
protected def elim {α β γ : Sort _} (f : α → γ) (g : β → γ) : Sum α β → γ :=
  fun x ↦ Sum.casesOn x f g
#align sum.elim Sum.elim

@[simp]
theorem elim_inl {α β γ : Sort _} (f : α → γ) (g : β → γ) (x : α) : Sum.elim f g (inl x) = f x :=
  rfl
#align sum.elim_inl Sum.elim_inl

@[simp]
theorem elim_inr {α β γ : Sort _} (f : α → γ) (g : β → γ) (x : β) : Sum.elim f g (inr x) = g x :=
  rfl
#align sum.elim_inr Sum.elim_inr

@[simp]
theorem elim_comp_inl {α β γ : Sort _} (f : α → γ) (g : β → γ) : Sum.elim f g ∘ inl = f :=
  rfl
#align sum.elim_comp_inl Sum.elim_comp_inl

@[simp]
theorem elim_comp_inr {α β γ : Sort _} (f : α → γ) (g : β → γ) : Sum.elim f g ∘ inr = g :=
  rfl
#align sum.elim_comp_inr Sum.elim_comp_inr

@[simp]
theorem elim_inl_inr {α β : Sort _} : @Sum.elim α β _ inl inr = id :=
  funext fun x ↦ Sum.casesOn x (fun _ ↦ rfl) fun _ ↦ rfl
#align sum.elim_inl_inr Sum.elim_inl_inr

theorem comp_elim {α β γ δ : Sort _} (f : γ → δ) (g : α → γ) (h : β → γ) :
    f ∘ Sum.elim g h = Sum.elim (f ∘ g) (f ∘ h) :=
  funext fun x ↦ Sum.casesOn x (fun _ ↦ rfl) fun _ ↦ rfl
#align sum.comp_elim Sum.comp_elim

@[simp]
theorem elim_comp_inl_inr {α β γ : Sort _} (f : Sum α β → γ) : Sum.elim (f ∘ inl) (f ∘ inr) = f :=
  funext fun x ↦ Sum.casesOn x (fun _ ↦ rfl) fun _ ↦ rfl
#align sum.elim_comp_inl_inr Sum.elim_comp_inl_inr

/-- Map `α ⊕ β` to `α' ⊕ β'` sending `α` to `α'` and `β` to `β'`. -/
protected def map (f : α → α') (g : β → β') : Sum α β → Sum α' β' :=
  Sum.elim (inl ∘ f) (inr ∘ g)
#align sum.map Sum.map

@[simp]
theorem map_inl (f : α → α') (g : β → β') (x : α) : (inl x).map f g = inl (f x) :=
  rfl
#align sum.map_inl Sum.map_inl

@[simp]
theorem map_inr (f : α → α') (g : β → β') (x : β) : (inr x).map f g = inr (g x) :=
  rfl
#align sum.map_inr Sum.map_inr

@[simp]
theorem map_map {α'' β''} (f' : α' → α'') (g' : β' → β'') (f : α → α') (g : β → β') :
    ∀ x : Sum α β, (x.map f g).map f' g' = x.map (f' ∘ f) (g' ∘ g)
  | inl _ => rfl
  | inr _ => rfl
#align sum.map_map Sum.map_map

@[simp]
theorem map_comp_map {α'' β''} (f' : α' → α'') (g' : β' → β'') (f : α → α') (g : β → β') :
    Sum.map f' g' ∘ Sum.map f g = Sum.map (f' ∘ f) (g' ∘ g) :=
  funext <| map_map f' g' f g
#align sum.map_comp_map Sum.map_comp_map

@[simp]
theorem map_id_id (α β) : Sum.map (@id α) (@id β) = id :=
  funext fun x ↦ Sum.recOn x (fun _ ↦ rfl) fun _ ↦ rfl
#align sum.map_id_id Sum.map_id_id

theorem elim_map {α β γ δ ε : Sort _} {f₁ : α → β} {f₂ : β → ε} {g₁ : γ → δ} {g₂ : δ → ε} {x} :
    Sum.elim f₂ g₂ (Sum.map f₁ g₁ x) = Sum.elim (f₂ ∘ f₁) (g₂ ∘ g₁) x := by
  cases x <;> rfl
#align sum.elim_map Sum.elim_map

theorem elim_comp_map {α β γ δ ε : Sort _} {f₁ : α → β} {f₂ : β → ε} {g₁ : γ → δ} {g₂ : δ → ε} :
    Sum.elim f₂ g₂ ∘ Sum.map f₁ g₁ = Sum.elim (f₂ ∘ f₁) (g₂ ∘ g₁) :=
  funext $ fun _ => elim_map
#align sum.elim_comp_map Sum.elim_comp_map

@[simp]
theorem isLeft_map (f : α → β) (g : γ → δ) (x : Sum α γ) : isLeft (x.map f g) = isLeft x :=
by cases x <;> rfl
#align sum.is_left_map Sum.isLeft_map

@[simp]
theorem isRight_map (f : α → β) (g : γ → δ) (x : Sum α γ) : isRight (x.map f g) = isRight x :=
by cases x <;> rfl
#align sum.is_right_map Sum.isRight_map

@[simp]
theorem getLeft_map (f : α → β) (g : γ → δ) (x : Sum α γ) : (x.map f g).getLeft = x.getLeft.map f :=
by cases x <;> rfl
#align sum.get_left_map Sum.getLeft_map

@[simp]
theorem getRight_map (f : α → β) (g : γ → δ) (x : α ⊕ γ) :
  (x.map f g).getRight = x.getRight.map g := by cases x <;> rfl
#align sum.get_right_map Sum.getRight_map

open Function (update update_eq_iff update_comp_eq_of_injective update_comp_eq_of_forall_ne)

@[simp]
theorem update_elim_inl [DecidableEq α] [DecidableEq (Sum α β)] {f : α → γ} {g : β → γ} {i : α}
    {x : γ} : update (Sum.elim f g) (inl i) x = Sum.elim (update f i x) g :=
  update_eq_iff.2 ⟨by simp, by simp (config := { contextual := true })⟩
#align sum.update_elim_inl Sum.update_elim_inl

@[simp]
theorem update_elim_inr [DecidableEq β] [DecidableEq (Sum α β)] {f : α → γ} {g : β → γ} {i : β}
    {x : γ} : update (Sum.elim f g) (inr i) x = Sum.elim f (update g i x) :=
  update_eq_iff.2 ⟨by simp, by simp (config := { contextual := true })⟩
#align sum.update_elim_inr Sum.update_elim_inr

@[simp]
theorem update_inl_comp_inl [DecidableEq α] [DecidableEq (Sum α β)] {f : Sum α β → γ} {i : α}
    {x : γ} : update f (inl i) x ∘ inl = update (f ∘ inl) i x :=
  update_comp_eq_of_injective _ inl_injective _ _
#align sum.update_inl_comp_inl Sum.update_inl_comp_inl

@[simp]
theorem update_inl_apply_inl [DecidableEq α] [DecidableEq (Sum α β)] {f : Sum α β → γ} {i j : α}
    {x : γ} : update f (inl i) x (inl j) = update (f ∘ inl) i x j := by
  rw [← update_inl_comp_inl, Function.comp_apply]
#align sum.update_inl_apply_inl Sum.update_inl_apply_inl

@[simp]
theorem update_inl_comp_inr [DecidableEq (Sum α β)] {f : Sum α β → γ} {i : α} {x : γ} :
    update f (inl i) x ∘ inr = f ∘ inr :=
  (update_comp_eq_of_forall_ne _ _) fun _ ↦ inr_ne_inl
#align sum.update_inl_comp_inr Sum.update_inl_comp_inr

theorem update_inl_apply_inr [DecidableEq (Sum α β)] {f : Sum α β → γ} {i : α} {j : β} {x : γ} :
    update f (inl i) x (inr j) = f (inr j) :=
  Function.update_noteq inr_ne_inl _ _
#align sum.update_inl_apply_inr Sum.update_inl_apply_inr

@[simp]
theorem update_inr_comp_inl [DecidableEq (Sum α β)] {f : Sum α β → γ} {i : β} {x : γ} :
    update f (inr i) x ∘ inl = f ∘ inl :=
  (update_comp_eq_of_forall_ne _ _) fun _ ↦ inl_ne_inr
#align sum.update_inr_comp_inl Sum.update_inr_comp_inl

theorem update_inr_apply_inl [DecidableEq (Sum α β)] {f : Sum α β → γ} {i : α} {j : β} {x : γ} :
    update f (inr j) x (inl i) = f (inl i) :=
  Function.update_noteq inl_ne_inr _ _
#align sum.update_inr_apply_inl Sum.update_inr_apply_inl

@[simp]
theorem update_inr_comp_inr [DecidableEq β] [DecidableEq (Sum α β)] {f : Sum α β → γ} {i : β}
    {x : γ} : update f (inr i) x ∘ inr = update (f ∘ inr) i x :=
  update_comp_eq_of_injective _ inr_injective _ _
#align sum.update_inr_comp_inr Sum.update_inr_comp_inr

@[simp]
theorem update_inr_apply_inr [DecidableEq β] [DecidableEq (Sum α β)] {f : Sum α β → γ} {i j : β}
    {x : γ} : update f (inr i) x (inr j) = update (f ∘ inr) i x j := by
  rw [← update_inr_comp_inr, Function.comp_apply]
#align sum.update_inr_apply_inr Sum.update_inr_apply_inr

/-- Swap the factors of a sum type -/
def swap : Sum α β → Sum β α :=
  Sum.elim inr inl
#align sum.swap Sum.swap

@[simp]
theorem swap_inl (x : α) : swap (inl x : Sum α β) = inr x :=
  rfl
#align sum.swap_inl Sum.swap_inl

@[simp]

@[simp]
theorem swap_swap (x : Sum α β) : swap (swap x) = x := by cases x <;> rfl
#align sum.swap_swap Sum.swap_swap

@[simp]
theorem swap_swap_eq : swap ∘ swap = @id (Sum α β) :=
  funext <| swap_swap
#align sum.swap_swap_eq Sum.swap_swap_eq

@[simp]
theorem swap_leftInverse : Function.LeftInverse (@swap α β) swap :=
  swap_swap
#align sum.swap_left_inverse Sum.swap_leftInverse

@[simp]
theorem swap_rightInverse : Function.RightInverse (@swap α β) swap :=
  swap_swap
#align sum.swap_right_inverse Sum.swap_rightInverse

@[simp]
theorem isLeft_swap (x : Sum α β) : x.swap.isLeft = x.isRight := by cases x <;> rfl
#align sum.is_left_swap Sum.isLeft_swap

@[simp]
theorem isRight_swap (x : Sum α β) : x.swap.isRight = x.isLeft := by cases x <;> rfl
#align sum.is_right_swap Sum.isRight_swap

@[simp]
theorem getLeft_swap (x : Sum α β) : x.swap.getLeft = x.getRight := by cases x <;> rfl
#align sum.get_left_swap Sum.getLeft_swap

@[simp]
theorem getRight_swap (x : Sum α β) : x.swap.getRight = x.getLeft := by cases x <;> rfl
#align sum.get_right_swap Sum.getRight_swap

section LiftRel

/-- Lifts pointwise two relations between `α` and `γ` and between `β` and `δ` to a relation between
`α ⊕ β` and `γ ⊕ δ`. -/
inductive LiftRel (r : α → γ → Prop) (s : β → δ → Prop) : Sum α β → Sum γ δ → Prop
| protected inl {a c} : r a c → LiftRel r s (inl a) (inl c)
| protected inr {b d} : s b d → LiftRel r s (inr b) (inr d)
#align sum.lift_rel Sum.LiftRel

variable {r r₁ r₂ : α → γ → Prop} {s s₁ s₂ : β → δ → Prop} {a : α} {b : β} {c : γ} {d : δ}
  {x : Sum α β} {y : Sum γ δ}

@[simp]
theorem liftRel_inl_inl : LiftRel r s (inl a) (inl c) ↔ r a c :=
  ⟨fun h ↦ by
    cases h
    assumption, LiftRel.inl⟩

#align sum.lift_rel_inl_inl Sum.liftRel_inl_inl

@[simp]
theorem not_liftRel_inl_inr : ¬LiftRel r s (inl a) (inr d) :=
  fun.

#align sum.not_lift_rel_inl_inr Sum.not_liftRel_inl_inr

@[simp]
theorem not_liftRel_inr_inl : ¬LiftRel r s (inr b) (inl c) :=
  fun.

#align sum.not_lift_rel_inr_inl Sum.not_liftRel_inr_inl

@[simp]
theorem liftRel_inr_inr : LiftRel r s (inr b) (inr d) ↔ s b d :=
  ⟨fun h ↦ by
    cases h
    assumption, LiftRel.inr⟩

#align sum.lift_rel_inr_inr Sum.liftRel_inr_inr

instance [∀ a c, Decidable (r a c)] [∀ b d, Decidable (s b d)] :
    ∀ (ab : Sum α β) (cd : Sum γ δ), Decidable (LiftRel r s ab cd)
  | inl _, inl _ => decidable_of_iff' _ liftRel_inl_inl
  | inl _, inr _ => Decidable.isFalse not_liftRel_inl_inr
  | inr _, inl _ => Decidable.isFalse not_liftRel_inr_inl
  | inr _, inr _ => decidable_of_iff' _ liftRel_inr_inr

theorem LiftRel.mono (hr : ∀ a b, r₁ a b → r₂ a b) (hs : ∀ a b, s₁ a b → s₂ a b)
  (h : LiftRel r₁ s₁ x y) : LiftRel r₂ s₂ x y := by
  cases h
  · exact LiftRel.inl (hr _ _ ‹_›)
  · exact LiftRel.inr (hs _ _ ‹_›)
#align sum.lift_rel.mono Sum.LiftRel.mono

theorem LiftRel.mono_left (hr : ∀ a b, r₁ a b → r₂ a b) (h : LiftRel r₁ s x y) :
    LiftRel r₂ s x y :=
  (h.mono hr) fun _ _ ↦ id
#align sum.lift_rel.mono_left Sum.LiftRel.mono_left

theorem LiftRel.mono_right (hs : ∀ a b, s₁ a b → s₂ a b) (h : LiftRel r s₁ x y) :
    LiftRel r s₂ x y :=
  h.mono (fun _ _ ↦ id) hs
#align sum.lift_rel.mono_right Sum.LiftRel.mono_right

protected theorem LiftRel.swap (h : LiftRel r s x y) : LiftRel s r x.swap y.swap := by
  cases h
  · exact LiftRel.inr ‹_›
  · exact LiftRel.inl ‹_›
#align sum.lift_rel.swap Sum.LiftRel.swap

@[simp]
theorem liftRel_swap_iff : LiftRel s r x.swap y.swap ↔ LiftRel r s x y :=
  ⟨fun h ↦ by
    rw [← swap_swap x, ← swap_swap y]
    exact h.swap, LiftRel.swap⟩

#align sum.lift_rel_swap_iff Sum.liftRel_swap_iff

end LiftRel

section Lex

/-- Lexicographic order for sum. Sort all the `inl a` before the `inr b`, otherwise use the
respective order on `α` or `β`. -/
inductive Lex (r : α → α → Prop) (s : β → β → Prop) : Sum α β → Sum α β → Prop
  | protected inl {a₁ a₂} (h : r a₁ a₂) : Lex r s (inl a₁) (inl a₂)
  | protected inr {b₁ b₂} (h : s b₁ b₂) : Lex r s (inr b₁) (inr b₂)
  | sep (a b) : Lex r s (inl a) (inr b)
#align sum.lex.inl Sum.Lex.inl
#align sum.lex.inr Sum.Lex.inr
#align sum.lex.sep Sum.Lex.sep
#align sum.lex Sum.Lex

attribute [simp] Lex.sep

variable {r r₁ r₂ : α → α → Prop} {s s₁ s₂ : β → β → Prop} {a a₁ a₂ : α} {b b₁ b₂ : β}
  {x y : Sum α β}

@[simp]
theorem lex_inl_inl : Lex r s (inl a₁) (inl a₂) ↔ r a₁ a₂ :=
  ⟨fun h ↦ by
    cases h
    assumption, Lex.inl⟩
#align sum.lex_inl_inl Sum.lex_inl_inl

@[simp]
theorem lex_inr_inr : Lex r s (inr b₁) (inr b₂) ↔ s b₁ b₂ :=
  ⟨fun h ↦ by
    cases h
    assumption, Lex.inr⟩
#align sum.lex_inr_inr Sum.lex_inr_inr

@[simp]
theorem lex_inr_inl : ¬Lex r s (inr b) (inl a) :=
  fun.
#align sum.lex_inr_inl Sum.lex_inr_inl

instance [DecidableRel r] [DecidableRel s] : DecidableRel (Lex r s)
  | inl _, inl _ => decidable_of_iff' _ lex_inl_inl
  | inl _, inr _ => Decidable.isTrue (Lex.sep _ _)
  | inr _, inl _ => Decidable.isFalse lex_inr_inl
  | inr _, inr _ => decidable_of_iff' _ lex_inr_inr

protected theorem LiftRel.lex {a b : Sum α β} (h : LiftRel r s a b) : Lex r s a b := by
  cases h
  · exact Lex.inl ‹_›
  · exact Lex.inr ‹_›
#align sum.lift_rel.lex Sum.LiftRel.lex

theorem liftRel_subrelation_lex : Subrelation (LiftRel r s) (Lex r s) := LiftRel.lex

#align sum.lift_rel_subrelation_lex Sum.liftRel_subrelation_lex

theorem Lex.mono (hr : ∀ a b, r₁ a b → r₂ a b) (hs : ∀ a b, s₁ a b → s₂ a b) (h : Lex r₁ s₁ x y) :
    Lex r₂ s₂ x y := by
  cases h
  · exact Lex.inl (hr _ _ ‹_›)
  · exact Lex.inr (hs _ _ ‹_›)
  · exact Lex.sep _ _
#align sum.lex.mono Sum.Lex.mono

theorem Lex.mono_left (hr : ∀ a b, r₁ a b → r₂ a b) (h : Lex r₁ s x y) : Lex r₂ s x y :=
  (h.mono hr) fun _ _ ↦ id
#align sum.lex.mono_left Sum.Lex.mono_left

theorem Lex.mono_right (hs : ∀ a b, s₁ a b → s₂ a b) (h : Lex r s₁ x y) : Lex r s₂ x y :=
  h.mono (fun _ _ ↦ id) hs
#align sum.lex.mono_right Sum.Lex.mono_right

theorem lex_acc_inl {a} (aca : Acc r a) : Acc (Lex r s) (inl a) := by
  induction' aca with a _ IH
  constructor
  intro y h
  cases' h with a' _ h'
  exact IH _ h'
#align sum.lex_acc_inl Sum.lex_acc_inl

theorem lex_acc_inr (aca : ∀ a, Acc (Lex r s) (inl a)) {b} (acb : Acc s b) :
    Acc (Lex r s) (inr b) := by
  induction' acb with b _ IH
  constructor
  intro y h
  cases' h with _ _ _ b' _ h' a
  · exact IH _ h'
  · exact aca _
#align sum.lex_acc_inr Sum.lex_acc_inr

theorem lex_wf (ha : WellFounded r) (hb : WellFounded s) : WellFounded (Lex r s) :=
  have aca : ∀ a, Acc (Lex r s) (inl a) := fun a ↦ lex_acc_inl (ha.apply a)
  ⟨fun x ↦ Sum.recOn x aca fun b ↦ lex_acc_inr aca (hb.apply b)⟩
#align sum.lex_wf Sum.lex_wf

end Lex

end Sum

open Sum

namespace Function

theorem Injective.sum_elim {f : α → γ} {g : β → γ} (hf : Injective f) (hg : Injective g)
    (hfg : ∀ a b, f a ≠ g b) : Injective (Sum.elim f g)
  | inl _, inl _, h => congr_arg inl <| hf h
  | inl _, inr _, h => (hfg _ _ h).elim
  | inr _, inl _, h => (hfg _ _ h.symm).elim
  | inr _, inr _, h => congr_arg inr <| hg h
#align function.injective.sum_elim Function.Injective.sum_elim

theorem Injective.sum_map {f : α → β} {g : α' → β'} (hf : Injective f) (hg : Injective g) :
    Injective (Sum.map f g)
  | inl _, inl _, h => congr_arg inl <| hf <| inl.inj h
  | inr _, inr _, h => congr_arg inr <| hg <| inr.inj h
#align function.injective.sum_map Function.Injective.sum_map

theorem Surjective.sum_map {f : α → β} {g : α' → β'} (hf : Surjective f) (hg : Surjective g) :
    Surjective (Sum.map f g)
  | inl y =>
    let ⟨x, hx⟩ := hf y
    ⟨inl x, congr_arg inl hx⟩
  | inr y =>
    let ⟨x, hx⟩ := hg y
    ⟨inr x, congr_arg inr hx⟩
#align function.surjective.sum_map Function.Surjective.sum_map

theorem Bijective.sum_map {f : α → β} {g : α' → β'} (hf : Bijective f) (hg : Bijective g) :
    Bijective (Sum.map f g) :=
  ⟨hf.injective.sum_map hg.injective, hf.surjective.sum_map hg.surjective⟩
#align function.bijective.sum_map Function.Bijective.sum_map

end Function

namespace Sum

open Function

@[simp]
theorem map_injective {f : α → γ} {g : β → δ} :
    Injective (Sum.map f g) ↔ Injective f ∧ Injective g :=
  ⟨fun h =>
    ⟨fun a₁ a₂ ha => inl_injective <| @h (inl a₁) (inl a₂) (congr_arg inl ha : _), fun b₁ b₂ hb =>
      inr_injective <| @h (inr b₁) (inr b₂) (congr_arg inr hb : _)⟩,
    fun h => h.1.sum_map h.2⟩
#align sum.map_injective Sum.map_injective

@[simp]
theorem map_surjective {f : α → γ} {g : β → δ} :
    Surjective (Sum.map f g) ↔ Surjective f ∧ Surjective g :=
  ⟨ fun h => ⟨
      (fun c => by
        obtain ⟨a | b, h⟩ := h (inl c)
        · exact ⟨a, inl_injective h⟩
        · cases h),
      (fun d => by
        obtain ⟨a | b, h⟩ := h (inr d)
        · cases h
        · exact ⟨b, inr_injective h⟩)⟩,
    fun h => h.1.sum_map h.2⟩
#align sum.map_surjective Sum.map_surjective

@[simp]
theorem map_bijective {f : α → γ} {g : β → δ} :
    Bijective (Sum.map f g) ↔ Bijective f ∧ Bijective g :=
  (map_injective.and map_surjective).trans <| and_and_and_comm
#align sum.map_bijective Sum.map_bijective

theorem elim_const_const (c : γ) :
    Sum.elim (const _ c : α → γ) (const _ c : β → γ) = const _ c := by
  ext x
  cases x <;> rfl
#align sum.elim_const_const Sum.elim_const_const

@[simp]
theorem elim_lam_const_lam_const (c : γ) :
    (Sum.elim (fun _ : α ↦ c) fun _ : β ↦ c) = fun _ ↦ c :=
  Sum.elim_const_const c
#align sum.elim_lam_const_lam_const Sum.elim_lam_const_lam_const

theorem elim_update_left [DecidableEq α] [DecidableEq β] (f : α → γ) (g : β → γ) (i : α) (c : γ) :
    Sum.elim (Function.update f i c) g = Function.update (Sum.elim f g) (inl i) c := by
  ext x
  rcases x with x | x
  · by_cases h : x = i
    · subst h
      simp
    · simp [h]
  · simp
#align sum.elim_update_left Sum.elim_update_left

theorem elim_update_right [DecidableEq α] [DecidableEq β] (f : α → γ) (g : β → γ) (i : β) (c : γ) :
    Sum.elim f (Function.update g i c) = Function.update (Sum.elim f g) (inr i) c := by
  ext x
  rcases x with x | x
  · simp
  · by_cases h : x = i
    · subst h
      simp
    · simp [h]
#align sum.elim_update_right Sum.elim_update_right

end Sum

/-!
### Ternary sum

Abbreviations for the maps from the summands to `α ⊕ β ⊕ γ`. This is useful for pattern-matching.
-/

namespace Sum3

/-- The map from the first summand into a ternary sum. -/
@[match_pattern, simp, reducible]
def in₀ (a : α) : Sum α (Sum β γ) :=
  inl a
#align sum3.in₀ Sum3.in₀

/-- The map from the second summand into a ternary sum. -/
@[match_pattern, simp, reducible]
def in₁ (b : β) : Sum α (Sum β γ) :=
  inr <| inl b
#align sum3.in₁ Sum3.in₁

/-- The map from the third summand into a ternary sum. -/
@[match_pattern, simp, reducible]
def in₂ (c : γ) : Sum α (Sum β γ) :=
  inr <| inr c
#align sum3.in₂ Sum3.in₂

end Sum3


/-
Copyright (c) 2022 Jun Yoshida. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
-/

universe u v w

variable {Cont : Type u} {idx : Type v} {elem : Type w} {dom : Cont → idx → Prop} [GetElem Cont idx elem dom]

theorem getElem_subst {motive : elem → Prop} : ∀ {x y : Cont} {i j : idx} {hxi : dom x i} {hyj : dom y j}, x = y → i = j → motive x[i] → motive y[j]
| _, _, _, _, _, _, rfl, rfl => id

theorem getElem_eq  {x y : Cont} {i j : idx} (hxy : x = y) (hij : i = j) {hxi : dom x i} (hyj : dom y j := hxy ▸ hij ▸ hxi) : x[i] = y[j] :=
  getElem_subst hxy hij rfl