import GMLInit.Class.DecLift
import GMLInit.Data.Array
import GMLInit.Data.Fin

instance {α} (β : α → Type _) [DecidableEq α] [(x : α) → DecidableEq (β x)] : DecidableEq ((x : α) × β x)
| a₁, a₂ =>
  match a₁, a₂, inferInstanceAs (Decidable (a₁.fst = a₂.fst)) with
  | ⟨_,y₁⟩, ⟨_,y₂⟩, isTrue rfl =>
    if h : y₁ = y₂ then
      isTrue <| Sigma.eq rfl (heq_of_eq h)
    else
      isFalse fun | rfl => h rfl
  | ⟨_,_⟩, ⟨_,_⟩, isFalse hne => isFalse fun | rfl => hne rfl

class Finite (α : Type _) extends Array α where
  find : α → Fin toArray.size
  find_eq_iff_get_eq (x : α) (i : Fin toArray.size) : find x = i ↔ toArray.get i = x
  decEq : DecidableEq α := by infer_instance

attribute [instance] Finite.decEq

namespace Finite
variable (α) [inst : Finite α]

protected abbrev size := inst.toArray.size

protected abbrev get := inst.toArray.get

theorem get_find (x : α) : Finite.get α (Finite.find x) = x := by
  rw [←find_eq_iff_get_eq]

theorem find_get (i : Fin (Finite.size α)) : Finite.find (Finite.get α i) = i := by
  rw [find_eq_iff_get_eq]

protected def toEquiv : Equiv α (Fin (Finite.size α)) where
  fwd := Finite.find
  rev := Finite.get α
  spec {x i} := Finite.find_eq_iff_get_eq x i

protected def ofEquiv {α n} [DecidableEq α] (e : Equiv α (Fin n)) : Finite α where
  toArray := Array.ofFun e.rev
  find x := (Array.ofFun_size e.rev).symm ▸ e.fwd x
  find_eq_iff_get_eq x i := by
    constr
    · intro h
      rw [Array.ofFun_get]
      rw [←e.spec]
      rw [←h]
      elim_casts
    · intro h
      rw [Array.ofFun_get] at h
      rw [←e.spec] at h
      clean
      rw [h]
      elim_casts

protected abbrev all {α} [Finite α] (p : α → Bool) : Bool :=
  Fin.all fun i => p (Finite.get α i)

theorem forall_eq_true_of_all_eq_true {α} [Finite α] {p : α → Bool} : Finite.all p = true → ∀ x, p x = true := by
  intro h x
  have hall := Fin.forall_eq_true_of_all_eq_true h
  rw [←hall (Finite.find x)]
  rw [Finite.get_find]

theorem exists_eq_false_of_all_eq_false {α} [Finite α] {p : α → Bool} : Finite.all p = false → ∃ x, p x = false := by
  intro h
  match Fin.exists_eq_false_of_all_eq_false h with
  | ⟨i, h⟩ => exists (Finite.get α i)

instance (p : α → Prop) [DecidablePred p] : Decidable (∀ x, p x) :=
  match hall : Finite.all fun x => decide (p x) with
  | false => isFalse $ by
    intro h
    match exists_eq_false_of_all_eq_false hall with
    | ⟨x, hx⟩ => absurd h x; exact of_decide_eq_false hx
  | true => isTrue $ by
    intro x
    apply of_decide_eq_true
    exact forall_eq_true_of_all_eq_true hall ..

theorem decide_forall (p : α → Prop) [DecidablePred p] : decide (∀ x, p x) = Finite.all fun x => decide (p x) := by
  match h : Finite.all fun x => decide (p x) with
  | false =>
    apply decide_eq_false
    match exists_eq_false_of_all_eq_false h with
    | ⟨x, hx⟩ =>
      intro h
      rw [decide_eq_true (h x)] at hx
      contradiction
  | true =>
    apply decide_eq_true
    intro x
    apply of_decide_eq_true
    apply forall_eq_true_of_all_eq_true h

instance {α} (p : α → Bool) [Finite α] [(x : α) → DecLift (p x)] : DecLift (Finite.all p) where
  toProp := ∀ x, DecLift.toProp (p x)
  instDecidable := inferInstance
  decide_eq := by rw [decide_forall]; congr; funext x; rw [DecLift.decide_eq]

protected abbrev any {α} [Finite α] (p : α → Bool) : Bool :=
  Fin.any fun i => p (Finite.get α i)

theorem exists_eq_true_of_any_eq_true {α} [Finite α] {p : α → Bool} : Finite.any p = true → ∃ x, p x = true := by
  intro h
  match Fin.exists_eq_true_of_any_eq_true h with
  | ⟨i, h⟩ => exists (Finite.get α i)

theorem forall_eq_false_of_any_eq_false {α} [Finite α] {p : α → Bool} : Finite.any p = false → ∀ x, p x = false := by
  intro h x
  have hany := Fin.forall_eq_false_of_any_eq_false h
  rw [←hany (Finite.find x)]
  rw [Finite.get_find]

instance (p : α → Prop) [DecidablePred p] : Decidable (∃ x, p x) :=
  match hany : Finite.any fun x => decide (p x) with
  | true => isTrue $ by
    match exists_eq_true_of_any_eq_true hany with
    | ⟨x, hx⟩ => exists x; exact of_decide_eq_true hx
  | false => isFalse $ by
    intro ⟨x, hx⟩
    absurd hx
    apply of_decide_eq_false
    exact forall_eq_false_of_any_eq_false hany ..

theorem decide_exists (p : α → Prop) [DecidablePred p] : decide (∃ x, p x) = Finite.any fun x => decide (p x) := by
  match h : Finite.any fun x => decide (p x) with
  | true =>
    apply decide_eq_true
    match exists_eq_true_of_any_eq_true h with
    | ⟨x, hx⟩ =>
      exists x
      exact of_decide_eq_true hx
  | false =>
    apply decide_eq_false
    intro ⟨x, hx⟩
    absurd forall_eq_false_of_any_eq_false h x
    rw [decide_eq_true hx]
    trivial

instance {α} (p : α → Bool) [Finite α] [(x : α) → DecLift (p x)] : DecLift (Finite.any p) where
  toProp := ∃ x, DecLift.toProp (p x)
  instDecidable := inferInstance
  decide_eq := by rw [decide_exists]; congr; funext x; rw [DecLift.decide_eq]

instance : Finite Empty := Finite.ofEquiv Fin.equivEmpty.inv

instance : Finite Unit := Finite.ofEquiv Fin.equivUnit.inv

instance : Finite Bool := Finite.ofEquiv Fin.equivBool.inv

instance : Finite Ordering := Finite.ofEquiv Fin.equivOrdering.inv

instance (n) : Finite (Fin n) := Finite.ofEquiv Equiv.id

instance (α) [Finite α] : Finite (Option α) :=
  let e₁ := Fin.equivOption (Finite.size α)
  let e₂ := Option.equiv (Finite.toEquiv α)
  Finite.ofEquiv <| Equiv.comp e₁.inv e₂

instance (α β) [DecidableEq β] [Finite α] [Finite β] : Finite (α ⊕ β) :=
  let e₁ := Fin.equivSum (Finite.size α) (Finite.size β)
  let e₂ := Sum.equiv (Finite.toEquiv α) (Finite.toEquiv β)
  Finite.ofEquiv <| Equiv.comp e₁.inv e₂

instance (α β) [DecidableEq β] [Finite α] [Finite β] : Finite (α × β) :=
  let e₁ := Fin.equivProd (Finite.size α) (Finite.size β)
  let e₂ := Prod.equiv (Finite.toEquiv α) (Finite.toEquiv β)
  Finite.ofEquiv <| Equiv.comp e₁.inv e₂

instance {α : Type _} (β : α → Type _) [Finite α] [(x : α) → Finite (β x)] : Finite ((x : α) × (β x)) :=
  let e₁ := Fin.equivSigma (fun i => Finite.size (β ((Finite.toEquiv α).rev i)))
  let e₂ := Sigma.equiv (Finite.toEquiv α).inv (fun i => (Finite.toEquiv (β ((Finite.toEquiv α).inv.fwd i))).inv)
  Finite.ofEquiv <| Equiv.comp e₁.inv e₂.inv

instance (α β) [Finite α] [DecidableEq β] : DecidableEq (α → β)
| f₁, f₂ =>
  if h : ∀ x, f₁ x = f₂ x then
    isTrue <| funext h
  else
    isFalse fun | rfl => h fun _ => rfl

instance (α β) [DecidableEq β] [Finite α] [Finite β] : Finite (α → β) :=
  let e₁ := Fin.equivFun (Finite.size β) (Finite.size α)
  let e₂ := Fun.equivND (Finite.toEquiv α) (Finite.toEquiv β)
  Finite.ofEquiv <| Equiv.comp e₁.inv e₂

instance {α} (β : α → Type _) [Finite α] [(x : α) → DecidableEq (β x)] : DecidableEq ((x : α) → β x)
| f₁, f₂ =>
  if h : ∀ x, f₁ x = f₂ x then
    isTrue <| funext h
  else
    isFalse fun | rfl => h fun _ => rfl

instance {α : Type _} (β : α → Type _) [Finite α] [(x : α) → Finite (β x)] : Finite ((x : α) → β x) :=
  let e₁ := Fin.equivPi (fun i => Finite.size (β ((Finite.toEquiv α).rev i)))
  let e₂ := Fun.equiv (Finite.toEquiv α).inv (fun i => (Finite.toEquiv (β ((Finite.toEquiv α).inv.fwd i))).inv)
  Finite.ofEquiv <| Equiv.comp e₁.inv e₂.inv

instance (p : α → Prop) [DecidablePred p] [Finite α] : Finite (Subtype p) :=
  let e₁ := Fin.equivSubtype fun i => p ((Finite.toEquiv α).rev i)
  let e₂ := Subtype.equiv (Finite.toEquiv α) (by intro; simp [Equiv.rev_fwd])
  Finite.ofEquiv <| Equiv.comp e₁.inv e₂

instance (s : Setoid α) [DecidableRel s.r] [Finite α] : Finite (Quotient s) :=
  let s' : Setoid (Fin (Finite.size α)) := {
    r := fun i j => (Finite.toEquiv α).rev i ≈ (Finite.toEquiv α).rev j
    iseqv := Equivalence.mk (fun _ => s.refl _) s.symm s.trans
  }
  let e₁ := Fin.equivQuotient s'
  let e₂ := Quotient.equiv (s₁:=s) (s₂:=s') (Finite.toEquiv α) $ by
    intro x y
    constr
    · intro h
      show (Finite.toEquiv α).rev ((Finite.toEquiv α).fwd x) ≈ (Finite.toEquiv α).rev ((Finite.toEquiv α).fwd y)
      rw [Equiv.rev_fwd, Equiv.rev_fwd]
      exact h
    · intro h
      have h : (Finite.toEquiv α).rev ((Finite.toEquiv α).fwd x) ≈ (Finite.toEquiv α).rev ((Finite.toEquiv α).fwd y) := h
      rw [Equiv.rev_fwd, Equiv.rev_fwd] at h
      exact h
  Finite.ofEquiv <| Equiv.comp e₁.inv e₂

end Finite


/-
Copyright (c) 2019 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import algebraic_geometry.presheafed_space.has_colimits
import topology.sheaves.functors

/-!
# Sheafed spaces

Introduces the category of topological spaces equipped with a sheaf (taking values in an
arbitrary target category `C`.)

We further describe how to apply functors and natural transformations to the values of the
presheaves.
-/

universes v u

open category_theory
open Top
open topological_space
open opposite
open category_theory.limits
open category_theory.category category_theory.functor

variables (C : Type u) [category.{v} C] [has_products.{v} C]

local attribute [tidy] tactic.op_induction'

namespace algebraic_geometry

/-- A `SheafedSpace C` is a topological space equipped with a sheaf of `C`s. -/
structure SheafedSpace extends PresheafedSpace.{v} C :=
(is_sheaf : presheaf.is_sheaf)

variables {C}

namespace SheafedSpace

instance coe_carrier : has_coe (SheafedSpace C) Top :=
{ coe := λ X, X.carrier }

/-- Extract the `sheaf C (X : Top)` from a `SheafedSpace C`. -/
def sheaf (X : SheafedSpace C) : sheaf C (X : Top.{v}) := ⟨X.presheaf, X.is_sheaf⟩

@[simp] lemma as_coe (X : SheafedSpace.{v} C) : X.carrier = (X : Top.{v}) := rfl
@[simp] lemma mk_coe (carrier) (presheaf) (h) :
  (({ carrier := carrier, presheaf := presheaf, is_sheaf := h } : SheafedSpace.{v} C) :
  Top.{v}) = carrier :=
rfl

instance (X : SheafedSpace.{v} C) : topological_space X := X.carrier.str

/-- The trivial `unit` valued sheaf on any topological space. -/
def unit (X : Top) : SheafedSpace (discrete unit) :=
{ is_sheaf := presheaf.is_sheaf_unit _,
  ..@PresheafedSpace.const (discrete unit) _ X ⟨⟨⟩⟩ }

instance : inhabited (SheafedSpace (discrete _root_.unit)) := ⟨unit (Top.of pempty)⟩

instance : category (SheafedSpace C) :=
show category (induced_category (PresheafedSpace.{v} C) SheafedSpace.to_PresheafedSpace),
by apply_instance

/-- Forgetting the sheaf condition is a functor from `SheafedSpace C` to `PresheafedSpace C`. -/
@[derive [full, faithful]]
def forget_to_PresheafedSpace : (SheafedSpace.{v} C) ⥤ (PresheafedSpace.{v} C) :=
induced_functor _

instance is_PresheafedSpace_iso {X Y : SheafedSpace.{v} C} (f : X ⟶ Y) [is_iso f] :
  @is_iso (PresheafedSpace C) _ _ _ f :=
SheafedSpace.forget_to_PresheafedSpace.map_is_iso f

variables {C}

section
local attribute [simp] id comp

@[simp] lemma id_base (X : SheafedSpace C) :
  ((𝟙 X) : X ⟶ X).base = (𝟙 (X : Top.{v})) := rfl

lemma id_c (X : SheafedSpace C) :
  ((𝟙 X) : X ⟶ X).c = eq_to_hom (presheaf.pushforward.id_eq X.presheaf).symm := rfl

@[simp] lemma id_c_app (X : SheafedSpace C) (U) :
  ((𝟙 X) : X ⟶ X).c.app U = eq_to_hom (by { induction U using opposite.rec, cases U, refl }) :=
by { induction U using opposite.rec, cases U, simp only [id_c], dsimp, simp, }

@[simp] lemma comp_base {X Y Z : SheafedSpace C} (f : X ⟶ Y) (g : Y ⟶ Z) :
  (f ≫ g).base = f.base ≫ g.base := rfl

@[simp] lemma comp_c_app {X Y Z : SheafedSpace C} (α : X ⟶ Y) (β : Y ⟶ Z) (U) :
  (α ≫ β).c.app U = (β.c).app U ≫ (α.c).app (op ((opens.map (β.base)).obj (unop U)))
:= rfl

lemma comp_c_app' {X Y Z : SheafedSpace C} (α : X ⟶ Y) (β : Y ⟶ Z) (U) :
  (α ≫ β).c.app (op U) = (β.c).app (op U) ≫ (α.c).app (op ((opens.map (β.base)).obj U))
:= rfl

lemma congr_app {X Y : SheafedSpace C} {α β : X ⟶ Y} (h : α = β) (U) :
  α.c.app U = β.c.app U ≫ X.presheaf.map (eq_to_hom (by subst h)) :=
PresheafedSpace.congr_app h U

variables (C)

/-- The forgetful functor from `SheafedSpace` to `Top`. -/
def forget : SheafedSpace C ⥤ Top :=
{ obj := λ X, (X : Top.{v}),
  map := λ X Y f, f.base }

end

open Top.presheaf

/--
The restriction of a sheafed space along an open embedding into the space.
-/
def restrict {U : Top} (X : SheafedSpace C)
  {f : U ⟶ (X : Top.{v})} (h : open_embedding f) : SheafedSpace C :=
{ is_sheaf := λ ι 𝒰, ⟨is_limit.of_iso_limit
    ((is_limit.postcompose_inv_equiv _ _).inv_fun (X.is_sheaf _).some)
    (sheaf_condition_equalizer_products.fork.iso_of_open_embedding h 𝒰).symm⟩,
  ..X.to_PresheafedSpace.restrict h }

/--
The restriction of a sheafed space `X` to the top subspace is isomorphic to `X` itself.
-/
def restrict_top_iso (X : SheafedSpace C) :
  X.restrict (opens.open_embedding ⊤) ≅ X :=
forget_to_PresheafedSpace.preimage_iso X.to_PresheafedSpace.restrict_top_iso

/--
The global sections, notated Gamma.
-/
def Γ : (SheafedSpace C)ᵒᵖ ⥤ C :=
forget_to_PresheafedSpace.op ⋙ PresheafedSpace.Γ

lemma Γ_def : (Γ : _ ⥤ C) = forget_to_PresheafedSpace.op ⋙ PresheafedSpace.Γ := rfl

@[simp] lemma Γ_obj (X : (SheafedSpace C)ᵒᵖ) : Γ.obj X = (unop X).presheaf.obj (op ⊤) := rfl

lemma Γ_obj_op (X : SheafedSpace C) : Γ.obj (op X) = X.presheaf.obj (op ⊤) := rfl

@[simp] lemma Γ_map {X Y : (SheafedSpace C)ᵒᵖ} (f : X ⟶ Y) :
  Γ.map f = f.unop.c.app (op ⊤) := rfl

lemma Γ_map_op {X Y : SheafedSpace C} (f : X ⟶ Y) :
  Γ.map f.op = f.c.app (op ⊤) := rfl

noncomputable
instance [has_limits C] : creates_colimits (forget_to_PresheafedSpace : SheafedSpace C ⥤ _) :=
⟨λ J hJ, by exactI ⟨λ K, creates_colimit_of_fully_faithful_of_iso
  ⟨(PresheafedSpace.colimit_cocone (K ⋙ forget_to_PresheafedSpace)).X,
    limit_is_sheaf _ (λ j, sheaf.pushforward_sheaf_of_sheaf _ (K.obj (unop j)).2)⟩
  (colimit.iso_colimit_cocone ⟨_, PresheafedSpace.colimit_cocone_is_colimit _⟩).symm⟩⟩

instance [has_limits C] : has_colimits (SheafedSpace C) :=
has_colimits_of_has_colimits_creates_colimits forget_to_PresheafedSpace

noncomputable instance [has_limits C] : preserves_colimits (forget C) :=
limits.comp_preserves_colimits forget_to_PresheafedSpace (PresheafedSpace.forget C)

end SheafedSpace

end algebraic_geometry


/-
Copyright (c) 2018 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes, Johannes Hölzl, Scott Morrison, Jens Wagemaker
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.polynomial.eval
import Mathlib.algebra.algebra.tower
import Mathlib.PostPort

universes u z u_1 u_2 u_3 v

namespace Mathlib

/-!
# Theory of univariate polynomials

We show that `polynomial A` is an R-algebra when `A` is an R-algebra.
We promote `eval₂` to an algebra hom in `aeval`.
-/

namespace polynomial

/-- Note that this instance also provides `algebra R (polynomial R)`. -/
protected instance algebra_of_algebra {R : Type u} {A : Type z} [comm_semiring R] [semiring A]
    [algebra R A] : algebra R (polynomial A) :=
  add_monoid_algebra.algebra

theorem algebra_map_apply {R : Type u} {A : Type z} [comm_semiring R] [semiring A] [algebra R A]
    (r : R) : coe_fn (algebra_map R (polynomial A)) r = coe_fn C (coe_fn (algebra_map R A) r) :=
  rfl

/--
When we have `[comm_ring R]`, the function `C` is the same as `algebra_map R (polynomial R)`.

(But note that `C` is defined when `R` is not necessarily commutative, in which case
`algebra_map` is not available.)
-/
theorem C_eq_algebra_map {R : Type u_1} [comm_ring R] (r : R) :
    coe_fn C r = coe_fn (algebra_map R (polynomial R)) r :=
  rfl

@[simp] theorem alg_hom_eval₂_algebra_map {R : Type u_1} {A : Type u_2} {B : Type u_3} [comm_ring R]
    [ring A] [ring B] [algebra R A] [algebra R B] (p : polynomial R) (f : alg_hom R A B) (a : A) :
    coe_fn f (eval₂ (algebra_map R A) a p) = eval₂ (algebra_map R B) (coe_fn f a) p :=
  sorry

@[simp] theorem eval₂_algebra_map_X {R : Type u_1} {A : Type u_2} [comm_ring R] [ring A]
    [algebra R A] (p : polynomial R) (f : alg_hom R (polynomial R) A) :
    eval₂ (algebra_map R A) (coe_fn f X) p = coe_fn f p :=
  sorry

@[simp] theorem ring_hom_eval₂_algebra_map_int {R : Type u_1} {S : Type u_2} [ring R] [ring S]
    (p : polynomial ℤ) (f : R →+* S) (r : R) :
    coe_fn f (eval₂ (algebra_map ℤ R) r p) = eval₂ (algebra_map ℤ S) (coe_fn f r) p :=
  alg_hom_eval₂_algebra_map p (ring_hom.to_int_alg_hom f) r

@[simp] theorem eval₂_algebra_map_int_X {R : Type u_1} [ring R] (p : polynomial ℤ)
    (f : polynomial ℤ →+* R) : eval₂ (algebra_map ℤ R) (coe_fn f X) p = coe_fn f p :=
  sorry

-- Unfortunately `f.to_int_alg_hom` doesn't work here, as typeclasses don't match up correctly.

theorem eval₂_comp {R : Type u} {S : Type v} [comm_semiring R] {p : polynomial R} {q : polynomial R}
    [comm_semiring S] (f : R →+* S) {x : S} : eval₂ f x (comp p q) = eval₂ f (eval₂ f x q) p :=
  sorry

theorem eval_comp {R : Type u} {a : R} [comm_semiring R] {p : polynomial R} {q : polynomial R} :
    eval a (comp p q) = eval (eval a q) p :=
  eval₂_comp (ring_hom.id R)

protected instance comp.is_semiring_hom {R : Type u} [comm_semiring R] {p : polynomial R} :
    is_semiring_hom fun (q : polynomial R) => comp q p :=
  eq.mpr
    (id
      ((fun (f f_1 : polynomial R → polynomial R) (e_3 : f = f_1) => congr_arg is_semiring_hom e_3)
        (fun (q : polynomial R) => comp q p) (fun (q : polynomial R) => eval₂ C p q)
        (funext fun (q : polynomial R) => comp.equations._eqn_1 q p)))
    (eval₂.is_semiring_hom C p)

/-- Given a valuation `x` of the variable in an `R`-algebra `A`, `aeval R A x` is
the unique `R`-algebra homomorphism from `R[X]` to `A` sending `X` to `x`. -/
def aeval {R : Type u} {A : Type z} [comm_semiring R] [semiring A] [algebra R A] (x : A) :
    alg_hom R (polynomial R) A :=
  alg_hom.mk (ring_hom.to_fun (eval₂_ring_hom' (algebra_map R A) x sorry)) sorry sorry sorry sorry
    sorry

theorem alg_hom_ext {R : Type u} {A : Type z} [comm_semiring R] [semiring A] [algebra R A]
    {f : alg_hom R (polynomial R) A} {g : alg_hom R (polynomial R) A}
    (h : coe_fn f X = coe_fn g X) : f = g :=
  add_monoid_algebra.alg_hom_ext' (monoid_hom.ext_mnat h)

theorem aeval_def {R : Type u} {A : Type z} [comm_semiring R] [semiring A] [algebra R A] (x : A)
    (p : polynomial R) : coe_fn (aeval x) p = eval₂ (algebra_map R A) x p :=
  rfl

@[simp] theorem aeval_zero {R : Type u} {A : Type z} [comm_semiring R] [semiring A] [algebra R A]
    (x : A) : coe_fn (aeval x) 0 = 0 :=
  alg_hom.map_zero (aeval x)

@[simp] theorem aeval_X {R : Type u} {A : Type z} [comm_semiring R] [semiring A] [algebra R A]
    (x : A) : coe_fn (aeval x) X = x :=
  eval₂_X (algebra_map R A) x

@[simp] theorem aeval_C {R : Type u} {A : Type z} [comm_semiring R] [semiring A] [algebra R A]
    (x : A) (r : R) : coe_fn (aeval x) (coe_fn C r) = coe_fn (algebra_map R A) r :=
  eval₂_C (algebra_map R A) x

theorem aeval_monomial {R : Type u} {A : Type z} [comm_semiring R] [semiring A] [algebra R A]
    (x : A) {n : ℕ} {r : R} :
    coe_fn (aeval x) (coe_fn (monomial n) r) = coe_fn (algebra_map R A) r * x ^ n :=
  eval₂_monomial (algebra_map R A) x

@[simp] theorem aeval_X_pow {R : Type u} {A : Type z} [comm_semiring R] [semiring A] [algebra R A]
    (x : A) {n : ℕ} : coe_fn (aeval x) (X ^ n) = x ^ n :=
  eval₂_X_pow (algebra_map R A) x

@[simp] theorem aeval_add {R : Type u} {A : Type z} [comm_semiring R] {p : polynomial R}
    {q : polynomial R} [semiring A] [algebra R A] (x : A) :
    coe_fn (aeval x) (p + q) = coe_fn (aeval x) p + coe_fn (aeval x) q :=
  alg_hom.map_add (aeval x) p q

@[simp] theorem aeval_one {R : Type u} {A : Type z} [comm_semiring R] [semiring A] [algebra R A]
    (x : A) : coe_fn (aeval x) 1 = 1 :=
  alg_hom.map_one (aeval x)

@[simp] theorem aeval_bit0 {R : Type u} {A : Type z} [comm_semiring R] {p : polynomial R}
    [semiring A] [algebra R A] (x : A) : coe_fn (aeval x) (bit0 p) = bit0 (coe_fn (aeval x) p) :=
  alg_hom.map_bit0 (aeval x) p

@[simp] theorem aeval_bit1 {R : Type u} {A : Type z} [comm_semiring R] {p : polynomial R}
    [semiring A] [algebra R A] (x : A) : coe_fn (aeval x) (bit1 p) = bit1 (coe_fn (aeval x) p) :=
  alg_hom.map_bit1 (aeval x) p

@[simp] theorem aeval_nat_cast {R : Type u} {A : Type z} [comm_semiring R] [semiring A]
    [algebra R A] (x : A) (n : ℕ) : coe_fn (aeval x) ↑n = ↑n :=
  alg_hom.map_nat_cast (aeval x) n

theorem aeval_mul {R : Type u} {A : Type z} [comm_semiring R] {p : polynomial R} {q : polynomial R}
    [semiring A] [algebra R A] (x : A) :
    coe_fn (aeval x) (p * q) = coe_fn (aeval x) p * coe_fn (aeval x) q :=
  alg_hom.map_mul (aeval x) p q

theorem aeval_comp {R : Type u} [comm_semiring R] {p : polynomial R} {q : polynomial R}
    {A : Type u_1} [comm_semiring A] [algebra R A] (x : A) :
    coe_fn (aeval x) (comp p q) = coe_fn (aeval (coe_fn (aeval x) q)) p :=
  eval₂_comp (algebra_map R A)

@[simp] theorem aeval_map {R : Type u} [comm_semiring R] {B : Type u_1} [semiring B] [algebra R B]
    {A : Type u_2} [comm_semiring A] [algebra R A] [algebra A B] [is_scalar_tower R A B] (b : B)
    (p : polynomial R) : coe_fn (aeval b) (map (algebra_map R A) p) = coe_fn (aeval b) p :=
  sorry

theorem eval_unique {R : Type u} {A : Type z} [comm_semiring R] [semiring A] [algebra R A]
    (φ : alg_hom R (polynomial R) A) (p : polynomial R) :
    coe_fn φ p = eval₂ (algebra_map R A) (coe_fn φ X) p :=
  sorry

theorem aeval_alg_hom {R : Type u} {A : Type z} [comm_semiring R] [semiring A] [algebra R A]
    {B : Type u_1} [semiring B] [algebra R B] (f : alg_hom R A B) (x : A) :
    aeval (coe_fn f x) = alg_hom.comp f (aeval x) :=
  sorry

theorem aeval_alg_hom_apply {R : Type u} {A : Type z} [comm_semiring R] [semiring A] [algebra R A]
    {B : Type u_1} [semiring B] [algebra R B] (f : alg_hom R A B) (x : A) (p : polynomial R) :
    coe_fn (aeval (coe_fn f x)) p = coe_fn f (coe_fn (aeval x) p) :=
  iff.mp alg_hom.ext_iff (aeval_alg_hom f x) p

@[simp] theorem coe_aeval_eq_eval {R : Type u} [comm_semiring R] (r : R) : ⇑(aeval r) = eval r :=
  rfl

theorem coeff_zero_eq_aeval_zero {R : Type u} [comm_semiring R] (p : polynomial R) :
    coeff p 0 = coe_fn (aeval 0) p :=
  sorry

theorem pow_comp {R : Type u} [comm_semiring R] (p : polynomial R) (q : polynomial R) (k : ℕ) :
    comp (p ^ k) q = comp p q ^ k :=
  sorry

theorem is_root_of_eval₂_map_eq_zero {R : Type u} {S : Type v} [comm_semiring R] {p : polynomial R}
    [comm_ring S] {f : R →+* S} (hf : function.injective ⇑f) {r : R} :
    eval₂ f (coe_fn f r) p = 0 → is_root p r :=
  sorry

theorem is_root_of_aeval_algebra_map_eq_zero {R : Type u} {S : Type v} [comm_semiring R]
    [comm_ring S] [algebra R S] {p : polynomial R} (inj : function.injective ⇑(algebra_map R S))
    {r : R} (hr : coe_fn (aeval (coe_fn (algebra_map R S) r)) p = 0) : is_root p r :=
  is_root_of_eval₂_map_eq_zero inj hr

theorem dvd_term_of_dvd_eval_of_dvd_terms {S : Type v} [comm_ring S] {z : S} {p : S}
    {f : polynomial S} (i : ℕ) (dvd_eval : p ∣ eval z f)
    (dvd_terms : ∀ (j : ℕ), j ≠ i → p ∣ coeff f j * z ^ j) : p ∣ coeff f i * z ^ i :=
  sorry

theorem dvd_term_of_is_root_of_dvd_terms {S : Type v} [comm_ring S] {r : S} {p : S}
    {f : polynomial S} (i : ℕ) (hr : is_root f r) (h : ∀ (j : ℕ), j ≠ i → p ∣ coeff f j * r ^ j) :
    p ∣ coeff f i * r ^ i :=
  dvd_term_of_dvd_eval_of_dvd_terms i (Eq.symm hr ▸ dvd_zero p) h

theorem aeval_eq_sum_range {R : Type u} {S : Type v} [comm_semiring R] [comm_ring S] [algebra R S]
    {p : polynomial R} (x : S) :
    coe_fn (aeval x) p =
        finset.sum (finset.range (nat_degree p + 1)) fun (i : ℕ) => coeff p i • x ^ i :=
  sorry

theorem aeval_eq_sum_range' {R : Type u} {S : Type v} [comm_semiring R] [comm_ring S] [algebra R S]
    {p : polynomial R} {n : ℕ} (hn : nat_degree p < n) (x : S) :
    coe_fn (aeval x) p = finset.sum (finset.range n) fun (i : ℕ) => coeff p i • x ^ i :=
  sorry

/--
The evaluation map is not generally multiplicative when the coefficient ring is noncommutative,
but nevertheless any polynomial of the form `p * (X - monomial 0 r)` is sent to zero
when evaluated at `r`.

This is the key step in our proof of the Cayley-Hamilton theorem.
-/
theorem eval_mul_X_sub_C {R : Type u} [ring R] {p : polynomial R} (r : R) :
    eval r (p * (X - coe_fn C r)) = 0 :=
  sorry

theorem not_is_unit_X_sub_C {R : Type u} [ring R] [nontrivial R] {r : R} :
    ¬is_unit (X - coe_fn C r) :=
  sorry

theorem aeval_endomorphism {R : Type u} {M : Type u_1} [comm_ring R] [add_comm_group M] [module R M]
    (f : linear_map R M M) (v : M) (p : polynomial R) :
    coe_fn (coe_fn (aeval f) p) v = finsupp.sum p fun (n : ℕ) (b : R) => b • coe_fn (f ^ n) v :=
  sorry

end Mathlib


universes u v

inductive imp (st : Type u)
| Seq : imp → imp → imp
| Modify : (st → st) → imp
| If : (st → Prop) → imp → imp → imp
| While : (st → Prop) → imp → imp

def Skip {α : Type u}: imp α := imp.Modify id

def repeat {α : Type u} (f : α → α) : α → ℕ → α
| v 0 := v
| v (nat.succ a) := repeat (f v) a

def imp_repeat {α : Type u} (f : imp α): ℕ → imp α := repeat (imp.Seq f) Skip

lemma repeat_succ {α : Type u} (f : α → α) (s : α) (n : ℕ):
   repeat f s (nat.succ n) = f (repeat f s n) :=
begin
unfold repeat,
induction n generalizing s,
trivial,
unfold repeat,
apply n_ih,
end