import .lovelib

/-! # LoVe Demo 8: Operational Semantics

In this and the next two lectures, we will see how to use Lean to specify the
syntax and semantics of programming languages and to reason about the
semantics. -/

set_option pp.beta true

namespace LoVe

/-! ## Formal Semantics

A formal semantics helps specify and reason about the programming language
itself, and about individual programs.

It can form the basis of verified compilers, interpreters, verifiers, static
analyzers, type checkers, etc. Without formal proofs, these tools are
**almost always wrong**.

In this area, proof assistants are widely used. Every year, about 10-20% of POPL
papers are partially or totally formalized. Reasons for this success:

* Little machinery (background libraries, tactics) is needed to get started,
  beyond inductive types and predicates and recursive functions.

* The proofs tend to have lots of cases, which is a good match for computers.

* Proof assistants keep track of what needs to be changed when as extend the
  programming language with more features.

Case in point: WebAssembly. To quote Conrad Watt (with some abbreviations):

    We have produced a full Isabelle mechanisation of the core execution
    semantics and type system of the WebAssembly language. To complete this
    proof, **several deficiencies** in the official WebAssembly specification,
    uncovered by our proof and modelling work, needed to be corrected. In some
    cases, these meant that the type system was **originally unsound**.

    We have maintained a constructive dialogue with the working group,
    verifying new features as they are added. In particular, the mechanism by
    which a WebAssembly implementation interfaces with its host environment was
    not formally specified in the working group's original paper. Extending our
    mechanisation to model this feature revealed a deficiency in the WebAssembly
    specification that **sabotaged the soundness** of the type system.

## A Minimalistic Imperative Language

__WHILE__ is a minimalistic imperative language with the following grammar:

    S  ::=  skip                 -- no-op
         |  x := a               -- assignment
         |  S ; S                -- sequential composition
         |  if b then S else S   -- conditional statement
         |  while b do S         -- while loop

where `S` stands for a statement (also called command or program), `x` for a
variable, `a` for an arithmetic expression, and `b` for a Boolean expression. -/

inductive stmt : Type
| skip   : stmt
| assign : string → (state → ℕ) → stmt
| seq    : stmt → stmt → stmt
| ite    : (state → Prop) → stmt → stmt → stmt
| while  : (state → Prop) → stmt → stmt

infixr ` ;; ` : 90 := stmt.seq

/-! In our grammer, we deliberately leave the syntax of arithmetic and Boolean
expressions unspecified. In Lean, we have the choice:

* We could use a type such as `aexp` from lecture 1 and similarly for Boolean
  expressions.

* Supposing a state `s` is a function from variable names to values
  (`string → ℕ`), we could decide that an arithmetic expression is simply a
  function from states to natural numbers (`state → ℕ`) and a Boolean expression
  is a predicate (`state → Prop` or `state → bool`).

This corresponds to the difference between deep and shallow embeddings:

* A __deep embedding__ of some syntax (expression, formula, program, etc.)
  consists of an abstract syntax tree specified in the proof assistant
  (e.g., `aexp`) with a semantics (e.g., `eval`).

* In contrast, a __shallow embedding__ simply reuses the corresponding
  mechanisms from the logic (e.g., λ-terms, functions and predicate types).

A deep embedding allows us to reason about the syntax (and its semantics). A
shallow embedding is more lightweight, because we can use it directly, without
having to define a semantics.

We will use a deep embedding of programs (which we find interesting), and
shallow embeddings of assignments and Boolean expressions (which we find
boring).

Examples:

    `λs : state, s "x" + s "y" + 1`   -- x + y + 1
    `λs : state, s "a" ≠ s "b"`       -- a ≠ b

## Big-Step Semantics

An __operational semantics__ corresponds to an idealized interpreter (specified
in a Prolog-like language). Two main variants:

* big-step semantics;

* small-step semantics.

In a __big-step semantics__ (also called __natural semantics__), judgments have
the form `(S, s) ⟹ t`:

    Starting in a state `s`, executing `S` terminates in the state `t`.

Example:

    `(x := x + y; y := 0, [x ↦ 3, y ↦ 5]) ⟹ [x ↦ 8, y ↦ 0]`

Derivation rules:

    ——————————————— Skip
    (skip, s) ⟹ s

    ——————————————————————————— Asn
    (x := a, s) ⟹ s[x ↦ s(a)]

    (S, s) ⟹ s'   (S', s') ⟹ s''
    ——————————————————————————————— Seq
    (S; S', s) ⟹ s''

    (S, s) ⟹ s'
    ——————————————————————————————— If-True   if s(b) is true
    (if b then S else S', s) ⟹ s'

    (S', s) ⟹ s'
    ——————————————————————————————— If-False   if s(b) is false
    (if b then S else S', s) ⟹ s'

    (S, s) ⟹ s'   (while b do S, s') ⟹ s''
    —————————————————————————————————————————— While-True   if s(b) is true
    (while b do S, s) ⟹ s''

    ————————————————————————— While-False   if s(b) is false
    (while b do S, s) ⟹ s

Above, `s(e)` denotes the value of expression `e` in state `s`.

In Lean, the judgment corresponds to an inductive predicate, and the derivation
rules correspond to the predicate's introduction rules. Using an inductive
predicate as opposed to a recursive function allows us to cope with
nontermination (e.g., a diverging `while`) and nondeterminism (e.g.,
multithreading). -/

inductive big_step : stmt × state → state → Prop
| skip {s} :
  big_step (stmt.skip, s) s
| assign {x a s} :
  big_step (stmt.assign x a, s) (s{x ↦ a s})
| seq {S T s t u} (hS : big_step (S, s) t)
    (hT : big_step (T, t) u) :
  big_step (S ;; T, s) u
| ite_true {b : state → Prop} {S T s t} (hcond : b s)
    (hbody : big_step (S, s) t) :
  big_step (stmt.ite b S T, s) t
| ite_false {b : state → Prop} {S T s t} (hcond : ¬ b s)
    (hbody : big_step (T, s) t) :
  big_step (stmt.ite b S T, s) t
| while_true {b : state → Prop} {S s t u} (hcond : b s)
    (hbody : big_step (S, s) t)
    (hrest : big_step (stmt.while b S, t) u) :
  big_step (stmt.while b S, s) u
| while_false {b : state → Prop} {S s} (hcond : ¬ b s) :
  big_step (stmt.while b S, s) s

infix ` ⟹ ` : 110 := big_step

/-! ## Properties of the Big-Step Semantics

Equipped with a big-step semantics, we can

* prove properties of the programming language, such as **equivalence proofs**
  between programs and **determinism**;

* reason about **concrete programs**, proving theorems relating final states `t`
  with initial states `s`. -/

lemma big_step_deterministic {S s l r} (hl : (S, s) ⟹ l)
    (hr : (S, s) ⟹ r) :
  l = r :=
begin
    induction hl generalizing r,
    case big_step.skip : s t {
        cases hr,
        refl },
    case big_step.assign : x a s {
        cases hr,
        refl },
    case big_step.seq : S T s t l hS hT ihS ihT {
        cases hr with _ _ _ _ _ _ _ t' _ hS' hT',
        cases ihS hS',
        cases ihT hT',
        refl },
    case big_step.ite_true : b S T s t hb hS ih {
        cases hr,
        { apply ih,
        assumption },
        { apply ih,
        cc } },
    case big_step.ite_false : b S T s t hb hT ih {
        cases hr,
        { apply ih,
        cc },
        { apply ih,
        assumption } },
    case big_step.while_true : b S s t u hb hS hw ihS ihw {
        cases hr,
        { cases ihS hr_hbody,
        cases ihw hr_hrest,
        refl },
        { cc } },
    { cases hr,
        { cc },
        { refl } }
end

lemma big_step_terminates {S s} :
  ∃t, (S, s) ⟹ t :=
sorry   -- unprovable

lemma big_step_doesnt_terminate {S s t} :
  ¬ (stmt.while (λ_, true) S, s) ⟹ t :=
begin
  generalize hws : (stmt.while (λ_, true) S, s) = ws,
  intro hw,
  induction hw generalizing s,
  case big_step.while_true : b S s t u hcond hbody hrest ih_body
      ih_rest {
    cases hws,
    apply ih_rest,
    refl },
  case big_step.while_false : b S s hcond {
    cases hws,
    apply hcond,
    apply true.intro },
  all_goals { cases hws }
end

@[simp] lemma big_step_skip_iff {s t} :
  (stmt.skip, s) ⟹ t ↔ t = s :=
begin
  apply iff.intro,
  { intro h,
    cases h,
    refl },
  { intro h,
    rewrite h,
    exact big_step.skip }
end

@[simp] lemma big_step_assign_iff {x a s t} :
  (stmt.assign x a, s) ⟹ t ↔ t = s{x ↦ a s} :=
begin
  apply iff.intro,
  { intro h,
    cases h,
    refl },
  { intro h,
    rewrite h,
    exact big_step.assign }
end

@[simp] lemma big_step_seq_iff {S T s t} :
  (S ;; T, s) ⟹ t ↔ (∃u, (S, s) ⟹ u ∧ (T, u) ⟹ t) :=
begin
  apply iff.intro,
  { intro h,
    cases h,
    apply exists.intro,
    apply and.intro; assumption },
  { intro h,
    cases h,
    cases h_h,
    apply big_step.seq; assumption }
end

@[simp] lemma big_step_ite_iff {b S T s t} :
  (stmt.ite b S T, s) ⟹ t ↔
  (b s ∧ (S, s) ⟹ t) ∨ (¬ b s ∧ (T, s) ⟹ t) :=
begin
  apply iff.intro,
  { intro h,
    cases h,
    { apply or.intro_left,
      cc },
    { apply or.intro_right,
      cc } },
  { intro h,
    cases h; cases h,
    { apply big_step.ite_true; assumption },
    { apply big_step.ite_false; assumption } }
end

lemma big_step_while_iff {b S s u} :
  (stmt.while b S, s) ⟹ u ↔
  (∃t, b s ∧ (S, s) ⟹ t ∧ (stmt.while b S, t) ⟹ u)
  ∨ (¬ b s ∧ u = s) :=
begin
  apply iff.intro,
  { intro h,
    cases h,
    { apply or.intro_left,
      apply exists.intro h_t,
      cc },
    { apply or.intro_right,
      cc } },
  { intro h,
    cases h,
    case or.inl {
      cases h with t h,
      cases h with hb h,
      cases h with hS hwhile,
      exact big_step.while_true hb hS hwhile },
    case or.inr {
      cases h with hb hus,
      rewrite hus,
      exact big_step.while_false hb } }
end

lemma big_step_while_true_iff {b : state → Prop} {S s u}
    (hcond : b s) :
  (stmt.while b S, s) ⟹ u ↔
  (∃t, (S, s) ⟹ t ∧ (stmt.while b S, t) ⟹ u) :=
by rewrite big_step_while_iff; simp [hcond]

@[simp] lemma big_step_while_false_iff {b : state → Prop}
    {S s t} (hcond : ¬ b s) :
  (stmt.while b S, s) ⟹ t ↔ t = s :=
by rewrite big_step_while_iff; simp [hcond]

/-! ## Small-Step Semantics

A big-step semantics

* does not let us reason about intermediate states;

* does not let us express nontermination or interleaving (for multithreading).

__Small-step semantics__ (also called __structural operational semantics__)
solve the above issues.

A judgment has the form `(S, s) ⇒ (T, t)`:

    Starting in a state `s`, executing one step of `S` leaves us in the
    state `t`, with the program `T` remaining to be executed.

An execution is a finite or infinite chain `(S₀, s₀) ⇒ (S₁, s₁) ⇒ …`.

A pair `(S, s)` is called a __configuration__. It is __final__ if no transition
of the form `(S, s) ⇒ _` is possible.

Example:

      `(x := x + y; y := 0, [x ↦ 3, y ↦ 5])`
    `⇒ (skip; y := 0,       [x ↦ 8, y ↦ 5])`
    `⇒ (y := 0,             [x ↦ 8, y ↦ 5])`
    `⇒ (skip,               [x ↦ 8, y ↦ 0])`

Derivation rules:

    ————————————————————————————————— Asn
    (x := a, s) ⇒ (skip, s[x ↦ s(a)])

    (S, s) ⇒ (S', s')
    ———————————————————————— Seq-Step
    (S; T, s) ⇒ (S' ; T, s')

    —————————————————————— Seq-Skip
    (skip ; S, s) ⇒ (S, s)

    ————————————————————————————————— If-True   if s(b) is true
    (if b then S else S', s) ⇒ (S, s)

    —————————————————————————————————— If-False   if s(b) is false
    (if b then S else S', s) ⇒ (S', s)

    ————————————————————————————————————————————————————————————————— While
    (while b do S, s) ⇒ (if b then (S ; while b do S) else skip, s)

There is no rule for `skip` (why?). -/

inductive small_step : stmt × state → stmt × state → Prop
| assign {x a s} :
  small_step (stmt.assign x a, s) (stmt.skip, s{x ↦ a s})
| seq_step {S S' T s s'} (hS : small_step (S, s) (S', s')) :
  small_step (S ;; T, s) (S' ;; T, s')
| seq_skip {T s} :
  small_step (stmt.skip ;; T, s) (T, s)
| ite_true {b : state → Prop} {S T s} (hcond : b s) :
  small_step (stmt.ite b S T, s) (S, s)
| ite_false {b : state → Prop} {S T s} (hcond : ¬ b s) :
  small_step (stmt.ite b S T, s) (T, s)
| while {b : state → Prop} {S s} :
  small_step (stmt.while b S, s)
    (stmt.ite b (S ;; stmt.while b S) stmt.skip, s)

infixr ` ⇒ ` := small_step
infixr ` ⇒* ` : 100 := star small_step

/-! Equipped with a small-step semantics, we can **define** a big-step
semantics:

> `(S, s) ⟹ s'` if and only if `(S, s) ⇒* (skip, s')`

where `r*` denotes the reflexive transitive closure of a relation `r`.

Alternatively, if we have already defined a big-step semantics, we can **prove**
the above equivalence theorem to validate our definitions.

The main disadvantage of small-step semantics is that we now have two relations,
`⇒` and `⇒*`, and reasoning tends to be more complicated.

## Properties of the Small-Step Semantics

We can prove that a configuration `(S, s)` is final if and only if `S = skip`.
This ensures that we have not forgotten a derivation rule. -/

lemma small_step_final {S s} :
  (¬ ∃T t, (S, s) ⇒ (T, t)) ↔ S = stmt.skip :=
begin
  induction S,
  case stmt.skip {
    simp,
    intros T t hstep,
    cases hstep },
  case stmt.assign : x a {
    simp,
    intro hall,
    exact hall _ _ small_step.assign },
  case stmt.seq : S T ihS ihT {
    simp,
    intro hall,
    cases classical.em (S = stmt.skip),
    case or.inl {
      apply hall T s,
      rewrite h,
      exact small_step.seq_skip },
    case or.inr {
      simp [h, auto.not_forall_eq, auto.not_not_eq] at ihS,
      cases ihS with S' hS',
      cases hS' with s' hs',
      apply hall (S' ;; T) s',
      exact small_step.seq_step hs' } },
  case stmt.ite : b S T ihS ihT {
    simp,
    intro hall,
    cases classical.em (b s),
    repeat {
      apply hall,
      exact small_step.ite_true h
      <|> exact small_step.ite_false h } },
  case stmt.while {
    simp,
    intro hall,
    apply hall,
    exact small_step.while }
end

lemma small_step_deterministic {S s Ll Rr}
    (hl : (S, s) ⇒ Ll) (hr : (S, s) ⇒ Rr) :
  Ll = Rr :=
begin
  induction hl generalizing Rr,
  case small_step.assign : x a s {
    cases hr,
    refl },
  case small_step.seq_step : S S₁ T s s₁ hS₁ ih {
    cases hr,
    case small_step.seq_step : S₂ s₂ hS₂ {
      have hSs₁₂ := ih hS₂,
      cc },
    case small_step.seq_skip {
      cases hS₁ } },
  case small_step.seq_skip : T s {
    cases hr,
    case small_step.seq_step : S₂ s₂ hS₂ {
      cases hS₂ },
    case small_step.seq_skip {
      refl } },
  case small_step.ite_true : b S T s hcond {
    cases hr,
    case small_step.ite_true {
      refl },
    case small_step.ite_false {
      cc } },
  case small_step.ite_false : b S T s hcond {
    cases hr,
    case small_step.ite_true {
      cc },
    case small_step.ite_false {
      refl } },
  case small_step.while : b S s {
    cases hr,
    refl }
end

/-! We can define inversion rules also about the small-step semantics. Here are
three examples: -/

lemma small_step_skip {S s t} :
  ¬ ((stmt.skip, s) ⇒ (S, t)) :=
by intro h; cases h

@[simp] lemma small_step_seq_iff {S T s Ut} :
  (S ;; T, s) ⇒ Ut ↔
  (∃S' t, (S, s) ⇒ (S', t) ∧ Ut = (S' ;; T, t))
  ∨ (S = stmt.skip ∧ Ut = (T, s)) :=
begin
  apply iff.intro,
  { intro h,
    cases h,
    { apply or.intro_left,
      apply exists.intro h_S',
      apply exists.intro h_s',
      cc },
    { apply or.intro_right,
      cc } },
  { intro h,
    cases h,
    { cases h,
      cases h_h,
      cases h_h_h,
      rewrite h_h_h_right,
      apply small_step.seq_step,
      assumption },
    { cases h,
      rewrite h_left,
      rewrite h_right,
      apply small_step.seq_skip } }
end

@[simp] lemma small_step_ite_iff {b S T s Us} :
  (stmt.ite b S T, s) ⇒ Us ↔
  (b s ∧ Us = (S, s)) ∨ (¬ b s ∧ Us = (T, s)) :=
begin
  apply iff.intro,
  { intro h,
    cases h,
    { apply or.intro_left,
      cc },
    { apply or.intro_right,
      cc } },
  { intro h,
    cases h,
    { cases h,
      rewrite h_right,
      apply small_step.ite_true,
      assumption },
    { cases h,
      rewrite h_right,
      apply small_step.ite_false,
      assumption } }
end

/-! ### Equivalence of the Big-Step and the Small-Step Semantics (**optional**)

A more important result is the connection between the big-step and the
small-step semantics:

    `(S, s) ⟹ t ↔ (S, s) ⇒* (stmt.skip, t)`

Its proof, given below, is beyond the scope of this course. -/

lemma star_small_step_seq {S T s u}
    (h : (S, s) ⇒* (stmt.skip, u)) :
  (S ;; T, s) ⇒* (stmt.skip ;; T, u) :=
begin
  apply star.lift (λSs, (prod.fst Ss ;; T, prod.snd Ss)) _ h,
  intros Ss Ss' h,
  cases Ss,
  cases Ss',
  simp,
  apply small_step.seq_step,
  assumption
end

lemma star_small_step_of_big_step {S s t} (h : (S, s) ⟹ t) :
  (S, s) ⇒* (stmt.skip, t) :=
begin
  induction h,
  case big_step.skip {
    refl },
  case big_step.assign {
    exact star.single small_step.assign },
  case big_step.seq : S T s t u hS hT ihS ihT {
    transitivity,
    exact star_small_step_seq ihS,
    apply star.head small_step.seq_skip ihT },
  case big_step.ite_true : b S T s t hs hst ih {
    exact star.head (small_step.ite_true hs) ih },
  case big_step.ite_false : b S T s t hs hst ih {
    exact star.head (small_step.ite_false hs) ih },
  case big_step.while_true : b S s t u hb hS hw ihS ihw {
    exact (star.head small_step.while
      (star.head (small_step.ite_true hb)
         (star.trans (star_small_step_seq ihS)
            (star.head small_step.seq_skip ihw)))) },
  case big_step.while_false : b S s hb {
    exact star.tail (star.single small_step.while)
      (small_step.ite_false hb) }
end

lemma big_step_of_small_step_of_big_step {S₀ S₁ s₀ s₁ s₂} :
  (S₀, s₀) ⇒ (S₁, s₁) → (S₁, s₁) ⟹ s₂ → (S₀, s₀) ⟹ s₂ :=
begin
  generalize hSs₀ : (S₀, s₀) = Ss₀,
  generalize hSs₁ : (S₁, s₁) = Ss₁,
  intro h,
  induction h generalizing S₀ s₀ S₁ s₁ s₂;
    cases hSs₁; clear hSs₁; cases hSs₀; clear hSs₀;
    simp [*, big_step_while_true_iff] { contextual := tt },
  { intros u hS' hT,
    apply exists.intro u,
    exact and.intro (h_ih (eq.refl _) (eq.refl _) hS') hT }
end

lemma big_step_of_star_small_step {S s t} :
  (S, s) ⇒* (stmt.skip, t) → (S, s) ⟹ t :=
begin
  generalize hSs : (S, s) = Ss,
  intro h,
  induction h
      using LoVe.rtc.star.head_induction_on
      with _ S's' h h' ih
      generalizing S s;
    cases hSs; clear hSs,
  { exact big_step.skip },
  { cases S's' with S' s',
    apply big_step_of_small_step_of_big_step h,
    apply ih,
    refl }
end

lemma big_step_iff_star_small_step {S s t} :
  (S, s) ⟹ t ↔ (S, s) ⇒* (stmt.skip, t) :=
iff.intro star_small_step_of_big_step
  big_step_of_star_small_step

/-! ## Parallelism (**optional**) -/

inductive par_step :
    nat → list stmt × state → list stmt × state → Prop
| intro {Ss Ss' S S' s s' i}
    (hi : i < list.length Ss)
    (hS : S = list.nth_le Ss i hi)
    (hs : (S, s) ⇒ (S', s'))
    (hS' : Ss' = list.update_nth Ss i S') :
  par_step i (Ss, s) (Ss', s')

lemma par_step_diamond {i j Ss Ts Ts' s t t'}
    (hi : i < list.length Ss)
    (hj : j < list.length Ss)
    (hij : i ≠ j)
    (hT : par_step i (Ss, s) (Ts, t))
    (hT' : par_step j (Ss, s) (Ts', t')) :
  ∃u Us, par_step j (Ts, t) (Us, u) ∧
    par_step i (Ts', t') (Us, u) :=
sorry   -- unprovable

def stmt.W : stmt → set string
| stmt.skip         := ∅
| (stmt.assign x _) := {x}
| (stmt.seq S T)    := stmt.W S ∪ stmt.W T
| (stmt.ite _ S T)  := stmt.W S ∪ stmt.W T
| (stmt.while _ S)  := stmt.W S

def exp.R {α : Type} : (state → α) → set string
| f := {x | ∀s n, f (s{x ↦ n}) ≠ f s}

def stmt.R : stmt → set string
| stmt.skip         := ∅
| (stmt.assign _ a) := exp.R a
| (stmt.seq S T)    := stmt.R S ∪ stmt.R T
| (stmt.ite b S T)  := exp.R b ∪ stmt.R S ∪ stmt.R T
| (stmt.while b S)  := exp.R b ∪ stmt.R S

def stmt.V : stmt → set string
| S := stmt.W S ∪ stmt.R S

lemma par_step_diamond_VW_disjoint {i j Ss Ts Ts' s t t'}
    (hiS : i < list.length Ss)
    (hjT : j < list.length Ts)
    (hij : i ≠ j)
    (hT : par_step i (Ss, s) (Ts, t))
    (hT' : par_step j (Ss, s) (Ts', t'))
    (hWV : stmt.W (list.nth_le Ss i hiS)
       ∩ stmt.V (list.nth_le Ts j hjT) = ∅)
    (hVW : stmt.V (list.nth_le Ss i hiS)
       ∩ stmt.W (list.nth_le Ts j hjT) = ∅) :
  ∃u Us, par_step j (Ts, t) (Us, u) ∧
    par_step i (Ts', t') (Us, u) :=
sorry   -- this should be provable

end LoVe


axiom Foo : Type
axiom foo : Foo

/- This works -/
def works : Prop :=
-- ∀ (x : Foo),
let ⟨foo₁, foo₂⟩ := (foo, foo);
false

/-
The following tests fail because the elaborator fails to propagate the expected type. The main issue is that the elaborator is missing the following case:

If the expected type is `Prop` for an expression `Forall (x : A), t`, then we should also elaborate `t` with expected type `Prop`. Note that every single example works if we write them as `Forall (x : A), (t : Prop)`.
-/

/- Uncommenting breaks it -/
def fails₁ : Prop :=
∀ (x : Foo),
let ⟨foo₁, foo₂⟩ := (foo, foo);
false
-- let_destruct_inside_forall.lean:11:4: error: invalid match/convoy expression, expected type is not known

/- All the following variations fail as well with the same message -/
def fails₂ : Prop :=
∀ (x : Foo),
let (⟨foo₁, foo₂⟩ : Foo × Foo) := (foo, foo);
foo₁ = foo₂

def fails₃ : Prop :=
∀ (x : Foo),
let (⟨foo₁, foo₂⟩ : Foo × Foo) := (foo, foo);
false

def fails₄ : Prop :=
∀ (x : Foo),
let (⟨foo₁, foo₂⟩ : Foo × Foo) := ((foo, foo) : Foo × Foo);
false

def fails₅ : Prop :=
∀ (x : Foo),
let p : Foo × Foo := (foo, foo);
let ⟨foo₁, foo₂⟩ := p;
foo₁ = foo₂


/-
Copyright (c) 2021 Rémy Degenne. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Rémy Degenne
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.measure_theory.measure_space
import Mathlib.PostPort

universes u_1 u_2 u_4

namespace Mathlib

/-!
# Sequence of measurable functions associated to a sequence of a.e.-measurable functions

We define here tools to prove statements about limits (infi, supr...) of sequences of
`ae_measurable` functions.
Given a sequence of a.e.-measurable functions `f : ι → α → β` with hypothesis
`hf : ∀ i, ae_measurable (f i) μ`, and a pointwise property `p : α → (ι → β) → Prop` such that we
have `hp : ∀ᵐ x ∂μ, p x (λ n, f n x)`, we define a sequence of measurable functions `ae_seq hf p`
and a measurable set `ae_seq_set hf p`, such that
* `μ (ae_seq_set hf p)ᶜ = 0`
* `x ∈ ae_seq_set hf p → ∀ i : ι, ae_seq hf hp i x = f i x`
* `x ∈ ae_seq_set hf p → p x (λ n, f n x)`
-/

/-- If we have the additional hypothesis `∀ᵐ x ∂μ, p x (λ n, f n x)`, this is a measurable set
whose complement has measure 0 such that for all `x ∈ ae_seq_set`, `f i x` is equal to
`(hf i).mk (f i) x` for all `i` and we have the pointwise property `p x (λ n, f n x)`. -/
def ae_seq_set {α : Type u_1} {β : Type u_2} {ι : Type u_4} [measurable_space α] [measurable_space β] {f : ι → α → β} {μ : measure_theory.measure α} (hf : ∀ (i : ι), ae_measurable (f i)) (p : α → (ι → β) → Prop) : set α :=
  measure_theory.to_measurable μ
      ((set_of fun (x : α) => (∀ (i : ι), f i x = ae_measurable.mk (f i) (hf i) x) ∧ p x fun (n : ι) => f n x)ᶜ)ᶜ

/-- A sequence of measurable functions that are equal to `f` and verify property `p` on the
measurable set `ae_seq_set hf p`. -/
def ae_seq {α : Type u_1} {β : Type u_2} {ι : Type u_4} [measurable_space α] [measurable_space β] {f : ι → α → β} {μ : measure_theory.measure α} (hf : ∀ (i : ι), ae_measurable (f i)) (p : α → (ι → β) → Prop) : ι → α → β :=
  fun (i : ι) (x : α) => ite (x ∈ ae_seq_set hf p) (ae_measurable.mk (f i) (hf i) x) (nonempty.some sorry)

namespace ae_seq

theorem mk_eq_fun_of_mem_ae_seq_set {α : Type u_1} {β : Type u_2} {ι : Type u_4} [measurable_space α] [measurable_space β] {f : ι → α → β} {μ : measure_theory.measure α} {p : α → (ι → β) → Prop} (hf : ∀ (i : ι), ae_measurable (f i)) {x : α} (hx : x ∈ ae_seq_set hf p) (i : ι) : ae_measurable.mk (f i) (hf i) x = f i x := sorry

theorem ae_seq_eq_mk_of_mem_ae_seq_set {α : Type u_1} {β : Type u_2} {ι : Type u_4} [measurable_space α] [measurable_space β] {f : ι → α → β} {μ : measure_theory.measure α} {p : α → (ι → β) → Prop} (hf : ∀ (i : ι), ae_measurable (f i)) {x : α} (hx : x ∈ ae_seq_set hf p) (i : ι) : ae_seq hf p i x = ae_measurable.mk (f i) (hf i) x := sorry

theorem ae_seq_eq_fun_of_mem_ae_seq_set {α : Type u_1} {β : Type u_2} {ι : Type u_4} [measurable_space α] [measurable_space β] {f : ι → α → β} {μ : measure_theory.measure α} {p : α → (ι → β) → Prop} (hf : ∀ (i : ι), ae_measurable (f i)) {x : α} (hx : x ∈ ae_seq_set hf p) (i : ι) : ae_seq hf p i x = f i x := sorry

theorem prop_of_mem_ae_seq_set {α : Type u_1} {β : Type u_2} {ι : Type u_4} [measurable_space α] [measurable_space β] {f : ι → α → β} {μ : measure_theory.measure α} {p : α → (ι → β) → Prop} (hf : ∀ (i : ι), ae_measurable (f i)) {x : α} (hx : x ∈ ae_seq_set hf p) : p x fun (n : ι) => ae_seq hf p n x := sorry

theorem fun_prop_of_mem_ae_seq_set {α : Type u_1} {β : Type u_2} {ι : Type u_4} [measurable_space α] [measurable_space β] {f : ι → α → β} {μ : measure_theory.measure α} {p : α → (ι → β) → Prop} (hf : ∀ (i : ι), ae_measurable (f i)) {x : α} (hx : x ∈ ae_seq_set hf p) : p x fun (n : ι) => f n x := sorry

theorem ae_seq_set_is_measurable {α : Type u_1} {β : Type u_2} {ι : Type u_4} [measurable_space α] [measurable_space β] {f : ι → α → β} {μ : measure_theory.measure α} {p : α → (ι → β) → Prop} {hf : ∀ (i : ι), ae_measurable (f i)} : is_measurable (ae_seq_set hf p) := sorry

theorem measurable {α : Type u_1} {β : Type u_2} {ι : Type u_4} [measurable_space α] [measurable_space β] {f : ι → α → β} {μ : measure_theory.measure α} (hf : ∀ (i : ι), ae_measurable (f i)) (p : α → (ι → β) → Prop) (i : ι) : measurable (ae_seq hf p i) :=
  measurable.ite ae_seq_set_is_measurable (ae_measurable.measurable_mk (hf i))
    (dite (Nonempty α) (fun (hα : Nonempty α) => measurable_const)
      fun (hα : ¬Nonempty α) => measurable_of_not_nonempty hα fun (x : α) => nonempty.some (_proof_1 i x))

theorem measure_compl_ae_seq_set_eq_zero {α : Type u_1} {β : Type u_2} {ι : Type u_4} [measurable_space α] [measurable_space β] {f : ι → α → β} {μ : measure_theory.measure α} {p : α → (ι → β) → Prop} [encodable ι] (hf : ∀ (i : ι), ae_measurable (f i)) (hp : filter.eventually (fun (x : α) => p x fun (n : ι) => f n x) (measure_theory.measure.ae μ)) : coe_fn μ (ae_seq_set hf pᶜ) = 0 := sorry

theorem ae_seq_eq_mk_ae {α : Type u_1} {β : Type u_2} {ι : Type u_4} [measurable_space α] [measurable_space β] {f : ι → α → β} {μ : measure_theory.measure α} {p : α → (ι → β) → Prop} [encodable ι] (hf : ∀ (i : ι), ae_measurable (f i)) (hp : filter.eventually (fun (x : α) => p x fun (n : ι) => f n x) (measure_theory.measure.ae μ)) : filter.eventually (fun (a : α) => ∀ (i : ι), ae_seq hf p i a = ae_measurable.mk (f i) (hf i) a)
  (measure_theory.measure.ae μ) := sorry

theorem ae_seq_eq_fun_ae {α : Type u_1} {β : Type u_2} {ι : Type u_4} [measurable_space α] [measurable_space β] {f : ι → α → β} {μ : measure_theory.measure α} {p : α → (ι → β) → Prop} [encodable ι] (hf : ∀ (i : ι), ae_measurable (f i)) (hp : filter.eventually (fun (x : α) => p x fun (n : ι) => f n x) (measure_theory.measure.ae μ)) : filter.eventually (fun (a : α) => ∀ (i : ι), ae_seq hf p i a = f i a) (measure_theory.measure.ae μ) :=
  measure_theory.measure_mono_null
    (fun (x : α) => mt fun (hx : x ∈ ae_seq_set hf p) (i : ι) => ae_seq_eq_fun_of_mem_ae_seq_set hf hx i)
    (measure_compl_ae_seq_set_eq_zero hf hp)

theorem ae_seq_n_eq_fun_n_ae {α : Type u_1} {β : Type u_2} {ι : Type u_4} [measurable_space α] [measurable_space β] {f : ι → α → β} {μ : measure_theory.measure α} {p : α → (ι → β) → Prop} [encodable ι] (hf : ∀ (i : ι), ae_measurable (f i)) (hp : filter.eventually (fun (x : α) => p x fun (n : ι) => f n x) (measure_theory.measure.ae μ)) (n : ι) : filter.eventually_eq (measure_theory.measure.ae μ) (ae_seq hf p n) (f n) :=
  iff.mp measure_theory.ae_all_iff (ae_seq_eq_fun_ae hf hp) n

theorem supr {α : Type u_1} {β : Type u_2} {ι : Type u_4} [measurable_space α] [measurable_space β] {f : ι → α → β} {μ : measure_theory.measure α} {p : α → (ι → β) → Prop} [complete_lattice β] [encodable ι] (hf : ∀ (i : ι), ae_measurable (f i)) (hp : filter.eventually (fun (x : α) => p x fun (n : ι) => f n x) (measure_theory.measure.ae μ)) : filter.eventually_eq (measure_theory.measure.ae μ) (supr fun (n : ι) => ae_seq hf p n) (supr fun (i : ι) => f i) := sorry


/-
Copyright (c) 2018 Kenny Lau. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kenny Lau, Yury Kudryashov
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.tactic.nth_rewrite.default
import Mathlib.data.matrix.basic
import Mathlib.data.equiv.ring_aut
import Mathlib.linear_algebra.tensor_product
import Mathlib.ring_theory.subring
import Mathlib.deprecated.subring
import Mathlib.algebra.opposites
import Mathlib.PostPort

universes u v l u_1 u_2 w u₁ v₁ u_3 u_4

namespace Mathlib

/-!
# Algebra over Commutative Semiring

In this file we define `algebra`s over commutative (semi)rings, algebra homomorphisms `alg_hom`,
algebra equivalences `alg_equiv`. We also define usual operations on `alg_hom`s
(`id`, `comp`).

`subalgebra`s are defined in `algebra.algebra.subalgebra`.

If `S` is an `R`-algebra and `A` is an `S`-algebra then `algebra.comap.algebra R S A` can be used
to provide `A` with a structure of an `R`-algebra. Other than that, `algebra.comap` is now
deprecated and replaced with `is_scalar_tower`.

For the category of `R`-algebras, denoted `Algebra R`, see the file
`algebra/category/Algebra/basic.lean`.

## Notations

* `A →ₐ[R] B` : `R`-algebra homomorphism from `A` to `B`.
* `A ≃ₐ[R] B` : `R`-algebra equivalence from `A` to `B`.
-/

-- We set this priority to 0 later in this file

/--
Given a commutative (semi)ring `R`, an `R`-algebra is a (possibly noncommutative)
(semi)ring `A` endowed with a morphism of rings `R →+* A` which lands in the
center of `A`.

For convenience, this typeclass extends `has_scalar R A` where the scalar action must
agree with left multiplication by the image of the structure morphism.

Given an `algebra R A` instance, the structure morphism `R →+* A` is denoted `algebra_map R A`.
-/
class algebra (R : Type u) (A : Type v) [comm_semiring R] [semiring A]
extends has_scalar R A, R →+* A
where
  commutes' : ∀ (r : R) (x : A), ring_hom.to_fun _to_ring_hom r * x = x * ring_hom.to_fun _to_ring_hom r
  smul_def' : ∀ (r : R) (x : A), r • x = ring_hom.to_fun _to_ring_hom r * x

/-- Embedding `R →+* A` given by `algebra` structure. -/
def algebra_map (R : Type u) (A : Type v) [comm_semiring R] [semiring A] [algebra R A] : R →+* A :=
  algebra.to_ring_hom

/-- Creating an algebra from a morphism to the center of a semiring. -/
def ring_hom.to_algebra' {R : Type u_1} {S : Type u_2} [comm_semiring R] [semiring S] (i : R →+* S) (h : ∀ (c : R) (x : S), coe_fn i c * x = x * coe_fn i c) : algebra R S :=
  algebra.mk i h sorry

/-- Creating an algebra from a morphism to a commutative semiring. -/
def ring_hom.to_algebra {R : Type u_1} {S : Type u_2} [comm_semiring R] [comm_semiring S] (i : R →+* S) : algebra R S :=
  ring_hom.to_algebra' i sorry

theorem ring_hom.algebra_map_to_algebra {R : Type u_1} {S : Type u_2} [comm_semiring R] [comm_semiring S] (i : R →+* S) : algebra_map R S = i :=
  rfl

namespace algebra

/-- Let `R` be a commutative semiring, let `A` be a semiring with a `semimodule R` structure.
If `(r • 1) * x = x * (r • 1) = r • x` for all `r : R` and `x : A`, then `A` is an `algebra`
over `R`. -/
def of_semimodule' {R : Type u} {A : Type w} [comm_semiring R] [semiring A] [semimodule R A] (h₁ : ∀ (r : R) (x : A), r • 1 * x = r • x) (h₂ : ∀ (r : R) (x : A), x * r • 1 = r • x) : algebra R A :=
  mk (ring_hom.mk (fun (r : R) => r • 1) sorry sorry sorry sorry) sorry sorry

/-- Let `R` be a commutative semiring, let `A` be a semiring with a `semimodule R` structure.
If `(r • x) * y = x * (r • y) = r • (x * y)` for all `r : R` and `x y : A`, then `A`
is an `algebra` over `R`. -/
def of_semimodule {R : Type u} {A : Type w} [comm_semiring R] [semiring A] [semimodule R A] (h₁ : ∀ (r : R) (x y : A), r • x * y = r • (x * y)) (h₂ : ∀ (r : R) (x y : A), x * r • y = r • (x * y)) : algebra R A :=
  of_semimodule' sorry sorry

theorem smul_def'' {R : Type u} {A : Type w} [comm_semiring R] [semiring A] [algebra R A] (r : R) (x : A) : r • x = coe_fn (algebra_map R A) r * x :=
  smul_def' r x

/--
To prove two algebra structures on a fixed `[comm_semiring R] [semiring A]` agree,
it suffices to check the `algebra_map`s agree.
-/
-- We'll later use this to show `algebra ℤ M` is a subsingleton.

theorem algebra_ext {R : Type u_1} [comm_semiring R] {A : Type u_2} [semiring A] (P : algebra R A) (Q : algebra R A) (w : ∀ (r : R), coe_fn (algebra_map R A) r = coe_fn (algebra_map R A) r) : P = Q := sorry

protected instance to_semimodule {R : Type u} {A : Type w} [comm_semiring R] [semiring A] [algebra R A] : semimodule R A :=
  semimodule.mk sorry sorry

-- from now on, we don't want to use the following instance anymore

theorem smul_def {R : Type u} {A : Type w} [comm_semiring R] [semiring A] [algebra R A] (r : R) (x : A) : r • x = coe_fn (algebra_map R A) r * x :=
  smul_def' r x

theorem algebra_map_eq_smul_one {R : Type u} {A : Type w} [comm_semiring R] [semiring A] [algebra R A] (r : R) : coe_fn (algebra_map R A) r = r • 1 :=
  Eq.trans (Eq.symm (mul_one (coe_fn (algebra_map R A) r))) (Eq.symm (smul_def r 1))

theorem commutes {R : Type u} {A : Type w} [comm_semiring R] [semiring A] [algebra R A] (r : R) (x : A) : coe_fn (algebra_map R A) r * x = x * coe_fn (algebra_map R A) r :=
  commutes' r x

theorem left_comm {R : Type u} {A : Type w} [comm_semiring R] [semiring A] [algebra R A] (r : R) (x : A) (y : A) : x * (coe_fn (algebra_map R A) r * y) = coe_fn (algebra_map R A) r * (x * y) := sorry

@[simp] theorem mul_smul_comm {R : Type u} {A : Type w} [comm_semiring R] [semiring A] [algebra R A] (s : R) (x : A) (y : A) : x * s • y = s • (x * y) := sorry

@[simp] theorem smul_mul_assoc {R : Type u} {A : Type w} [comm_semiring R] [semiring A] [algebra R A] (r : R) (x : A) (y : A) : r • x * y = r • (x * y) := sorry

@[simp] theorem bit0_smul_one {R : Type u} {A : Type w} [comm_semiring R] [semiring A] [algebra R A] {r : R} : bit0 r • 1 = r • bit0 1 := sorry

@[simp] theorem bit0_smul_bit0 {R : Type u} {A : Type w} [comm_semiring R] [semiring A] [algebra R A] {r : R} {a : A} : bit0 r • bit0 a = r • bit0 (bit0 a) := sorry

@[simp] theorem bit0_smul_bit1 {R : Type u} {A : Type w} [comm_semiring R] [semiring A] [algebra R A] {r : R} {a : A} : bit0 r • bit1 a = r • bit0 (bit1 a) := sorry

@[simp] theorem bit1_smul_one {R : Type u} {A : Type w} [comm_semiring R] [semiring A] [algebra R A] {r : R} : bit1 r • 1 = r • bit0 1 + 1 := sorry

@[simp] theorem bit1_smul_bit0 {R : Type u} {A : Type w} [comm_semiring R] [semiring A] [algebra R A] {r : R} {a : A} : bit1 r • bit0 a = r • bit0 (bit0 a) + bit0 a := sorry

@[simp] theorem bit1_smul_bit1 {R : Type u} {A : Type w} [comm_semiring R] [semiring A] [algebra R A] {r : R} {a : A} : bit1 r • bit1 a = r • bit0 (bit1 a) + bit1 a := sorry

/--
The canonical ring homomorphism `algebra_map R A : R →* A` for any `R`-algebra `A`,
packaged as an `R`-linear map.
-/
protected def linear_map (R : Type u) (A : Type w) [comm_semiring R] [semiring A] [algebra R A] : linear_map R R A :=
  linear_map.mk (ring_hom.to_fun (algebra_map R A)) sorry sorry

@[simp] theorem linear_map_apply (R : Type u) (A : Type w) [comm_semiring R] [semiring A] [algebra R A] (r : R) : coe_fn (algebra.linear_map R A) r = coe_fn (algebra_map R A) r :=
  rfl

protected instance id (R : Type u) [comm_semiring R] : algebra R R :=
  ring_hom.to_algebra (ring_hom.id R)

namespace id

@[simp] theorem map_eq_self {R : Type u} [comm_semiring R] (x : R) : coe_fn (algebra_map R R) x = x :=
  rfl

@[simp] theorem smul_eq_mul {R : Type u} [comm_semiring R] (x : R) (y : R) : x • y = x * y :=
  rfl

end id

protected instance prod.algebra (R : Type u) (A : Type w) (B : Type u_1) [comm_semiring R] [semiring A] [algebra R A] [semiring B] [algebra R B] : algebra R (A × B) :=
  mk (ring_hom.mk (ring_hom.to_fun (ring_hom.prod (algebra_map R A) (algebra_map R B))) sorry sorry sorry sorry) sorry
    sorry

@[simp] theorem algebra_map_prod_apply {R : Type u} {A : Type w} {B : Type u_1} [comm_semiring R] [semiring A] [algebra R A] [semiring B] [algebra R B] (r : R) : coe_fn (algebra_map R (A × B)) r = (coe_fn (algebra_map R A) r, coe_fn (algebra_map R B) r) :=
  rfl

/-- Algebra over a subsemiring. -/
protected instance of_subsemiring {R : Type u} {A : Type w} [comm_semiring R] [semiring A] [algebra R A] (S : subsemiring R) : algebra (↥S) A :=
  mk (ring_hom.mk (ring_hom.to_fun (ring_hom.comp (algebra_map R A) (subsemiring.subtype S))) sorry sorry sorry sorry)
    sorry sorry

/-- Algebra over a subring. -/
protected instance of_subring {R : Type u_1} {A : Type u_2} [comm_ring R] [ring A] [algebra R A] (S : subring R) : algebra (↥S) A :=
  mk (ring_hom.mk (ring_hom.to_fun (ring_hom.comp (algebra_map R A) (subring.subtype S))) sorry sorry sorry sorry) sorry
    sorry

theorem algebra_map_of_subring {R : Type u_1} [comm_ring R] (S : subring R) : algebra_map (↥S) R = subring.subtype S :=
  rfl

theorem coe_algebra_map_of_subring {R : Type u_1} [comm_ring R] (S : subring R) : ⇑(algebra_map (↥S) R) = subtype.val :=
  rfl

theorem algebra_map_of_subring_apply {R : Type u_1} [comm_ring R] (S : subring R) (x : ↥S) : coe_fn (algebra_map (↥S) R) x = ↑x :=
  rfl

/-- Algebra over a set that is closed under the ring operations. -/
def of_is_subring {R : Type u_1} {A : Type u_2} [comm_ring R] [ring A] [algebra R A] (S : set R) [is_subring S] : algebra (↥S) A :=
  algebra.of_subring (set.to_subring S)

theorem is_subring_coe_algebra_map_hom {R : Type u_1} [comm_ring R] (S : set R) [is_subring S] : algebra_map (↥S) R = is_subring.subtype S :=
  rfl

theorem is_subring_coe_algebra_map {R : Type u_1} [comm_ring R] (S : set R) [is_subring S] : ⇑(algebra_map (↥S) R) = subtype.val :=
  rfl

theorem is_subring_algebra_map_apply {R : Type u_1} [comm_ring R] (S : set R) [is_subring S] (x : ↥S) : coe_fn (algebra_map (↥S) R) x = ↑x :=
  rfl

theorem set_range_subset {R : Type u_1} [comm_ring R] {T₁ : set R} {T₂ : set R} [is_subring T₁] (hyp : T₁ ⊆ T₂) : set.range ⇑(algebra_map (↥T₁) R) ⊆ T₂ := sorry

/-- Explicit characterization of the submonoid map in the case of an algebra.
`S` is made explicit to help with type inference -/
def algebra_map_submonoid {R : Type u} [comm_semiring R] (S : Type u_1) [semiring S] [algebra R S] (M : submonoid R) : submonoid S :=
  submonoid.map (↑(algebra_map R S)) M

theorem mem_algebra_map_submonoid_of_mem {R : Type u} {S : Type v} [comm_semiring R] [comm_semiring S] [algebra R S] {M : submonoid R} (x : ↥M) : coe_fn (algebra_map R S) ↑x ∈ algebra_map_submonoid S M :=
  set.mem_image_of_mem (⇑(algebra_map R S)) (subtype.property x)

/-- A `semiring` that is an `algebra` over a commutative ring carries a natural `ring` structure. -/
def semiring_to_ring (R : Type u) {A : Type w} [comm_ring R] [semiring A] [algebra R A] : ring A :=
  ring.mk add_comm_group.add sorry add_comm_group.zero sorry sorry add_comm_group.neg add_comm_group.sub sorry sorry
    semiring.mul sorry semiring.one sorry sorry sorry sorry

theorem mul_sub_algebra_map_commutes {R : Type u} {A : Type w} [comm_ring R] [ring A] [algebra R A] (x : A) (r : R) : x * (x - coe_fn (algebra_map R A) r) = (x - coe_fn (algebra_map R A) r) * x := sorry

theorem mul_sub_algebra_map_pow_commutes {R : Type u} {A : Type w} [comm_ring R] [ring A] [algebra R A] (x : A) (r : R) (n : ℕ) : x * (x - coe_fn (algebra_map R A) r) ^ n = (x - coe_fn (algebra_map R A) r) ^ n * x := sorry

end algebra

namespace opposite

protected instance algebra {R : Type u_1} {A : Type u_2} [comm_semiring R] [semiring A] [algebra R A] : algebra R (Aᵒᵖ) :=
  algebra.mk (ring_hom.to_opposite (algebra_map R A) sorry) sorry sorry

@[simp] theorem algebra_map_apply {R : Type u_1} {A : Type u_2} [comm_semiring R] [semiring A] [algebra R A] (c : R) : coe_fn (algebra_map R (Aᵒᵖ)) c = op (coe_fn (algebra_map R A) c) :=
  rfl

end opposite

namespace module

protected instance endomorphism_algebra (R : Type u) (M : Type v) [comm_semiring R] [add_comm_monoid M] [semimodule R M] : algebra R (linear_map R M M) :=
  algebra.mk (ring_hom.mk (fun (r : R) => r • linear_map.id) sorry sorry sorry sorry) sorry sorry

theorem algebra_map_End_eq_smul_id (R : Type u) (M : Type v) [comm_semiring R] [add_comm_monoid M] [semimodule R M] (a : R) : coe_fn (algebra_map R (End R M)) a = a • linear_map.id :=
  rfl

@[simp] theorem algebra_map_End_apply (R : Type u) (M : Type v) [comm_semiring R] [add_comm_monoid M] [semimodule R M] (a : R) (m : M) : coe_fn (coe_fn (algebra_map R (End R M)) a) m = a • m :=
  rfl

@[simp] theorem ker_algebra_map_End (K : Type u) (V : Type v) [field K] [add_comm_group V] [vector_space K V] (a : K) (ha : a ≠ 0) : linear_map.ker (coe_fn (algebra_map K (End K V)) a) = ⊥ :=
  linear_map.ker_smul linear_map.id a ha

end module

protected instance matrix_algebra (n : Type u) (R : Type v) [DecidableEq n] [fintype n] [comm_semiring R] : algebra R (matrix n n R) :=
  algebra.mk (ring_hom.mk (ring_hom.to_fun (matrix.scalar n)) sorry sorry sorry sorry) sorry sorry

/-- Defining the homomorphism in the category R-Alg. -/
structure alg_hom (R : Type u) (A : Type v) (B : Type w) [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B]
extends A →+* B
where
  commutes' : ∀ (r : R), to_fun (coe_fn (algebra_map R A) r) = coe_fn (algebra_map R B) r

namespace alg_hom

protected instance has_coe_to_fun {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] : has_coe_to_fun (alg_hom R A B) :=
  has_coe_to_fun.mk (fun (f : alg_hom R A B) => A → B) fun (f : alg_hom R A B) => to_fun f

protected instance coe_ring_hom {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] : has_coe (alg_hom R A B) (A →+* B) :=
  has_coe.mk to_ring_hom

protected instance coe_monoid_hom {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] : has_coe (alg_hom R A B) (A →* B) :=
  has_coe.mk fun (f : alg_hom R A B) => ↑↑f

protected instance coe_add_monoid_hom {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] : has_coe (alg_hom R A B) (A →+ B) :=
  has_coe.mk fun (f : alg_hom R A B) => ↑↑f

@[simp] theorem coe_mk {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] {f : A → B} (h₁ : f 1 = 1) (h₂ : ∀ (x y : A), f (x * y) = f x * f y) (h₃ : f 0 = 0) (h₄ : ∀ (x y : A), f (x + y) = f x + f y) (h₅ : ∀ (r : R), f (coe_fn (algebra_map R A) r) = coe_fn (algebra_map R B) r) : ⇑(mk f h₁ h₂ h₃ h₄ h₅) = f :=
  rfl

@[simp] theorem coe_to_ring_hom {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] (f : alg_hom R A B) : ⇑↑f = ⇑f :=
  rfl

-- as `simp` can already prove this lemma, it is not tagged with the `simp` attribute.

theorem coe_to_monoid_hom {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] (f : alg_hom R A B) : ⇑↑f = ⇑f :=
  rfl

-- as `simp` can already prove this lemma, it is not tagged with the `simp` attribute.

theorem coe_to_add_monoid_hom {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] (f : alg_hom R A B) : ⇑↑f = ⇑f :=
  rfl

theorem coe_fn_inj {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] {φ₁ : alg_hom R A B} {φ₂ : alg_hom R A B} (H : ⇑φ₁ = ⇑φ₂) : φ₁ = φ₂ := sorry

theorem coe_ring_hom_injective {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] : function.injective coe :=
  fun (φ₁ φ₂ : alg_hom R A B) (H : ↑φ₁ = ↑φ₂) => coe_fn_inj ((fun (this : ⇑↑φ₁ = ⇑↑φ₂) => this) (congr_arg coe_fn H))

theorem coe_monoid_hom_injective {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] : function.injective coe :=
  function.injective.comp ring_hom.coe_monoid_hom_injective coe_ring_hom_injective

theorem coe_add_monoid_hom_injective {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] : function.injective coe :=
  function.injective.comp ring_hom.coe_add_monoid_hom_injective coe_ring_hom_injective

protected theorem congr_fun {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] {φ₁ : alg_hom R A B} {φ₂ : alg_hom R A B} (H : φ₁ = φ₂) (x : A) : coe_fn φ₁ x = coe_fn φ₂ x :=
  H ▸ rfl

protected theorem congr_arg {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] (φ : alg_hom R A B) {x : A} {y : A} (h : x = y) : coe_fn φ x = coe_fn φ y :=
  h ▸ rfl

theorem ext {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] {φ₁ : alg_hom R A B} {φ₂ : alg_hom R A B} (H : ∀ (x : A), coe_fn φ₁ x = coe_fn φ₂ x) : φ₁ = φ₂ :=
  coe_fn_inj (funext H)

theorem ext_iff {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] {φ₁ : alg_hom R A B} {φ₂ : alg_hom R A B} : φ₁ = φ₂ ↔ ∀ (x : A), coe_fn φ₁ x = coe_fn φ₂ x :=
  { mp := alg_hom.congr_fun, mpr := ext }

@[simp] theorem mk_coe {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] {f : alg_hom R A B} (h₁ : coe_fn f 1 = 1) (h₂ : ∀ (x y : A), coe_fn f (x * y) = coe_fn f x * coe_fn f y) (h₃ : coe_fn f 0 = 0) (h₄ : ∀ (x y : A), coe_fn f (x + y) = coe_fn f x + coe_fn f y) (h₅ : ∀ (r : R), coe_fn f (coe_fn (algebra_map R A) r) = coe_fn (algebra_map R B) r) : mk (⇑f) h₁ h₂ h₃ h₄ h₅ = f :=
  ext fun (_x : A) => rfl

@[simp] theorem commutes {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] (φ : alg_hom R A B) (r : R) : coe_fn φ (coe_fn (algebra_map R A) r) = coe_fn (algebra_map R B) r :=
  commutes' φ r

theorem comp_algebra_map {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] (φ : alg_hom R A B) : ring_hom.comp (↑φ) (algebra_map R A) = algebra_map R B :=
  ring_hom.ext (commutes φ)

@[simp] theorem map_add {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] (φ : alg_hom R A B) (r : A) (s : A) : coe_fn φ (r + s) = coe_fn φ r + coe_fn φ s :=
  ring_hom.map_add (to_ring_hom φ) r s

@[simp] theorem map_zero {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] (φ : alg_hom R A B) : coe_fn φ 0 = 0 :=
  ring_hom.map_zero (to_ring_hom φ)

@[simp] theorem map_mul {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] (φ : alg_hom R A B) (x : A) (y : A) : coe_fn φ (x * y) = coe_fn φ x * coe_fn φ y :=
  ring_hom.map_mul (to_ring_hom φ) x y

@[simp] theorem map_one {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] (φ : alg_hom R A B) : coe_fn φ 1 = 1 :=
  ring_hom.map_one (to_ring_hom φ)

@[simp] theorem map_smul {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] (φ : alg_hom R A B) (r : R) (x : A) : coe_fn φ (r • x) = r • coe_fn φ x := sorry

@[simp] theorem map_pow {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] (φ : alg_hom R A B) (x : A) (n : ℕ) : coe_fn φ (x ^ n) = coe_fn φ x ^ n :=
  ring_hom.map_pow (to_ring_hom φ) x n

theorem map_sum {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] (φ : alg_hom R A B) {ι : Type u_1} (f : ι → A) (s : finset ι) : coe_fn φ (finset.sum s fun (x : ι) => f x) = finset.sum s fun (x : ι) => coe_fn φ (f x) :=
  ring_hom.map_sum (to_ring_hom φ) f s

theorem map_finsupp_sum {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] (φ : alg_hom R A B) {α : Type u_1} [HasZero α] {ι : Type u_2} (f : ι →₀ α) (g : ι → α → A) : coe_fn φ (finsupp.sum f g) = finsupp.sum f fun (i : ι) (a : α) => coe_fn φ (g i a) :=
  map_sum φ (fun (a : ι) => g a (coe_fn f a)) (finsupp.support f)

@[simp] theorem map_nat_cast {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] (φ : alg_hom R A B) (n : ℕ) : coe_fn φ ↑n = ↑n :=
  ring_hom.map_nat_cast (to_ring_hom φ) n

@[simp] theorem map_bit0 {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] (φ : alg_hom R A B) (x : A) : coe_fn φ (bit0 x) = bit0 (coe_fn φ x) :=
  ring_hom.map_bit0 (to_ring_hom φ) x

@[simp] theorem map_bit1 {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] (φ : alg_hom R A B) (x : A) : coe_fn φ (bit1 x) = bit1 (coe_fn φ x) :=
  ring_hom.map_bit1 (to_ring_hom φ) x

/-- If a `ring_hom` is `R`-linear, then it is an `alg_hom`. -/
def mk' {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] (f : A →+* B) (h : ∀ (c : R) (x : A), coe_fn f (c • x) = c • coe_fn f x) : alg_hom R A B :=
  mk (⇑f) (ring_hom.map_one' f) (ring_hom.map_mul' f) (ring_hom.map_zero' f) (ring_hom.map_add' f) sorry

@[simp] theorem coe_mk' {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] (f : A →+* B) (h : ∀ (c : R) (x : A), coe_fn f (c • x) = c • coe_fn f x) : ⇑(mk' f h) = ⇑f :=
  rfl

/-- Identity map as an `alg_hom`. -/
protected def id (R : Type u) (A : Type v) [comm_semiring R] [semiring A] [algebra R A] : alg_hom R A A :=
  mk (ring_hom.to_fun (ring_hom.id A)) sorry sorry sorry sorry sorry

@[simp] theorem id_apply {R : Type u} {A : Type v} [comm_semiring R] [semiring A] [algebra R A] (p : A) : coe_fn (alg_hom.id R A) p = p :=
  rfl

/-- Composition of algebra homeomorphisms. -/
def comp {R : Type u} {A : Type v} {B : Type w} {C : Type u₁} [comm_semiring R] [semiring A] [semiring B] [semiring C] [algebra R A] [algebra R B] [algebra R C] (φ₁ : alg_hom R B C) (φ₂ : alg_hom R A B) : alg_hom R A C :=
  mk (ring_hom.to_fun (ring_hom.comp (to_ring_hom φ₁) ↑φ₂)) sorry sorry sorry sorry sorry

@[simp] theorem comp_apply {R : Type u} {A : Type v} {B : Type w} {C : Type u₁} [comm_semiring R] [semiring A] [semiring B] [semiring C] [algebra R A] [algebra R B] [algebra R C] (φ₁ : alg_hom R B C) (φ₂ : alg_hom R A B) (p : A) : coe_fn (comp φ₁ φ₂) p = coe_fn φ₁ (coe_fn φ₂ p) :=
  rfl

@[simp] theorem comp_id {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] (φ : alg_hom R A B) : comp φ (alg_hom.id R A) = φ :=
  ext fun (x : A) => rfl

@[simp] theorem id_comp {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] (φ : alg_hom R A B) : comp (alg_hom.id R B) φ = φ :=
  ext fun (x : A) => rfl

theorem comp_assoc {R : Type u} {A : Type v} {B : Type w} {C : Type u₁} {D : Type v₁} [comm_semiring R] [semiring A] [semiring B] [semiring C] [semiring D] [algebra R A] [algebra R B] [algebra R C] [algebra R D] (φ₁ : alg_hom R C D) (φ₂ : alg_hom R B C) (φ₃ : alg_hom R A B) : comp (comp φ₁ φ₂) φ₃ = comp φ₁ (comp φ₂ φ₃) :=
  ext fun (x : A) => rfl

/-- R-Alg ⥤ R-Mod -/
def to_linear_map {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] (φ : alg_hom R A B) : linear_map R A B :=
  linear_map.mk (⇑φ) (map_add φ) (map_smul φ)

@[simp] theorem to_linear_map_apply {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] (φ : alg_hom R A B) (p : A) : coe_fn (to_linear_map φ) p = coe_fn φ p :=
  rfl

theorem to_linear_map_inj {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B] {φ₁ : alg_hom R A B} {φ₂ : alg_hom R A B} (H : to_linear_map φ₁ = to_linear_map φ₂) : φ₁ = φ₂ := sorry

@[simp] theorem comp_to_linear_map {R : Type u} {A : Type v} {B : Type w} {C : Type u₁} [comm_semiring R] [semiring A] [semiring B] [semiring C] [algebra R A] [algebra R B] [algebra R C] (f : alg_hom R A B) (g : alg_hom R B C) : to_linear_map (comp g f) = linear_map.comp (to_linear_map g) (to_linear_map f) :=
  rfl

theorem map_prod {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [comm_semiring A] [comm_semiring B] [algebra R A] [algebra R B] (φ : alg_hom R A B) {ι : Type u_1} (f : ι → A) (s : finset ι) : coe_fn φ (finset.prod s fun (x : ι) => f x) = finset.prod s fun (x : ι) => coe_fn φ (f x) :=
  ring_hom.map_prod (to_ring_hom φ) f s

theorem map_finsupp_prod {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [comm_semiring A] [comm_semiring B] [algebra R A] [algebra R B] (φ : alg_hom R A B) {α : Type u_1} [HasZero α] {ι : Type u_2} (f : ι →₀ α) (g : ι → α → A) : coe_fn φ (finsupp.prod f g) = finsupp.prod f fun (i : ι) (a : α) => coe_fn φ (g i a) :=
  map_prod φ (fun (a : ι) => g a (coe_fn f a)) (finsupp.support f)

@[simp] theorem map_neg {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [ring A] [ring B] [algebra R A] [algebra R B] (φ : alg_hom R A B) (x : A) : coe_fn φ (-x) = -coe_fn φ x :=
  ring_hom.map_neg (to_ring_hom φ) x

@[simp] theorem map_sub {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [ring A] [ring B] [algebra R A] [algebra R B] (φ : alg_hom R A B) (x : A) (y : A) : coe_fn φ (x - y) = coe_fn φ x - coe_fn φ y :=
  ring_hom.map_sub (to_ring_hom φ) x y

@[simp] theorem map_int_cast {R : Type u} {A : Type v} {B : Type w} [comm_semiring R] [ring A] [ring B] [algebra R A] [algebra R B] (φ : alg_hom R A B) (n : ℤ) : coe_fn φ ↑n = ↑n :=
  ring_hom.map_int_cast (to_ring_hom φ) n

@[simp] theorem map_inv {R : Type u} {A : Type v} {B : Type w} [comm_ring R] [division_ring A] [division_ring B] [algebra R A] [algebra R B] (φ : alg_hom R A B) (x : A) : coe_fn φ (x⁻¹) = (coe_fn φ x⁻¹) :=
  ring_hom.map_inv (to_ring_hom φ) x

@[simp] theorem map_div {R : Type u} {A : Type v} {B : Type w} [comm_ring R] [division_ring A] [division_ring B] [algebra R A] [algebra R B] (φ : alg_hom R A B) (x : A) (y : A) : coe_fn φ (x / y) = coe_fn φ x / coe_fn φ y :=
  ring_hom.map_div (to_ring_hom φ) x y

theorem injective_iff {R : Type u_1} {A : Type u_2} {B : Type u_3} [comm_semiring R] [ring A] [semiring B] [algebra R A] [algebra R B] (f : alg_hom R A B) : function.injective ⇑f ↔ ∀ (x : A), coe_fn f x = 0 → x = 0 :=
  ring_hom.injective_iff ↑f

end alg_hom

/-- An equivalence of algebras is an equivalence of rings commuting with the actions of scalars. -/
structure alg_equiv (R : Type u) (A : Type v) (B : Type w) [comm_semiring R] [semiring A] [semiring B] [algebra R A] [algebra R B]
extends A ≃+ B, A ≃+* B, A ≃ B, A ≃* B
where
  commutes' : ∀ (r : R), to_fun (coe_fn (algebra_map R A) r) = coe_fn (algebra_map R B) r

namespace alg_equiv

protected instance has_coe_to_fun {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] : has_coe_to_fun (alg_equiv R A₁ A₂) :=
  has_coe_to_fun.mk (fun (x : alg_equiv R A₁ A₂) => A₁ → A₂) to_fun

theorem ext {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] {f : alg_equiv R A₁ A₂} {g : alg_equiv R A₁ A₂} (h : ∀ (a : A₁), coe_fn f a = coe_fn g a) : f = g := sorry

protected theorem congr_arg {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] {f : alg_equiv R A₁ A₂} {x : A₁} {x' : A₁} : x = x' → coe_fn f x = coe_fn f x' := sorry

protected theorem congr_fun {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] {f : alg_equiv R A₁ A₂} {g : alg_equiv R A₁ A₂} (h : f = g) (x : A₁) : coe_fn f x = coe_fn g x :=
  h ▸ rfl

theorem ext_iff {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] {f : alg_equiv R A₁ A₂} {g : alg_equiv R A₁ A₂} : f = g ↔ ∀ (x : A₁), coe_fn f x = coe_fn g x :=
  { mp := fun (h : f = g) (x : A₁) => h ▸ rfl, mpr := ext }

theorem coe_fun_injective {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] : function.injective fun (e : alg_equiv R A₁ A₂) => ⇑e :=
  id
    fun (f g : alg_equiv R A₁ A₂) (w : (fun (e : alg_equiv R A₁ A₂) => ⇑e) f = (fun (e : alg_equiv R A₁ A₂) => ⇑e) g) =>
      ext fun (a : A₁) => congr_fun w a

protected instance has_coe_to_ring_equiv {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] : has_coe (alg_equiv R A₁ A₂) (A₁ ≃+* A₂) :=
  has_coe.mk to_ring_equiv

@[simp] theorem mk_apply {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] {to_fun : A₁ → A₂} {inv_fun : A₂ → A₁} {left_inv : function.left_inverse inv_fun to_fun} {right_inv : function.right_inverse inv_fun to_fun} {map_mul : ∀ (x y : A₁), to_fun (x * y) = to_fun x * to_fun y} {map_add : ∀ (x y : A₁), to_fun (x + y) = to_fun x + to_fun y} {commutes : ∀ (r : R), to_fun (coe_fn (algebra_map R A₁) r) = coe_fn (algebra_map R A₂) r} {a : A₁} : coe_fn (mk to_fun inv_fun left_inv right_inv map_mul map_add commutes) a = to_fun a :=
  rfl

@[simp] theorem to_fun_apply {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] {e : alg_equiv R A₁ A₂} {a : A₁} : to_fun e a = coe_fn e a :=
  rfl

@[simp] theorem coe_ring_equiv {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) : ⇑↑e = ⇑e :=
  rfl

theorem coe_ring_equiv_injective {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] : function.injective fun (e : alg_equiv R A₁ A₂) => ↑e :=
  id
    fun (f g : alg_equiv R A₁ A₂) (w : (fun (e : alg_equiv R A₁ A₂) => ↑e) f = (fun (e : alg_equiv R A₁ A₂) => ↑e) g) =>
      ext fun (a : A₁) => congr_fun (congr_arg (fun (e : A₁ ≃+* A₂) => ⇑e) w) a

@[simp] theorem map_add {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) (x : A₁) (y : A₁) : coe_fn e (x + y) = coe_fn e x + coe_fn e y :=
  add_equiv.map_add (to_add_equiv e)

@[simp] theorem map_zero {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) : coe_fn e 0 = 0 :=
  add_equiv.map_zero (to_add_equiv e)

@[simp] theorem map_mul {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) (x : A₁) (y : A₁) : coe_fn e (x * y) = coe_fn e x * coe_fn e y :=
  mul_equiv.map_mul (to_mul_equiv e)

@[simp] theorem map_one {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) : coe_fn e 1 = 1 :=
  mul_equiv.map_one (to_mul_equiv e)

@[simp] theorem commutes {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) (r : R) : coe_fn e (coe_fn (algebra_map R A₁) r) = coe_fn (algebra_map R A₂) r :=
  commutes' e

theorem map_sum {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) {ι : Type u_1} (f : ι → A₁) (s : finset ι) : coe_fn e (finset.sum s fun (x : ι) => f x) = finset.sum s fun (x : ι) => coe_fn e (f x) :=
  add_equiv.map_sum (to_add_equiv e) f s

theorem map_finsupp_sum {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) {α : Type u_1} [HasZero α] {ι : Type u_2} (f : ι →₀ α) (g : ι → α → A₁) : coe_fn e (finsupp.sum f g) = finsupp.sum f fun (i : ι) (b : α) => coe_fn e (g i b) :=
  map_sum e (fun (a : ι) => g a (coe_fn f a)) (finsupp.support f)

/-- Interpret an algebra equivalence as an algebra homomorphism.

This definition is included for symmetry with the other `to_*_hom` projections.
The `simp` normal form is to use the coercion of the `has_coe_to_alg_hom` instance. -/
def to_alg_hom {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) : alg_hom R A₁ A₂ :=
  alg_hom.mk (to_fun e) (map_one e) (map_mul' e) (map_zero e) (map_add' e) (commutes' e)

protected instance has_coe_to_alg_hom {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] : has_coe (alg_equiv R A₁ A₂) (alg_hom R A₁ A₂) :=
  has_coe.mk to_alg_hom

@[simp] theorem to_alg_hom_eq_coe {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) : to_alg_hom e = ↑e :=
  rfl

@[simp] theorem coe_alg_hom {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) : ⇑↑e = ⇑e :=
  rfl

@[simp] theorem map_pow {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) (x : A₁) (n : ℕ) : coe_fn e (x ^ n) = coe_fn e x ^ n :=
  alg_hom.map_pow (to_alg_hom e)

theorem injective {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) : function.injective ⇑e :=
  equiv.injective (to_equiv e)

theorem surjective {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) : function.surjective ⇑e :=
  equiv.surjective (to_equiv e)

theorem bijective {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) : function.bijective ⇑e :=
  equiv.bijective (to_equiv e)

protected instance has_one {R : Type u} {A₁ : Type v} [comm_semiring R] [semiring A₁] [algebra R A₁] : HasOne (alg_equiv R A₁ A₁) :=
  { one := mk (ring_equiv.to_fun 1) (ring_equiv.inv_fun 1) sorry sorry sorry sorry sorry }

protected instance inhabited {R : Type u} {A₁ : Type v} [comm_semiring R] [semiring A₁] [algebra R A₁] : Inhabited (alg_equiv R A₁ A₁) :=
  { default := 1 }

/-- Algebra equivalences are reflexive. -/
def refl {R : Type u} {A₁ : Type v} [comm_semiring R] [semiring A₁] [algebra R A₁] : alg_equiv R A₁ A₁ :=
  1

@[simp] theorem coe_refl {R : Type u} {A₁ : Type v} [comm_semiring R] [semiring A₁] [algebra R A₁] : ↑refl = alg_hom.id R A₁ :=
  alg_hom.ext fun (x : A₁) => rfl

/-- Algebra equivalences are symmetric. -/
def symm {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) : alg_equiv R A₂ A₁ :=
  mk (ring_equiv.to_fun (ring_equiv.symm (to_ring_equiv e))) (ring_equiv.inv_fun (ring_equiv.symm (to_ring_equiv e)))
    sorry sorry sorry sorry sorry

/-- See Note [custom simps projection] -/
def simps.inv_fun {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) : A₂ → A₁ :=
  ⇑(symm e)

@[simp] theorem inv_fun_eq_symm {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] {e : alg_equiv R A₁ A₂} : inv_fun e = ⇑(symm e) :=
  rfl

@[simp] theorem symm_symm {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] {e : alg_equiv R A₁ A₂} : symm (symm e) = e :=
  ext fun (a : A₁) => Eq.refl (coe_fn (symm (symm e)) a)

/-- Algebra equivalences are transitive. -/
def trans {R : Type u} {A₁ : Type v} {A₂ : Type w} {A₃ : Type u₁} [comm_semiring R] [semiring A₁] [semiring A₂] [semiring A₃] [algebra R A₁] [algebra R A₂] [algebra R A₃] (e₁ : alg_equiv R A₁ A₂) (e₂ : alg_equiv R A₂ A₃) : alg_equiv R A₁ A₃ :=
  mk (ring_equiv.to_fun (ring_equiv.trans (to_ring_equiv e₁) (to_ring_equiv e₂)))
    (ring_equiv.inv_fun (ring_equiv.trans (to_ring_equiv e₁) (to_ring_equiv e₂))) sorry sorry sorry sorry sorry

@[simp] theorem apply_symm_apply {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) (x : A₂) : coe_fn e (coe_fn (symm e) x) = x :=
  equiv.apply_symm_apply (to_equiv e)

@[simp] theorem symm_apply_apply {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) (x : A₁) : coe_fn (symm e) (coe_fn e x) = x :=
  equiv.symm_apply_apply (to_equiv e)

@[simp] theorem trans_apply {R : Type u} {A₁ : Type v} {A₂ : Type w} {A₃ : Type u₁} [comm_semiring R] [semiring A₁] [semiring A₂] [semiring A₃] [algebra R A₁] [algebra R A₂] [algebra R A₃] (e₁ : alg_equiv R A₁ A₂) (e₂ : alg_equiv R A₂ A₃) (x : A₁) : coe_fn (trans e₁ e₂) x = coe_fn e₂ (coe_fn e₁ x) :=
  rfl

@[simp] theorem comp_symm {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) : alg_hom.comp ↑e ↑(symm e) = alg_hom.id R A₂ := sorry

@[simp] theorem symm_comp {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) : alg_hom.comp ↑(symm e) ↑e = alg_hom.id R A₁ := sorry

/-- If `A₁` is equivalent to `A₁'` and `A₂` is equivalent to `A₂'`, then the type of maps
`A₁ →ₐ[R] A₂` is equivalent to the type of maps `A₁' →ₐ[R] A₂'`. -/
def arrow_congr {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] {A₁' : Type u_1} {A₂' : Type u_2} [semiring A₁'] [semiring A₂'] [algebra R A₁'] [algebra R A₂'] (e₁ : alg_equiv R A₁ A₁') (e₂ : alg_equiv R A₂ A₂') : alg_hom R A₁ A₂ ≃ alg_hom R A₁' A₂' :=
  equiv.mk (fun (f : alg_hom R A₁ A₂) => alg_hom.comp (alg_hom.comp (to_alg_hom e₂) f) (to_alg_hom (symm e₁)))
    (fun (f : alg_hom R A₁' A₂') => alg_hom.comp (alg_hom.comp (to_alg_hom (symm e₂)) f) (to_alg_hom e₁)) sorry sorry

theorem arrow_congr_comp {R : Type u} {A₁ : Type v} {A₂ : Type w} {A₃ : Type u₁} [comm_semiring R] [semiring A₁] [semiring A₂] [semiring A₃] [algebra R A₁] [algebra R A₂] [algebra R A₃] {A₁' : Type u_1} {A₂' : Type u_2} {A₃' : Type u_3} [semiring A₁'] [semiring A₂'] [semiring A₃'] [algebra R A₁'] [algebra R A₂'] [algebra R A₃'] (e₁ : alg_equiv R A₁ A₁') (e₂ : alg_equiv R A₂ A₂') (e₃ : alg_equiv R A₃ A₃') (f : alg_hom R A₁ A₂) (g : alg_hom R A₂ A₃) : coe_fn (arrow_congr e₁ e₃) (alg_hom.comp g f) =
  alg_hom.comp (coe_fn (arrow_congr e₂ e₃) g) (coe_fn (arrow_congr e₁ e₂) f) := sorry

@[simp] theorem arrow_congr_refl {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] : arrow_congr refl refl = equiv.refl (alg_hom R A₁ A₂) :=
  equiv.ext
    fun (x : alg_hom R A₁ A₂) => alg_hom.ext fun (x_1 : A₁) => Eq.refl (coe_fn (coe_fn (arrow_congr refl refl) x) x_1)

@[simp] theorem arrow_congr_trans {R : Type u} {A₁ : Type v} {A₂ : Type w} {A₃ : Type u₁} [comm_semiring R] [semiring A₁] [semiring A₂] [semiring A₃] [algebra R A₁] [algebra R A₂] [algebra R A₃] {A₁' : Type u_1} {A₂' : Type u_2} {A₃' : Type u_3} [semiring A₁'] [semiring A₂'] [semiring A₃'] [algebra R A₁'] [algebra R A₂'] [algebra R A₃'] (e₁ : alg_equiv R A₁ A₂) (e₁' : alg_equiv R A₁' A₂') (e₂ : alg_equiv R A₂ A₃) (e₂' : alg_equiv R A₂' A₃') : arrow_congr (trans e₁ e₂) (trans e₁' e₂') = equiv.trans (arrow_congr e₁ e₁') (arrow_congr e₂ e₂') :=
  equiv.ext
    fun (x : alg_hom R A₁ A₁') =>
      alg_hom.ext fun (x_1 : A₃) => Eq.refl (coe_fn (coe_fn (arrow_congr (trans e₁ e₂) (trans e₁' e₂')) x) x_1)

@[simp] theorem arrow_congr_symm {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] {A₁' : Type u_1} {A₂' : Type u_2} [semiring A₁'] [semiring A₂'] [algebra R A₁'] [algebra R A₂'] (e₁ : alg_equiv R A₁ A₁') (e₂ : alg_equiv R A₂ A₂') : equiv.symm (arrow_congr e₁ e₂) = arrow_congr (symm e₁) (symm e₂) :=
  equiv.ext
    fun (x : alg_hom R A₁' A₂') =>
      alg_hom.ext fun (x_1 : A₁) => Eq.refl (coe_fn (coe_fn (equiv.symm (arrow_congr e₁ e₂)) x) x_1)

/-- If an algebra morphism has an inverse, it is a algebra isomorphism. -/
def of_alg_hom {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (f : alg_hom R A₁ A₂) (g : alg_hom R A₂ A₁) (h₁ : alg_hom.comp f g = alg_hom.id R A₂) (h₂ : alg_hom.comp g f = alg_hom.id R A₁) : alg_equiv R A₁ A₂ :=
  mk (alg_hom.to_fun f) ⇑g sorry sorry (alg_hom.map_mul' f) (alg_hom.map_add' f) (alg_hom.commutes' f)

/-- Promotes a bijective algebra homomorphism to an algebra equivalence. -/
def of_bijective {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (f : alg_hom R A₁ A₂) (hf : function.bijective ⇑f) : alg_equiv R A₁ A₂ :=
  mk (ring_equiv.to_fun (ring_equiv.of_bijective (↑f) hf)) (ring_equiv.inv_fun (ring_equiv.of_bijective (↑f) hf)) sorry
    sorry sorry sorry (alg_hom.commutes' f)

/-- Forgetting the multiplicative structures, an equivalence of algebras is a linear equivalence. -/
def to_linear_equiv {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) : linear_equiv R A₁ A₂ :=
  linear_equiv.mk (to_fun e) sorry sorry (to_fun (symm e)) (left_inv e) (right_inv e)

@[simp] theorem to_linear_equiv_apply {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) (x : A₁) : coe_fn (to_linear_equiv e) x = coe_fn e x :=
  rfl

theorem to_linear_equiv_inj {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] {e₁ : alg_equiv R A₁ A₂} {e₂ : alg_equiv R A₁ A₂} (H : to_linear_equiv e₁ = to_linear_equiv e₂) : e₁ = e₂ := sorry

/-- Interpret an algebra equivalence as a linear map. -/
def to_linear_map {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) : linear_map R A₁ A₂ :=
  alg_hom.to_linear_map (to_alg_hom e)

@[simp] theorem to_alg_hom_to_linear_map {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) : alg_hom.to_linear_map ↑e = to_linear_map e :=
  rfl

@[simp] theorem to_linear_equiv_to_linear_map {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) : linear_equiv.to_linear_map (to_linear_equiv e) = to_linear_map e :=
  rfl

@[simp] theorem to_linear_map_apply {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) (x : A₁) : coe_fn (to_linear_map e) x = coe_fn e x :=
  rfl

theorem to_linear_map_inj {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [semiring A₁] [semiring A₂] [algebra R A₁] [algebra R A₂] {e₁ : alg_equiv R A₁ A₂} {e₂ : alg_equiv R A₁ A₂} (H : to_linear_map e₁ = to_linear_map e₂) : e₁ = e₂ := sorry

@[simp] theorem trans_to_linear_map {R : Type u} {A₁ : Type v} {A₂ : Type w} {A₃ : Type u₁} [comm_semiring R] [semiring A₁] [semiring A₂] [semiring A₃] [algebra R A₁] [algebra R A₂] [algebra R A₃] (f : alg_equiv R A₁ A₂) (g : alg_equiv R A₂ A₃) : to_linear_map (trans f g) = linear_map.comp (to_linear_map g) (to_linear_map f) :=
  rfl

protected instance aut {R : Type u} {A₁ : Type v} [comm_semiring R] [semiring A₁] [algebra R A₁] : group (alg_equiv R A₁ A₁) :=
  group.mk (fun (ϕ ψ : alg_equiv R A₁ A₁) => trans ψ ϕ) sorry 1 sorry sorry symm
    (div_inv_monoid.div._default (fun (ϕ ψ : alg_equiv R A₁ A₁) => trans ψ ϕ) sorry 1 sorry sorry symm) sorry

theorem map_prod {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [comm_semiring A₁] [comm_semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) {ι : Type u_1} (f : ι → A₁) (s : finset ι) : coe_fn e (finset.prod s fun (x : ι) => f x) = finset.prod s fun (x : ι) => coe_fn e (f x) :=
  alg_hom.map_prod (to_alg_hom e) f s

theorem map_finsupp_prod {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_semiring R] [comm_semiring A₁] [comm_semiring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) {α : Type u_1} [HasZero α] {ι : Type u_2} (f : ι →₀ α) (g : ι → α → A₁) : coe_fn e (finsupp.prod f g) = finsupp.prod f fun (i : ι) (a : α) => coe_fn e (g i a) :=
  alg_hom.map_finsupp_prod (to_alg_hom e) f g

@[simp] theorem map_neg {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_ring R] [ring A₁] [ring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) (x : A₁) : coe_fn e (-x) = -coe_fn e x :=
  alg_hom.map_neg (to_alg_hom e) x

@[simp] theorem map_sub {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_ring R] [ring A₁] [ring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) (x : A₁) (y : A₁) : coe_fn e (x - y) = coe_fn e x - coe_fn e y :=
  alg_hom.map_sub (to_alg_hom e) x y

@[simp] theorem map_inv {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_ring R] [division_ring A₁] [division_ring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) (x : A₁) : coe_fn e (x⁻¹) = (coe_fn e x⁻¹) :=
  alg_hom.map_inv (to_alg_hom e) x

@[simp] theorem map_div {R : Type u} {A₁ : Type v} {A₂ : Type w} [comm_ring R] [division_ring A₁] [division_ring A₂] [algebra R A₁] [algebra R A₂] (e : alg_equiv R A₁ A₂) (x : A₁) (y : A₁) : coe_fn e (x / y) = coe_fn e x / coe_fn e y :=
  alg_hom.map_div (to_alg_hom e) x y

end alg_equiv

namespace matrix

/-! ### `matrix` section

Specialize `matrix.one_map` and `matrix.zero_map` to `alg_hom` and `alg_equiv`.
TODO: there should be a way to avoid restating these for each `foo_hom`.
-/

/-- A version of `matrix.one_map` where `f` is an `alg_hom`. -/
@[simp] theorem alg_hom_map_one {R : Type u_1} {A₁ : Type u_2} {A₂ : Type u_3} {n : Type u_4} [fintype n] [comm_semiring R] [semiring A₁] [algebra R A₁] [semiring A₂] [algebra R A₂] [DecidableEq n] (f : alg_hom R A₁ A₂) : map 1 ⇑f = 1 :=
  one_map (alg_hom.map_zero f) (alg_hom.map_one f)

/-- A version of `matrix.one_map` where `f` is an `alg_equiv`. -/
@[simp] theorem alg_equiv_map_one {R : Type u_1} {A₁ : Type u_2} {A₂ : Type u_3} {n : Type u_4} [fintype n] [comm_semiring R] [semiring A₁] [algebra R A₁] [semiring A₂] [algebra R A₂] [DecidableEq n] (f : alg_equiv R A₁ A₂) : map 1 ⇑f = 1 :=
  one_map (alg_equiv.map_zero f) (alg_equiv.map_one f)

/-- A version of `matrix.zero_map` where `f` is an `alg_hom`. -/
@[simp] theorem alg_hom_map_zero {R : Type u_1} {A₁ : Type u_2} {A₂ : Type u_3} {n : Type u_4} [fintype n] [comm_semiring R] [semiring A₁] [algebra R A₁] [semiring A₂] [algebra R A₂] (f : alg_hom R A₁ A₂) : map 0 ⇑f = 0 :=
  map_zero (alg_hom.map_zero f)

/-- A version of `matrix.zero_map` where `f` is an `alg_equiv`. -/
@[simp] theorem alg_equiv_map_zero {R : Type u_1} {A₁ : Type u_2} {A₂ : Type u_3} {n : Type u_4} [fintype n] [comm_semiring R] [semiring A₁] [algebra R A₁] [semiring A₂] [algebra R A₂] (f : alg_equiv R A₁ A₂) : map 0 ⇑f = 0 :=
  map_zero (alg_equiv.map_zero f)

end matrix

namespace algebra

/-- `comap R S A` is a type alias for `A`, and has an R-algebra structure defined on it
  when `algebra R S` and `algebra S A`. If `S` is an `R`-algebra and `A` is an `S`-algebra then
  `algebra.comap.algebra R S A` can be used to provide `A` with a structure of an `R`-algebra.
  Other than that, `algebra.comap` is now deprecated and replaced with `is_scalar_tower`. -/
/- This is done to avoid a type class search with meta-variables `algebra R ?m_1` and
    `algebra ?m_1 A -/

/- The `nolint` attribute is added because it has unused arguments `R` and `S`, but these are
  necessary for synthesizing the appropriate type classes -/

def comap (R : Type u) (S : Type v) (A : Type w) :=
  A

protected instance comap.inhabited (R : Type u) (S : Type v) (A : Type w) [h : Inhabited A] : Inhabited (comap R S A) :=
  h

protected instance comap.semiring (R : Type u) (S : Type v) (A : Type w) [h : semiring A] : semiring (comap R S A) :=
  h

protected instance comap.ring (R : Type u) (S : Type v) (A : Type w) [h : ring A] : ring (comap R S A) :=
  h

protected instance comap.comm_semiring (R : Type u) (S : Type v) (A : Type w) [h : comm_semiring A] : comm_semiring (comap R S A) :=
  h

protected instance comap.comm_ring (R : Type u) (S : Type v) (A : Type w) [h : comm_ring A] : comm_ring (comap R S A) :=
  h

protected instance comap.algebra' (R : Type u) (S : Type v) (A : Type w) [comm_semiring S] [semiring A] [h : algebra S A] : algebra S (comap R S A) :=
  h

/-- Identity homomorphism `A →ₐ[S] comap R S A`. -/
def comap.to_comap (R : Type u) (S : Type v) (A : Type w) [comm_semiring S] [semiring A] [algebra S A] : alg_hom S A (comap R S A) :=
  alg_hom.id S A

/-- Identity homomorphism `comap R S A →ₐ[S] A`. -/
def comap.of_comap (R : Type u) (S : Type v) (A : Type w) [comm_semiring S] [semiring A] [algebra S A] : alg_hom S (comap R S A) A :=
  alg_hom.id S A

/-- `R ⟶ S` induces `S-Alg ⥤ R-Alg` -/
protected instance comap.algebra (R : Type u) (S : Type v) (A : Type w) [comm_semiring R] [comm_semiring S] [semiring A] [algebra R S] [algebra S A] : algebra R (comap R S A) :=
  mk (ring_hom.mk (ring_hom.to_fun (ring_hom.comp (algebra_map S A) (algebra_map R S))) sorry sorry sorry sorry) sorry
    sorry

/-- Embedding of `S` into `comap R S A`. -/
def to_comap (R : Type u) (S : Type v) (A : Type w) [comm_semiring R] [comm_semiring S] [semiring A] [algebra R S] [algebra S A] : alg_hom R S (comap R S A) :=
  alg_hom.mk (ring_hom.to_fun (algebra_map S A)) sorry sorry sorry sorry sorry

theorem to_comap_apply (R : Type u) (S : Type v) (A : Type w) [comm_semiring R] [comm_semiring S] [semiring A] [algebra R S] [algebra S A] (x : S) : coe_fn (to_comap R S A) x = coe_fn (algebra_map S A) x :=
  rfl

end algebra

namespace alg_hom

/-- R ⟶ S induces S-Alg ⥤ R-Alg -/
def comap {R : Type u} {S : Type v} {A : Type w} {B : Type u₁} [comm_semiring R] [comm_semiring S] [semiring A] [semiring B] [algebra R S] [algebra S A] [algebra S B] (φ : alg_hom S A B) : alg_hom R (algebra.comap R S A) (algebra.comap R S B) :=
  mk (to_fun φ) (map_one' φ) (map_mul' φ) (map_zero' φ) (map_add' φ) sorry

end alg_hom

namespace ring_hom

/-- Reinterpret a `ring_hom` as an `ℕ`-algebra homomorphism. -/
def to_nat_alg_hom {R : Type u_1} {S : Type u_2} [semiring R] [semiring S] [algebra ℕ R] [algebra ℕ S] (f : R →+* S) : alg_hom ℕ R S :=
  alg_hom.mk (⇑f) (map_one' f) (map_mul' f) (map_zero' f) (map_add' f) sorry

/-- Reinterpret a `ring_hom` as a `ℤ`-algebra homomorphism. -/
def to_int_alg_hom {R : Type u_1} {S : Type u_2} [ring R] [ring S] [algebra ℤ R] [algebra ℤ S] (f : R →+* S) : alg_hom ℤ R S :=
  alg_hom.mk (to_fun f) sorry sorry sorry sorry sorry

@[simp] theorem map_rat_algebra_map {R : Type u_1} {S : Type u_2} [ring R] [ring S] [algebra ℚ R] [algebra ℚ S] (f : R →+* S) (r : ℚ) : coe_fn f (coe_fn (algebra_map ℚ R) r) = coe_fn (algebra_map ℚ S) r :=
  iff.mp ext_iff (subsingleton.elim (comp f (algebra_map ℚ R)) (algebra_map ℚ S)) r

/-- Reinterpret a `ring_hom` as a `ℚ`-algebra homomorphism. -/
def to_rat_alg_hom {R : Type u_1} {S : Type u_2} [ring R] [ring S] [algebra ℚ R] [algebra ℚ S] (f : R →+* S) : alg_hom ℚ R S :=
  alg_hom.mk (to_fun f) sorry sorry sorry sorry (map_rat_algebra_map f)

end ring_hom

namespace rat

protected instance algebra_rat {α : Type u_1} [division_ring α] [char_zero α] : algebra ℚ α :=
  ring_hom.to_algebra' (cast_hom α) sorry

end rat

namespace algebra

/-- `algebra_map` as an `alg_hom`. -/
def of_id (R : Type u) (A : Type v) [comm_semiring R] [semiring A] [algebra R A] : alg_hom R R A :=
  alg_hom.mk (ring_hom.to_fun (algebra_map R A)) sorry sorry sorry sorry sorry

theorem of_id_apply {R : Type u} (A : Type v) [comm_semiring R] [semiring A] [algebra R A] (r : R) : coe_fn (of_id R A) r = coe_fn (algebra_map R A) r :=
  rfl

/-- The multiplication in an algebra is a bilinear map. -/
def lmul (R : Type u) (A : Type v) [comm_semiring R] [semiring A] [algebra R A] : alg_hom R A (module.End R A) :=
  alg_hom.mk
    (linear_map.to_fun
      ((fun (this : linear_map R A (linear_map R A A)) => this) (linear_map.mk₂ R Mul.mul sorry sorry sorry sorry)))
    sorry sorry sorry sorry sorry

/-- The multiplication on the left in an algebra is a linear map. -/
def lmul_left (R : Type u) {A : Type v} [comm_semiring R] [semiring A] [algebra R A] (r : A) : linear_map R A A :=
  coe_fn (lmul R A) r

/-- The multiplication on the right in an algebra is a linear map. -/
def lmul_right (R : Type u) {A : Type v} [comm_semiring R] [semiring A] [algebra R A] (r : A) : linear_map R A A :=
  coe_fn (linear_map.flip (alg_hom.to_linear_map (lmul R A))) r

/-- Simultaneous multiplication on the left and right is a linear map. -/
def lmul_left_right (R : Type u) {A : Type v} [comm_semiring R] [semiring A] [algebra R A] (vw : A × A) : linear_map R A A :=
  linear_map.comp (lmul_right R (prod.snd vw)) (lmul_left R (prod.fst vw))

/-- The multiplication map on an algebra, as an `R`-linear map from `A ⊗[R] A` to `A`. -/
def lmul' (R : Type u) {A : Type v} [comm_semiring R] [semiring A] [algebra R A] : linear_map R (tensor_product R A A) A :=
  tensor_product.lift (alg_hom.to_linear_map (lmul R A))

@[simp] theorem lmul_apply {R : Type u} {A : Type v} [comm_semiring R] [semiring A] [algebra R A] (p : A) (q : A) : coe_fn (coe_fn (lmul R A) p) q = p * q :=
  rfl

@[simp] theorem lmul_left_apply {R : Type u} {A : Type v} [comm_semiring R] [semiring A] [algebra R A] (p : A) (q : A) : coe_fn (lmul_left R p) q = p * q :=
  rfl

@[simp] theorem lmul_right_apply {R : Type u} {A : Type v} [comm_semiring R] [semiring A] [algebra R A] (p : A) (q : A) : coe_fn (lmul_right R p) q = q * p :=
  rfl

@[simp] theorem lmul_left_right_apply {R : Type u} {A : Type v} [comm_semiring R] [semiring A] [algebra R A] (vw : A × A) (p : A) : coe_fn (lmul_left_right R vw) p = prod.fst vw * p * prod.snd vw :=
  rfl

@[simp] theorem lmul_left_one {R : Type u} {A : Type v} [comm_semiring R] [semiring A] [algebra R A] : lmul_left R 1 = linear_map.id := sorry

@[simp] theorem lmul_left_mul {R : Type u} {A : Type v} [comm_semiring R] [semiring A] [algebra R A] (a : A) (b : A) : lmul_left R (a * b) = linear_map.comp (lmul_left R a) (lmul_left R b) := sorry

@[simp] theorem lmul_right_one {R : Type u} {A : Type v} [comm_semiring R] [semiring A] [algebra R A] : lmul_right R 1 = linear_map.id := sorry

@[simp] theorem lmul_right_mul {R : Type u} {A : Type v} [comm_semiring R] [semiring A] [algebra R A] (a : A) (b : A) : lmul_right R (a * b) = linear_map.comp (lmul_right R b) (lmul_right R a) := sorry

@[simp] theorem lmul'_apply {R : Type u} {A : Type v} [comm_semiring R] [semiring A] [algebra R A] {x : A} {y : A} : coe_fn (lmul' R) (tensor_product.tmul R x y) = x * y := sorry

protected instance linear_map.semimodule' (R : Type u) [comm_semiring R] (M : Type v) [add_comm_monoid M] [semimodule R M] (S : Type w) [comm_semiring S] [algebra R S] : semimodule S (linear_map R M S) :=
  semimodule.mk sorry sorry

end algebra

/-- Semiring ⥤ ℕ-Alg -/
protected instance algebra_nat (R : Type u_1) [semiring R] : algebra ℕ R :=
  algebra.mk (nat.cast_ring_hom R) nat.cast_commute sorry

theorem span_nat_eq_add_group_closure (R : Type u_1) [semiring R] (s : set R) : submodule.to_add_submonoid (submodule.span ℕ s) = add_submonoid.closure s := sorry

@[simp] theorem span_nat_eq (R : Type u_1) [semiring R] (s : add_submonoid R) : submodule.to_add_submonoid (submodule.span ℕ ↑s) = s := sorry

/-- Ring ⥤ ℤ-Alg -/
protected instance algebra_int (R : Type u_1) [ring R] : algebra ℤ R :=
  algebra.mk (int.cast_ring_hom R) int.cast_commute sorry

protected instance int_algebra_subsingleton {S : Type u_2} [ring S] : subsingleton (algebra ℤ S) :=
  subsingleton.intro
    fun (P Q : algebra ℤ S) =>
      algebra.algebra_ext P Q
        fun (r : ℤ) =>
          eq.mpr
            (id
              (Eq.trans
                ((fun (a a_1 : S) (e_1 : a = a_1) (ᾰ ᾰ_1 : S) (e_2 : ᾰ = ᾰ_1) => congr (congr_arg Eq e_1) e_2)
                  (coe_fn (algebra_map ℤ S) r) (↑r) (ring_hom.eq_int_cast (algebra_map ℤ S) r)
                  (coe_fn (algebra_map ℤ S) r) (↑r) (ring_hom.eq_int_cast (algebra_map ℤ S) r))
                (propext (eq_self_iff_true ↑r))))
            trivial

protected instance nat_algebra_subsingleton {S : Type u_2} [semiring S] : subsingleton (algebra ℕ S) :=
  subsingleton.intro
    fun (P Q : algebra ℕ S) =>
      algebra.algebra_ext P Q
        fun (r : ℕ) =>
          eq.mpr
            (id
              (Eq.trans
                ((fun (a a_1 : S) (e_1 : a = a_1) (ᾰ ᾰ_1 : S) (e_2 : ᾰ = ᾰ_1) => congr (congr_arg Eq e_1) e_2)
                  (coe_fn (algebra_map ℕ S) r) (↑r) (ring_hom.eq_nat_cast (algebra_map ℕ S) r)
                  (coe_fn (algebra_map ℕ S) r) (↑r) (ring_hom.eq_nat_cast (algebra_map ℕ S) r))
                (propext (eq_self_iff_true ↑r))))
            trivial

theorem span_int_eq_add_group_closure {R : Type u_1} [ring R] (s : set R) : submodule.to_add_subgroup (submodule.span ℤ s) = add_subgroup.closure s := sorry

@[simp] theorem span_int_eq {R : Type u_1} [ring R] (s : add_subgroup R) : submodule.to_add_subgroup (submodule.span ℤ ↑s) = s :=
  eq.mpr
    (id (Eq._oldrec (Eq.refl (submodule.to_add_subgroup (submodule.span ℤ ↑s) = s)) (span_int_eq_add_group_closure ↑s)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (add_subgroup.closure ↑s = s)) (add_subgroup.closure_eq s))) (Eq.refl s))

/-!
The R-algebra structure on `Π i : I, A i` when each `A i` is an R-algebra.

We couldn't set this up back in `algebra.pi_instances` because this file imports it.
-/

namespace pi

protected instance algebra (I : Type u) (f : I → Type v) (α : Type u_1) {r : comm_semiring α} [s : (i : I) → semiring (f i)] [(i : I) → algebra α (f i)] : algebra α ((i : I) → f i) :=
  algebra.mk (ring_hom.mk (ring_hom.to_fun (pi.ring_hom fun (i : I) => algebra_map α (f i))) sorry sorry sorry sorry)
    sorry sorry

@[simp] theorem algebra_map_apply (I : Type u) (f : I → Type v) (α : Type u_1) {r : comm_semiring α} [s : (i : I) → semiring (f i)] [(i : I) → algebra α (f i)] (a : α) (i : I) : coe_fn (algebra_map α ((i : I) → f i)) a i = coe_fn (algebra_map α (f i)) a :=
  rfl

-- One could also build a `Π i, R i`-algebra structure on `Π i, A i`,

-- when each `A i` is an `R i`-algebra, although I'm not sure that it's useful.

end pi

theorem algebra_compatible_smul {R : Type u_1} [comm_semiring R] (A : Type u_2) [semiring A] [algebra R A] {M : Type u_3} [add_comm_monoid M] [semimodule A M] [semimodule R M] [is_scalar_tower R A M] (r : R) (m : M) : r • m = coe_fn (algebra_map R A) r • m := sorry

@[simp] theorem algebra_map_smul {R : Type u_1} [comm_semiring R] (A : Type u_2) [semiring A] [algebra R A] {M : Type u_3} [add_comm_monoid M] [semimodule A M] [semimodule R M] [is_scalar_tower R A M] (r : R) (m : M) : coe_fn (algebra_map R A) r • m = r • m :=
  Eq.symm (algebra_compatible_smul A r m)

protected instance is_scalar_tower.to_smul_comm_class {R : Type u_1} [comm_semiring R] {A : Type u_2} [semiring A] [algebra R A] {M : Type u_3} [add_comm_monoid M] [semimodule A M] [semimodule R M] [is_scalar_tower R A M] : smul_comm_class R A M :=
  smul_comm_class.mk
    fun (r : R) (a : A) (m : M) =>
      eq.mpr (id (Eq._oldrec (Eq.refl (r • a • m = a • r • m)) (algebra_compatible_smul A r (a • m))))
        (eq.mpr
          (id
            (Eq._oldrec (Eq.refl (coe_fn (algebra_map R A) r • a • m = a • r • m))
              (smul_smul (coe_fn (algebra_map R A) r) a m)))
          (eq.mpr (id (Eq._oldrec (Eq.refl ((coe_fn (algebra_map R A) r * a) • m = a • r • m)) (algebra.commutes r a)))
            (eq.mpr
              (id
                (Eq._oldrec (Eq.refl ((a * coe_fn (algebra_map R A) r) • m = a • r • m))
                  (mul_smul a (coe_fn (algebra_map R A) r) m)))
              (eq.mpr
                (id
                  (Eq._oldrec (Eq.refl (a • coe_fn (algebra_map R A) r • m = a • r • m))
                    (Eq.symm (algebra_compatible_smul A r m))))
                (Eq.refl (a • r • m))))))

protected instance is_scalar_tower.to_smul_comm_class' {R : Type u_1} [comm_semiring R] {A : Type u_2} [semiring A] [algebra R A] {M : Type u_3} [add_comm_monoid M] [semimodule A M] [semimodule R M] [is_scalar_tower R A M] : smul_comm_class A R M :=
  smul_comm_class.symm R A M

theorem smul_algebra_smul_comm {R : Type u_1} [comm_semiring R] {A : Type u_2} [semiring A] [algebra R A] {M : Type u_3} [add_comm_monoid M] [semimodule A M] [semimodule R M] [is_scalar_tower R A M] (r : R) (a : A) (m : M) : a • r • m = r • a • m :=
  smul_comm a r m

namespace linear_map

protected instance coe_is_scalar_tower {R : Type u_1} [comm_semiring R] {A : Type u_2} [semiring A] [algebra R A] {M : Type u_3} [add_comm_monoid M] [semimodule A M] [semimodule R M] [is_scalar_tower R A M] {N : Type u_4} [add_comm_monoid N] [semimodule A N] [semimodule R N] [is_scalar_tower R A N] : has_coe (linear_map A M N) (linear_map R M N) :=
  has_coe.mk (restrict_scalars R)

@[simp] theorem coe_restrict_scalars_eq_coe (R : Type u_1) [comm_semiring R] {A : Type u_2} [semiring A] [algebra R A] {M : Type u_3} [add_comm_monoid M] [semimodule A M] [semimodule R M] [is_scalar_tower R A M] {N : Type u_4} [add_comm_monoid N] [semimodule A N] [semimodule R N] [is_scalar_tower R A N] (f : linear_map A M N) : ⇑(restrict_scalars R f) = ⇑f :=
  rfl

@[simp] theorem coe_coe_is_scalar_tower (R : Type u_1) [comm_semiring R] {A : Type u_2} [semiring A] [algebra R A] {M : Type u_3} [add_comm_monoid M] [semimodule A M] [semimodule R M] [is_scalar_tower R A M] {N : Type u_4} [add_comm_monoid N] [semimodule A N] [semimodule R N] [is_scalar_tower R A N] (f : linear_map A M N) : ⇑↑f = ⇑f :=
  rfl

/-- `A`-linearly coerce a `R`-linear map from `M` to `A` to a function, given an algebra `A` over
a commutative semiring `R` and `M` a semimodule over `R`. -/
def lto_fun (R : Type u) (M : Type v) (A : Type w) [comm_semiring R] [add_comm_monoid M] [semimodule R M] [comm_ring A] [algebra R A] : linear_map A (linear_map R M A) (M → A) :=
  mk to_fun sorry sorry

end linear_map

/- In this section, we describe restriction of scalars: if `S` is an algebra over `R`, then
`S`-modules are also `R`-modules. -/

/--
Warning: use this type synonym judiciously!
The preferred way of working with an `A`-module `M` as `R`-module (where `A` is an `R`-algebra),
is by `[module R M] [module A M] [is_scalar_tower R A M]`.

When `M` is a module over a ring `A`, and `A` is an algebra over `R`, then `M` inherits a
module structure over `R`, provided as a type synonym `module.restrict_scalars R A M := M`.
-/
def restrict_scalars (R : Type u_1) (A : Type u_2) (M : Type u_3) :=
  M

protected instance restrict_scalars.inhabited (R : Type u_1) (A : Type u_2) (M : Type u_3) [I : Inhabited M] : Inhabited (restrict_scalars R A M) :=
  I

protected instance restrict_scalars.add_comm_monoid (R : Type u_1) (A : Type u_2) (M : Type u_3) [I : add_comm_monoid M] : add_comm_monoid (restrict_scalars R A M) :=
  I

protected instance restrict_scalars.add_comm_group (R : Type u_1) (A : Type u_2) (M : Type u_3) [I : add_comm_group M] : add_comm_group (restrict_scalars R A M) :=
  I

protected instance restrict_scalars.module_orig (R : Type u_1) (A : Type u_2) (M : Type u_3) [semiring A] [add_comm_monoid M] [I : semimodule A M] : semimodule A (restrict_scalars R A M) :=
  I

/--
When `M` is a module over a ring `A`, and `A` is an algebra over `R`, then `M` inherits a
module structure over `R`.

The preferred way of setting this up is `[module R M] [module A M] [is_scalar_tower R A M]`.
-/
protected instance restrict_scalars.semimodule (R : Type u_1) (A : Type u_2) (M : Type u_3) [comm_semiring R] [semiring A] [algebra R A] [add_comm_monoid M] [semimodule A M] : semimodule R (restrict_scalars R A M) :=
  semimodule.mk sorry sorry

theorem restrict_scalars_smul_def (R : Type u_1) (A : Type u_2) (M : Type u_3) [comm_semiring R] [semiring A] [algebra R A] [add_comm_monoid M] [semimodule A M] (c : R) (x : restrict_scalars R A M) : c • x = coe_fn (algebra_map R A) c • x :=
  rfl

protected instance restrict_scalars.is_scalar_tower (R : Type u_1) (A : Type u_2) (M : Type u_3) [comm_semiring R] [semiring A] [algebra R A] [add_comm_monoid M] [semimodule A M] : is_scalar_tower R A (restrict_scalars R A M) :=
  is_scalar_tower.mk
    fun (r : R) (A_1 : A) (M_1 : restrict_scalars R A M) =>
      eq.mpr (id (Eq._oldrec (Eq.refl ((r • A_1) • M_1 = r • A_1 • M_1)) (algebra.smul_def r A_1)))
        (eq.mpr
          (id
            (Eq._oldrec (Eq.refl ((coe_fn (algebra_map R A) r * A_1) • M_1 = r • A_1 • M_1))
              (mul_smul (coe_fn (algebra_map R A) r) A_1 M_1)))
          (Eq.refl (coe_fn (algebra_map R A) r • A_1 • M_1)))

protected instance submodule.restricted_module (R : Type u_1) (A : Type u_2) (M : Type u_3) [comm_semiring R] [semiring A] [algebra R A] [add_comm_monoid M] [semimodule A M] (V : submodule A M) : semimodule R ↥V :=
  restrict_scalars.semimodule R A ↥V

protected instance submodule.restricted_module_is_scalar_tower (R : Type u_1) (A : Type u_2) (M : Type u_3) [comm_semiring R] [semiring A] [algebra R A] [add_comm_monoid M] [semimodule A M] (V : submodule A M) : is_scalar_tower R A ↥V :=
  restrict_scalars.is_scalar_tower R A ↥V

namespace submodule

/--
`V.restrict_scalars R` is the `R`-submodule of the `R`-module given by restriction of scalars,
corresponding to `V`, an `S`-submodule of the original `S`-module.
-/
def restrict_scalars (R : Type u_1) {A : Type u_2} {M : Type u_3} [comm_semiring R] [semiring A] [algebra R A] [add_comm_monoid M] [semimodule R M] [semimodule A M] [is_scalar_tower R A M] (V : submodule A M) : submodule R M :=
  mk (carrier V) (zero_mem V) sorry sorry

@[simp] theorem restrict_scalars_mem (R : Type u_1) {A : Type u_2} {M : Type u_3} [comm_semiring R] [semiring A] [algebra R A] [add_comm_monoid M] [semimodule R M] [semimodule A M] [is_scalar_tower R A M] (V : submodule A M) (m : M) : m ∈ restrict_scalars R V ↔ m ∈ V :=
  iff.refl (m ∈ restrict_scalars R V)

theorem restrict_scalars_injective (R : Type u_1) (A : Type u_2) (M : Type u_3) [comm_semiring R] [semiring A] [algebra R A] [add_comm_monoid M] [semimodule R M] [semimodule A M] [is_scalar_tower R A M] : function.injective (restrict_scalars R) :=
  fun (V₁ V₂ : submodule A M) (h : restrict_scalars R V₁ = restrict_scalars R V₂) =>
    ext (eq.mpr (Eq.refl (∀ (x : M), x ∈ V₁ ↔ x ∈ V₂)) (iff.mp set.ext_iff (iff.mp ext'_iff h)))

@[simp] theorem restrict_scalars_inj (R : Type u_1) (A : Type u_2) (M : Type u_3) [comm_semiring R] [semiring A] [algebra R A] [add_comm_monoid M] [semimodule R M] [semimodule A M] [is_scalar_tower R A M] {V₁ : submodule A M} {V₂ : submodule A M} : restrict_scalars R V₁ = restrict_scalars R V₂ ↔ V₁ = V₂ :=
  { mp := fun (h : restrict_scalars R V₁ = restrict_scalars R V₂) => restrict_scalars_injective R A M h,
    mpr := congr_arg fun {V₁ : submodule A M} => restrict_scalars R V₁ }

@[simp] theorem restrict_scalars_bot (R : Type u_1) (A : Type u_2) (M : Type u_3) [comm_semiring R] [semiring A] [algebra R A] [add_comm_monoid M] [semimodule R M] [semimodule A M] [is_scalar_tower R A M] : restrict_scalars R ⊥ = ⊥ :=
  rfl

@[simp] theorem restrict_scalars_top (R : Type u_1) (A : Type u_2) (M : Type u_3) [comm_semiring R] [semiring A] [algebra R A] [add_comm_monoid M] [semimodule R M] [semimodule A M] [is_scalar_tower R A M] : restrict_scalars R ⊤ = ⊤ :=
  rfl

end submodule

@[simp] theorem linear_map.ker_restrict_scalars (R : Type u_1) {A : Type u_2} {M : Type u_3} {N : Type u_4} [comm_semiring R] [semiring A] [algebra R A] [add_comm_monoid M] [semimodule R M] [semimodule A M] [is_scalar_tower R A M] [add_comm_monoid N] [semimodule R N] [semimodule A N] [is_scalar_tower R A N] (f : linear_map A M N) : linear_map.ker (linear_map.restrict_scalars R f) = submodule.restrict_scalars R (linear_map.ker f) :=
  rfl

namespace linear_map

/-! When `V` is an `R`-module and `W` is an `S`-module, where `S` is an algebra over `R`, then
the collection of `R`-linear maps from `V` to `W` admits an `S`-module structure, given by
multiplication in the target. -/

protected instance is_scalar_tower_extend_scalars (R : Type u_1) [comm_semiring R] (S : Type u_2) [semiring S] [algebra R S] (V : Type u_3) [add_comm_monoid V] [semimodule R V] (W : Type u_4) [add_comm_monoid W] [semimodule R W] [semimodule S W] [is_scalar_tower R S W] : is_scalar_tower R S (linear_map R V W) := sorry

/-- When `f` is a linear map taking values in `S`, then `λb, f b • x` is a linear map. -/
def smul_algebra_right {R : Type u_1} [comm_semiring R] {S : Type u_2} [semiring S] [algebra R S] {V : Type u_3} [add_comm_monoid V] [semimodule R V] {W : Type u_4} [add_comm_monoid W] [semimodule R W] [semimodule S W] [is_scalar_tower R S W] (f : linear_map R V S) (x : W) : linear_map R V W :=
  mk (fun (b : V) => coe_fn f b • x) sorry sorry

@[simp] theorem smul_algebra_right_apply {R : Type u_1} [comm_semiring R] {S : Type u_2} [semiring S] [algebra R S] {V : Type u_3} [add_comm_monoid V] [semimodule R V] {W : Type u_4} [add_comm_monoid W] [semimodule R W] [semimodule S W] [is_scalar_tower R S W] (f : linear_map R V S) (x : W) (c : V) : coe_fn (smul_algebra_right f x) c = coe_fn f c • x :=
  rfl