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

! This file was ported from Lean 3 source module computability.language
! leanprover-community/mathlib commit e97cf15cd1aec9bd5c193b2ffac5a6dc9118912b
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Hom.Ring
import Mathbin.Algebra.Order.Kleene
import Mathbin.Data.List.Join
import Mathbin.Data.Set.Lattice

/-!
# Languages

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

This file contains the definition and operations on formal languages over an alphabet. Note strings
are implemented as lists over the alphabet.
The operations in this file define a [Kleene algebra](https://en.wikipedia.org/wiki/Kleene_algebra)
over the languages.
-/

open List Set

open Computability

universe v

variable {α β γ : Type _}

/- ./././Mathport/Syntax/Translate/Command.lean:42:9: unsupported derive handler has_mem[has_mem] (list[list] α) -/
/- ./././Mathport/Syntax/Translate/Command.lean:42:9: unsupported derive handler has_singleton[has_singleton] (list[list] α) -/
/- ./././Mathport/Syntax/Translate/Command.lean:42:9: unsupported derive handler has_insert[has_insert] (list[list] α) -/
#print Language /-
/-- A language is a set of strings over an alphabet. -/
def Language (α) :=
  Set (List α)deriving
  «./././Mathport/Syntax/Translate/Command.lean:42:9: unsupported derive handler has_mem[has_mem] (list[list] α)»,
  «./././Mathport/Syntax/Translate/Command.lean:42:9: unsupported derive handler has_singleton[has_singleton] (list[list] α)»,
  «./././Mathport/Syntax/Translate/Command.lean:42:9: unsupported derive handler has_insert[has_insert] (list[list] α)»,
  CompleteBooleanAlgebra
#align language Language
-/

namespace Language

variable {l m : Language α} {a b x : List α}

attribute [local reducible] Language

/-- Zero language has no elements. -/
instance : Zero (Language α) :=
  ⟨(∅ : Set _)⟩

/-- `1 : language α` contains only one element `[]`. -/
instance : One (Language α) :=
  ⟨{[]}⟩

instance : Inhabited (Language α) :=
  ⟨0⟩

/-- The sum of two languages is their union. -/
instance : Add (Language α) :=
  ⟨(· ∪ ·)⟩

/-- The product of two languages `l` and `m` is the language made of the strings `x ++ y` where
`x ∈ l` and `y ∈ m`. -/
instance : Mul (Language α) :=
  ⟨image2 (· ++ ·)⟩

#print Language.zero_def /-
theorem zero_def : (0 : Language α) = (∅ : Set _) :=
  rfl
#align language.zero_def Language.zero_def
-/

#print Language.one_def /-
theorem one_def : (1 : Language α) = {[]} :=
  rfl
#align language.one_def Language.one_def
-/

/- warning: language.add_def -> Language.add_def is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (l : Language.{u1} α) (m : Language.{u1} α), Eq.{succ u1} (Language.{u1} α) (HAdd.hAdd.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHAdd.{u1} (Language.{u1} α) (Language.hasAdd.{u1} α)) l m) (Union.union.{u1} (Language.{u1} α) (Set.hasUnion.{u1} (List.{u1} α)) l m)
but is expected to have type
  forall {α : Type.{u1}} (l : Language.{u1} α) (m : Language.{u1} α), Eq.{succ u1} (Language.{u1} α) (HAdd.hAdd.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHAdd.{u1} (Language.{u1} α) (Language.instAddLanguage.{u1} α)) l m) (Union.union.{u1} (Set.{u1} (List.{u1} α)) (Set.instUnionSet.{u1} (List.{u1} α)) l m)
Case conversion may be inaccurate. Consider using '#align language.add_def Language.add_defₓ'. -/
theorem add_def (l m : Language α) : l + m = l ∪ m :=
  rfl
#align language.add_def Language.add_def

#print Language.mul_def /-
theorem mul_def (l m : Language α) : l * m = image2 (· ++ ·) l m :=
  rfl
#align language.mul_def Language.mul_def
-/

/-- The Kleene star of a language `L` is the set of all strings which can be written by
concatenating strings from `L`. -/
instance : KStar (Language α) :=
  ⟨fun l => { x | ∃ L : List (List α), x = L.join ∧ ∀ y ∈ L, y ∈ l }⟩

#print Language.kstar_def /-
theorem kstar_def (l : Language α) :
    l∗ = { x | ∃ L : List (List α), x = L.join ∧ ∀ y ∈ L, y ∈ l } :=
  rfl
#align language.kstar_def Language.kstar_def
-/

#print Language.not_mem_zero /-
@[simp]
theorem not_mem_zero (x : List α) : x ∉ (0 : Language α) :=
  id
#align language.not_mem_zero Language.not_mem_zero
-/

#print Language.mem_one /-
@[simp]
theorem mem_one (x : List α) : x ∈ (1 : Language α) ↔ x = [] := by rfl
#align language.mem_one Language.mem_one
-/

#print Language.nil_mem_one /-
theorem nil_mem_one : [] ∈ (1 : Language α) :=
  Set.mem_singleton _
#align language.nil_mem_one Language.nil_mem_one
-/

/- warning: language.mem_add -> Language.mem_add is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (l : Language.{u1} α) (m : Language.{u1} α) (x : List.{u1} α), Iff (Membership.Mem.{u1, u1} (List.{u1} α) (Language.{u1} α) (Language.hasMem.{u1} α) x (HAdd.hAdd.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHAdd.{u1} (Language.{u1} α) (Language.hasAdd.{u1} α)) l m)) (Or (Membership.Mem.{u1, u1} (List.{u1} α) (Language.{u1} α) (Language.hasMem.{u1} α) x l) (Membership.Mem.{u1, u1} (List.{u1} α) (Language.{u1} α) (Language.hasMem.{u1} α) x m))
but is expected to have type
  forall {α : Type.{u1}} (l : Language.{u1} α) (m : Language.{u1} α) (x : List.{u1} α), Iff (Membership.mem.{u1, u1} (List.{u1} α) (Language.{u1} α) (instMembershipListLanguage.{u1} α) x (HAdd.hAdd.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHAdd.{u1} (Language.{u1} α) (Language.instAddLanguage.{u1} α)) l m)) (Or (Membership.mem.{u1, u1} (List.{u1} α) (Language.{u1} α) (instMembershipListLanguage.{u1} α) x l) (Membership.mem.{u1, u1} (List.{u1} α) (Language.{u1} α) (instMembershipListLanguage.{u1} α) x m))
Case conversion may be inaccurate. Consider using '#align language.mem_add Language.mem_addₓ'. -/
theorem mem_add (l m : Language α) (x : List α) : x ∈ l + m ↔ x ∈ l ∨ x ∈ m :=
  Iff.rfl
#align language.mem_add Language.mem_add

#print Language.mem_mul /-
theorem mem_mul : x ∈ l * m ↔ ∃ a b, a ∈ l ∧ b ∈ m ∧ a ++ b = x :=
  mem_image2
#align language.mem_mul Language.mem_mul
-/

#print Language.append_mem_mul /-
theorem append_mem_mul : a ∈ l → b ∈ m → a ++ b ∈ l * m :=
  mem_image2_of_mem
#align language.append_mem_mul Language.append_mem_mul
-/

#print Language.mem_kstar /-
theorem mem_kstar : x ∈ l∗ ↔ ∃ L : List (List α), x = L.join ∧ ∀ y ∈ L, y ∈ l :=
  Iff.rfl
#align language.mem_kstar Language.mem_kstar
-/

#print Language.join_mem_kstar /-
theorem join_mem_kstar {L : List (List α)} (h : ∀ y ∈ L, y ∈ l) : L.join ∈ l∗ :=
  ⟨L, rfl, h⟩
#align language.join_mem_kstar Language.join_mem_kstar
-/

#print Language.nil_mem_kstar /-
theorem nil_mem_kstar (l : Language α) : [] ∈ l∗ :=
  ⟨[], rfl, fun _ => False.elim⟩
#align language.nil_mem_kstar Language.nil_mem_kstar
-/

instance : Semiring (Language α) where
  add := (· + ·)
  add_assoc := union_assoc
  zero := 0
  zero_add := empty_union
  add_zero := union_empty
  add_comm := union_comm
  mul := (· * ·)
  mul_assoc _ _ _ := image2_assoc append_assoc
  zero_mul _ := image2_empty_left
  mul_zero _ := image2_empty_right
  one := 1
  one_mul l := by simp [mul_def, one_def]
  mul_one l := by simp [mul_def, one_def]
  natCast n := if n = 0 then 0 else 1
  natCast_zero := rfl
  natCast_succ n := by cases n <;> simp [Nat.cast, add_def, zero_def]
  left_distrib _ _ _ := image2_union_right
  right_distrib _ _ _ := image2_union_left

/- warning: language.add_self -> Language.add_self is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (l : Language.{u1} α), Eq.{succ u1} (Language.{u1} α) (HAdd.hAdd.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHAdd.{u1} (Language.{u1} α) (Language.hasAdd.{u1} α)) l l) l
but is expected to have type
  forall {α : Type.{u1}} (l : Language.{u1} α), Eq.{succ u1} (Language.{u1} α) (HAdd.hAdd.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHAdd.{u1} (Language.{u1} α) (Language.instAddLanguage.{u1} α)) l l) l
Case conversion may be inaccurate. Consider using '#align language.add_self Language.add_selfₓ'. -/
@[simp]
theorem add_self (l : Language α) : l + l = l :=
  sup_idem
#align language.add_self Language.add_self

/- warning: language.map -> Language.map is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}}, (α -> β) -> (RingHom.{u1, u2} (Language.{u1} α) (Language.{u2} β) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.semiring.{u1} α)) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} β) (Language.semiring.{u2} β)))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}}, (α -> β) -> (RingHom.{u1, u2} (Language.{u1} α) (Language.{u2} β) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.instSemiringLanguage.{u1} α)) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} β) (Language.instSemiringLanguage.{u2} β)))
Case conversion may be inaccurate. Consider using '#align language.map Language.mapₓ'. -/
/-- Maps the alphabet of a language. -/
def map (f : α → β) : Language α →+* Language β
    where
  toFun := image (List.map f)
  map_zero' := image_empty _
  map_one' := image_singleton
  map_add' := image_union _
  map_mul' _ _ := image_image2_distrib <| map_append _
#align language.map Language.map

/- warning: language.map_id -> Language.map_id is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (l : Language.{u1} α), Eq.{succ u1} (Language.{u1} α) (coeFn.{succ u1, succ u1} (RingHom.{u1, u1} (Language.{u1} α) (Language.{u1} α) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.semiring.{u1} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.semiring.{u1} α))) (fun (_x : RingHom.{u1, u1} (Language.{u1} α) (Language.{u1} α) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.semiring.{u1} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.semiring.{u1} α))) => (Language.{u1} α) -> (Language.{u1} α)) (RingHom.hasCoeToFun.{u1, u1} (Language.{u1} α) (Language.{u1} α) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.semiring.{u1} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.semiring.{u1} α))) (Language.map.{u1, u1} α α (id.{succ u1} α)) l) l
but is expected to have type
  forall {α : Type.{u1}} (l : Language.{u1} α), Eq.{succ u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : Language.{u1} α) => Language.{u1} α) l) (FunLike.coe.{succ u1, succ u1, succ u1} (RingHom.{u1, u1} (Language.{u1} α) (Language.{u1} α) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.instSemiringLanguage.{u1} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.instSemiringLanguage.{u1} α))) (Language.{u1} α) (fun (_x : Language.{u1} α) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : Language.{u1} α) => Language.{u1} α) _x) (MulHomClass.toFunLike.{u1, u1, u1} (RingHom.{u1, u1} (Language.{u1} α) (Language.{u1} α) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.instSemiringLanguage.{u1} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.instSemiringLanguage.{u1} α))) (Language.{u1} α) (Language.{u1} α) (NonUnitalNonAssocSemiring.toMul.{u1} (Language.{u1} α) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Language.{u1} α) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.instSemiringLanguage.{u1} α)))) (NonUnitalNonAssocSemiring.toMul.{u1} (Language.{u1} α) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Language.{u1} α) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.instSemiringLanguage.{u1} α)))) (NonUnitalRingHomClass.toMulHomClass.{u1, u1, u1} (RingHom.{u1, u1} (Language.{u1} α) (Language.{u1} α) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.instSemiringLanguage.{u1} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.instSemiringLanguage.{u1} α))) (Language.{u1} α) (Language.{u1} α) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Language.{u1} α) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.instSemiringLanguage.{u1} α))) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Language.{u1} α) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.instSemiringLanguage.{u1} α))) (RingHomClass.toNonUnitalRingHomClass.{u1, u1, u1} (RingHom.{u1, u1} (Language.{u1} α) (Language.{u1} α) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.instSemiringLanguage.{u1} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.instSemiringLanguage.{u1} α))) (Language.{u1} α) (Language.{u1} α) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.instSemiringLanguage.{u1} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.instSemiringLanguage.{u1} α)) (RingHom.instRingHomClassRingHom.{u1, u1} (Language.{u1} α) (Language.{u1} α) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.instSemiringLanguage.{u1} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.instSemiringLanguage.{u1} α)))))) (Language.map.{u1, u1} α α (id.{succ u1} α)) l) l
Case conversion may be inaccurate. Consider using '#align language.map_id Language.map_idₓ'. -/
@[simp]
theorem map_id (l : Language α) : map id l = l := by simp [map]
#align language.map_id Language.map_id

/- warning: language.map_map -> Language.map_map is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} (g : β -> γ) (f : α -> β) (l : Language.{u1} α), Eq.{succ u3} (Language.{u3} γ) (coeFn.{max (succ u2) (succ u3), max (succ u2) (succ u3)} (RingHom.{u2, u3} (Language.{u2} β) (Language.{u3} γ) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} β) (Language.semiring.{u2} β)) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} γ) (Language.semiring.{u3} γ))) (fun (_x : RingHom.{u2, u3} (Language.{u2} β) (Language.{u3} γ) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} β) (Language.semiring.{u2} β)) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} γ) (Language.semiring.{u3} γ))) => (Language.{u2} β) -> (Language.{u3} γ)) (RingHom.hasCoeToFun.{u2, u3} (Language.{u2} β) (Language.{u3} γ) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} β) (Language.semiring.{u2} β)) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} γ) (Language.semiring.{u3} γ))) (Language.map.{u2, u3} β γ g) (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (RingHom.{u1, u2} (Language.{u1} α) (Language.{u2} β) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.semiring.{u1} α)) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} β) (Language.semiring.{u2} β))) (fun (_x : RingHom.{u1, u2} (Language.{u1} α) (Language.{u2} β) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.semiring.{u1} α)) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} β) (Language.semiring.{u2} β))) => (Language.{u1} α) -> (Language.{u2} β)) (RingHom.hasCoeToFun.{u1, u2} (Language.{u1} α) (Language.{u2} β) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.semiring.{u1} α)) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} β) (Language.semiring.{u2} β))) (Language.map.{u1, u2} α β f) l)) (coeFn.{max (succ u1) (succ u3), max (succ u1) (succ u3)} (RingHom.{u1, u3} (Language.{u1} α) (Language.{u3} γ) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.semiring.{u1} α)) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} γ) (Language.semiring.{u3} γ))) (fun (_x : RingHom.{u1, u3} (Language.{u1} α) (Language.{u3} γ) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.semiring.{u1} α)) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} γ) (Language.semiring.{u3} γ))) => (Language.{u1} α) -> (Language.{u3} γ)) (RingHom.hasCoeToFun.{u1, u3} (Language.{u1} α) (Language.{u3} γ) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.semiring.{u1} α)) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} γ) (Language.semiring.{u3} γ))) (Language.map.{u1, u3} α γ (Function.comp.{succ u1, succ u2, succ u3} α β γ g f)) l)
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u1}} {γ : Type.{u2}} (g : β -> γ) (f : α -> β) (l : Language.{u3} α), Eq.{succ u2} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : Language.{u1} β) => Language.{u2} γ) (FunLike.coe.{max (succ u3) (succ u1), succ u3, succ u1} (RingHom.{u3, u1} (Language.{u3} α) (Language.{u1} β) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} α) (Language.instSemiringLanguage.{u3} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β))) (Language.{u3} α) (fun (a : Language.{u3} α) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : Language.{u3} α) => Language.{u1} β) a) (MulHomClass.toFunLike.{max u3 u1, u3, u1} (RingHom.{u3, u1} (Language.{u3} α) (Language.{u1} β) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} α) (Language.instSemiringLanguage.{u3} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β))) (Language.{u3} α) (Language.{u1} β) (NonUnitalNonAssocSemiring.toMul.{u3} (Language.{u3} α) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} (Language.{u3} α) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} α) (Language.instSemiringLanguage.{u3} α)))) (NonUnitalNonAssocSemiring.toMul.{u1} (Language.{u1} β) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Language.{u1} β) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β)))) (NonUnitalRingHomClass.toMulHomClass.{max u3 u1, u3, u1} (RingHom.{u3, u1} (Language.{u3} α) (Language.{u1} β) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} α) (Language.instSemiringLanguage.{u3} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β))) (Language.{u3} α) (Language.{u1} β) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} (Language.{u3} α) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} α) (Language.instSemiringLanguage.{u3} α))) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Language.{u1} β) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β))) (RingHomClass.toNonUnitalRingHomClass.{max u3 u1, u3, u1} (RingHom.{u3, u1} (Language.{u3} α) (Language.{u1} β) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} α) (Language.instSemiringLanguage.{u3} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β))) (Language.{u3} α) (Language.{u1} β) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} α) (Language.instSemiringLanguage.{u3} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β)) (RingHom.instRingHomClassRingHom.{u3, u1} (Language.{u3} α) (Language.{u1} β) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} α) (Language.instSemiringLanguage.{u3} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β)))))) (Language.map.{u3, u1} α β f) l)) (FunLike.coe.{max (succ u1) (succ u2), succ u1, succ u2} (RingHom.{u1, u2} (Language.{u1} β) (Language.{u2} γ) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β)) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} γ) (Language.instSemiringLanguage.{u2} γ))) (Language.{u1} β) (fun (_x : Language.{u1} β) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : Language.{u1} β) => Language.{u2} γ) _x) (MulHomClass.toFunLike.{max u1 u2, u1, u2} (RingHom.{u1, u2} (Language.{u1} β) (Language.{u2} γ) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β)) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} γ) (Language.instSemiringLanguage.{u2} γ))) (Language.{u1} β) (Language.{u2} γ) (NonUnitalNonAssocSemiring.toMul.{u1} (Language.{u1} β) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Language.{u1} β) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β)))) (NonUnitalNonAssocSemiring.toMul.{u2} (Language.{u2} γ) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} (Language.{u2} γ) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} γ) (Language.instSemiringLanguage.{u2} γ)))) (NonUnitalRingHomClass.toMulHomClass.{max u1 u2, u1, u2} (RingHom.{u1, u2} (Language.{u1} β) (Language.{u2} γ) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β)) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} γ) (Language.instSemiringLanguage.{u2} γ))) (Language.{u1} β) (Language.{u2} γ) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Language.{u1} β) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β))) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} (Language.{u2} γ) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} γ) (Language.instSemiringLanguage.{u2} γ))) (RingHomClass.toNonUnitalRingHomClass.{max u1 u2, u1, u2} (RingHom.{u1, u2} (Language.{u1} β) (Language.{u2} γ) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β)) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} γ) (Language.instSemiringLanguage.{u2} γ))) (Language.{u1} β) (Language.{u2} γ) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β)) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} γ) (Language.instSemiringLanguage.{u2} γ)) (RingHom.instRingHomClassRingHom.{u1, u2} (Language.{u1} β) (Language.{u2} γ) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β)) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} γ) (Language.instSemiringLanguage.{u2} γ)))))) (Language.map.{u1, u2} β γ g) (FunLike.coe.{max (succ u3) (succ u1), succ u3, succ u1} (RingHom.{u3, u1} (Language.{u3} α) (Language.{u1} β) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} α) (Language.instSemiringLanguage.{u3} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β))) (Language.{u3} α) (fun (_x : Language.{u3} α) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : Language.{u3} α) => Language.{u1} β) _x) (MulHomClass.toFunLike.{max u3 u1, u3, u1} (RingHom.{u3, u1} (Language.{u3} α) (Language.{u1} β) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} α) (Language.instSemiringLanguage.{u3} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β))) (Language.{u3} α) (Language.{u1} β) (NonUnitalNonAssocSemiring.toMul.{u3} (Language.{u3} α) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} (Language.{u3} α) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} α) (Language.instSemiringLanguage.{u3} α)))) (NonUnitalNonAssocSemiring.toMul.{u1} (Language.{u1} β) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Language.{u1} β) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β)))) (NonUnitalRingHomClass.toMulHomClass.{max u3 u1, u3, u1} (RingHom.{u3, u1} (Language.{u3} α) (Language.{u1} β) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} α) (Language.instSemiringLanguage.{u3} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β))) (Language.{u3} α) (Language.{u1} β) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} (Language.{u3} α) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} α) (Language.instSemiringLanguage.{u3} α))) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Language.{u1} β) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β))) (RingHomClass.toNonUnitalRingHomClass.{max u3 u1, u3, u1} (RingHom.{u3, u1} (Language.{u3} α) (Language.{u1} β) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} α) (Language.instSemiringLanguage.{u3} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β))) (Language.{u3} α) (Language.{u1} β) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} α) (Language.instSemiringLanguage.{u3} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β)) (RingHom.instRingHomClassRingHom.{u3, u1} (Language.{u3} α) (Language.{u1} β) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} α) (Language.instSemiringLanguage.{u3} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β)))))) (Language.map.{u3, u1} α β f) l)) (FunLike.coe.{max (succ u3) (succ u2), succ u3, succ u2} (RingHom.{u3, u2} (Language.{u3} α) (Language.{u2} γ) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} α) (Language.instSemiringLanguage.{u3} α)) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} γ) (Language.instSemiringLanguage.{u2} γ))) (Language.{u3} α) (fun (_x : Language.{u3} α) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : Language.{u3} α) => Language.{u2} γ) _x) (MulHomClass.toFunLike.{max u3 u2, u3, u2} (RingHom.{u3, u2} (Language.{u3} α) (Language.{u2} γ) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} α) (Language.instSemiringLanguage.{u3} α)) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} γ) (Language.instSemiringLanguage.{u2} γ))) (Language.{u3} α) (Language.{u2} γ) (NonUnitalNonAssocSemiring.toMul.{u3} (Language.{u3} α) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} (Language.{u3} α) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} α) (Language.instSemiringLanguage.{u3} α)))) (NonUnitalNonAssocSemiring.toMul.{u2} (Language.{u2} γ) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} (Language.{u2} γ) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} γ) (Language.instSemiringLanguage.{u2} γ)))) (NonUnitalRingHomClass.toMulHomClass.{max u3 u2, u3, u2} (RingHom.{u3, u2} (Language.{u3} α) (Language.{u2} γ) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} α) (Language.instSemiringLanguage.{u3} α)) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} γ) (Language.instSemiringLanguage.{u2} γ))) (Language.{u3} α) (Language.{u2} γ) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u3} (Language.{u3} α) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} α) (Language.instSemiringLanguage.{u3} α))) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} (Language.{u2} γ) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} γ) (Language.instSemiringLanguage.{u2} γ))) (RingHomClass.toNonUnitalRingHomClass.{max u3 u2, u3, u2} (RingHom.{u3, u2} (Language.{u3} α) (Language.{u2} γ) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} α) (Language.instSemiringLanguage.{u3} α)) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} γ) (Language.instSemiringLanguage.{u2} γ))) (Language.{u3} α) (Language.{u2} γ) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} α) (Language.instSemiringLanguage.{u3} α)) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} γ) (Language.instSemiringLanguage.{u2} γ)) (RingHom.instRingHomClassRingHom.{u3, u2} (Language.{u3} α) (Language.{u2} γ) (Semiring.toNonAssocSemiring.{u3} (Language.{u3} α) (Language.instSemiringLanguage.{u3} α)) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} γ) (Language.instSemiringLanguage.{u2} γ)))))) (Language.map.{u3, u2} α γ (Function.comp.{succ u3, succ u1, succ u2} α β γ g f)) l)
Case conversion may be inaccurate. Consider using '#align language.map_map Language.map_mapₓ'. -/
@[simp]
theorem map_map (g : β → γ) (f : α → β) (l : Language α) : map g (map f l) = map (g ∘ f) l := by
  simp [map, image_image]
#align language.map_map Language.map_map

#print Language.kstar_def_nonempty /-
theorem kstar_def_nonempty (l : Language α) :
    l∗ = { x | ∃ S : List (List α), x = S.join ∧ ∀ y ∈ S, y ∈ l ∧ y ≠ [] } :=
  by
  ext x
  constructor
  · rintro ⟨S, rfl, h⟩
    refine' ⟨S.filter fun l => ¬List.isEmpty l, by simp, fun y hy => _⟩
    rw [mem_filter, empty_iff_eq_nil] at hy
    exact ⟨h y hy.1, hy.2⟩
  · rintro ⟨S, hx, h⟩
    exact ⟨S, hx, fun y hy => (h y hy).1⟩
#align language.kstar_def_nonempty Language.kstar_def_nonempty
-/

/- warning: language.le_iff -> Language.le_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (l : Language.{u1} α) (m : Language.{u1} α), Iff (LE.le.{u1} (Language.{u1} α) (Set.hasLe.{u1} (List.{u1} α)) l m) (Eq.{succ u1} (Language.{u1} α) (HAdd.hAdd.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHAdd.{u1} (Language.{u1} α) (Language.hasAdd.{u1} α)) l m) m)
but is expected to have type
  forall {α : Type.{u1}} (l : Language.{u1} α) (m : Language.{u1} α), Iff (LE.le.{u1} (Language.{u1} α) (Preorder.toLE.{u1} (Language.{u1} α) (PartialOrder.toPreorder.{u1} (Language.{u1} α) (CompleteSemilatticeInf.toPartialOrder.{u1} (Language.{u1} α) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Language.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Language.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Language.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Language.{u1} α) (instCompleteBooleanAlgebraLanguage.{u1} α)))))))) l m) (Eq.{succ u1} (Language.{u1} α) (HAdd.hAdd.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHAdd.{u1} (Language.{u1} α) (Language.instAddLanguage.{u1} α)) l m) m)
Case conversion may be inaccurate. Consider using '#align language.le_iff Language.le_iffₓ'. -/
theorem le_iff (l m : Language α) : l ≤ m ↔ l + m = m :=
  sup_eq_right.symm
#align language.le_iff Language.le_iff

#print Language.le_mul_congr /-
theorem le_mul_congr {l₁ l₂ m₁ m₂ : Language α} : l₁ ≤ m₁ → l₂ ≤ m₂ → l₁ * l₂ ≤ m₁ * m₂ :=
  by
  intro h₁ h₂ x hx
  simp only [mul_def, exists_and_left, mem_image2, image_prod] at hx⊢
  tauto
#align language.le_mul_congr Language.le_mul_congr
-/

/- warning: language.le_add_congr -> Language.le_add_congr is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {l₁ : Language.{u1} α} {l₂ : Language.{u1} α} {m₁ : Language.{u1} α} {m₂ : Language.{u1} α}, (LE.le.{u1} (Language.{u1} α) (Set.hasLe.{u1} (List.{u1} α)) l₁ m₁) -> (LE.le.{u1} (Language.{u1} α) (Set.hasLe.{u1} (List.{u1} α)) l₂ m₂) -> (LE.le.{u1} (Language.{u1} α) (Set.hasLe.{u1} (List.{u1} α)) (HAdd.hAdd.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHAdd.{u1} (Language.{u1} α) (Language.hasAdd.{u1} α)) l₁ l₂) (HAdd.hAdd.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHAdd.{u1} (Language.{u1} α) (Language.hasAdd.{u1} α)) m₁ m₂))
but is expected to have type
  forall {α : Type.{u1}} {l₁ : Language.{u1} α} {l₂ : Language.{u1} α} {m₁ : Language.{u1} α} {m₂ : Language.{u1} α}, (LE.le.{u1} (Language.{u1} α) (Preorder.toLE.{u1} (Language.{u1} α) (PartialOrder.toPreorder.{u1} (Language.{u1} α) (CompleteSemilatticeInf.toPartialOrder.{u1} (Language.{u1} α) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Language.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Language.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Language.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Language.{u1} α) (instCompleteBooleanAlgebraLanguage.{u1} α)))))))) l₁ m₁) -> (LE.le.{u1} (Language.{u1} α) (Preorder.toLE.{u1} (Language.{u1} α) (PartialOrder.toPreorder.{u1} (Language.{u1} α) (CompleteSemilatticeInf.toPartialOrder.{u1} (Language.{u1} α) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Language.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Language.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Language.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Language.{u1} α) (instCompleteBooleanAlgebraLanguage.{u1} α)))))))) l₂ m₂) -> (LE.le.{u1} (Language.{u1} α) (Preorder.toLE.{u1} (Language.{u1} α) (PartialOrder.toPreorder.{u1} (Language.{u1} α) (CompleteSemilatticeInf.toPartialOrder.{u1} (Language.{u1} α) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Language.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Language.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Language.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Language.{u1} α) (instCompleteBooleanAlgebraLanguage.{u1} α)))))))) (HAdd.hAdd.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHAdd.{u1} (Language.{u1} α) (Language.instAddLanguage.{u1} α)) l₁ l₂) (HAdd.hAdd.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHAdd.{u1} (Language.{u1} α) (Language.instAddLanguage.{u1} α)) m₁ m₂))
Case conversion may be inaccurate. Consider using '#align language.le_add_congr Language.le_add_congrₓ'. -/
theorem le_add_congr {l₁ l₂ m₁ m₂ : Language α} : l₁ ≤ m₁ → l₂ ≤ m₂ → l₁ + l₂ ≤ m₁ + m₂ :=
  sup_le_sup
#align language.le_add_congr Language.le_add_congr

/- warning: language.mem_supr -> Language.mem_supᵢ is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u2}} {ι : Sort.{u1}} {l : ι -> (Language.{u2} α)} {x : List.{u2} α}, Iff (Membership.Mem.{u2, u2} (List.{u2} α) (Language.{u2} α) (Language.hasMem.{u2} α) x (supᵢ.{u2, u1} (Language.{u2} α) (Set.hasSup.{u2} (List.{u2} α)) ι (fun (i : ι) => l i))) (Exists.{u1} ι (fun (i : ι) => Membership.Mem.{u2, u2} (List.{u2} α) (Language.{u2} α) (Language.hasMem.{u2} α) x (l i)))
but is expected to have type
  forall {α : Type.{u1}} {ι : Sort.{u2}} {l : ι -> (Language.{u1} α)} {x : List.{u1} α}, Iff (Membership.mem.{u1, u1} (List.{u1} α) (Language.{u1} α) (instMembershipListLanguage.{u1} α) x (supᵢ.{u1, u2} (Language.{u1} α) (CompleteBooleanAlgebra.toSupSet.{u1} (Language.{u1} α) (instCompleteBooleanAlgebraLanguage.{u1} α)) ι (fun (i : ι) => l i))) (Exists.{u2} ι (fun (i : ι) => Membership.mem.{u1, u1} (List.{u1} α) (Language.{u1} α) (instMembershipListLanguage.{u1} α) x (l i)))
Case conversion may be inaccurate. Consider using '#align language.mem_supr Language.mem_supᵢₓ'. -/
theorem mem_supᵢ {ι : Sort v} {l : ι → Language α} {x : List α} : (x ∈ ⨆ i, l i) ↔ ∃ i, x ∈ l i :=
  mem_unionᵢ
#align language.mem_supr Language.mem_supᵢ

/- warning: language.supr_mul -> Language.supᵢ_mul is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u2}} {ι : Sort.{u1}} (l : ι -> (Language.{u2} α)) (m : Language.{u2} α), Eq.{succ u2} (Language.{u2} α) (HMul.hMul.{u2, u2, u2} (Language.{u2} α) (Language.{u2} α) (Language.{u2} α) (instHMul.{u2} (Language.{u2} α) (Language.hasMul.{u2} α)) (supᵢ.{u2, u1} (Language.{u2} α) (Set.hasSup.{u2} (List.{u2} α)) ι (fun (i : ι) => l i)) m) (supᵢ.{u2, u1} (Language.{u2} α) (Set.hasSup.{u2} (List.{u2} α)) ι (fun (i : ι) => HMul.hMul.{u2, u2, u2} (Language.{u2} α) (Language.{u2} α) (Language.{u2} α) (instHMul.{u2} (Language.{u2} α) (Language.hasMul.{u2} α)) (l i) m))
but is expected to have type
  forall {α : Type.{u1}} {ι : Sort.{u2}} (l : ι -> (Language.{u1} α)) (m : Language.{u1} α), Eq.{succ u1} (Language.{u1} α) (HMul.hMul.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHMul.{u1} (Language.{u1} α) (Language.instMulLanguage.{u1} α)) (supᵢ.{u1, u2} (Language.{u1} α) (CompleteBooleanAlgebra.toSupSet.{u1} (Language.{u1} α) (instCompleteBooleanAlgebraLanguage.{u1} α)) ι (fun (i : ι) => l i)) m) (supᵢ.{u1, u2} (Language.{u1} α) (CompleteBooleanAlgebra.toSupSet.{u1} (Language.{u1} α) (instCompleteBooleanAlgebraLanguage.{u1} α)) ι (fun (i : ι) => HMul.hMul.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHMul.{u1} (Language.{u1} α) (Language.instMulLanguage.{u1} α)) (l i) m))
Case conversion may be inaccurate. Consider using '#align language.supr_mul Language.supᵢ_mulₓ'. -/
theorem supᵢ_mul {ι : Sort v} (l : ι → Language α) (m : Language α) :
    (⨆ i, l i) * m = ⨆ i, l i * m :=
  image2_unionᵢ_left _ _ _
#align language.supr_mul Language.supᵢ_mul

/- warning: language.mul_supr -> Language.mul_supᵢ is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u2}} {ι : Sort.{u1}} (l : ι -> (Language.{u2} α)) (m : Language.{u2} α), Eq.{succ u2} (Language.{u2} α) (HMul.hMul.{u2, u2, u2} (Language.{u2} α) (Language.{u2} α) (Language.{u2} α) (instHMul.{u2} (Language.{u2} α) (Language.hasMul.{u2} α)) m (supᵢ.{u2, u1} (Language.{u2} α) (Set.hasSup.{u2} (List.{u2} α)) ι (fun (i : ι) => l i))) (supᵢ.{u2, u1} (Language.{u2} α) (Set.hasSup.{u2} (List.{u2} α)) ι (fun (i : ι) => HMul.hMul.{u2, u2, u2} (Language.{u2} α) (Language.{u2} α) (Language.{u2} α) (instHMul.{u2} (Language.{u2} α) (Language.hasMul.{u2} α)) m (l i)))
but is expected to have type
  forall {α : Type.{u1}} {ι : Sort.{u2}} (l : ι -> (Language.{u1} α)) (m : Language.{u1} α), Eq.{succ u1} (Language.{u1} α) (HMul.hMul.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHMul.{u1} (Language.{u1} α) (Language.instMulLanguage.{u1} α)) m (supᵢ.{u1, u2} (Language.{u1} α) (CompleteBooleanAlgebra.toSupSet.{u1} (Language.{u1} α) (instCompleteBooleanAlgebraLanguage.{u1} α)) ι (fun (i : ι) => l i))) (supᵢ.{u1, u2} (Language.{u1} α) (CompleteBooleanAlgebra.toSupSet.{u1} (Language.{u1} α) (instCompleteBooleanAlgebraLanguage.{u1} α)) ι (fun (i : ι) => HMul.hMul.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHMul.{u1} (Language.{u1} α) (Language.instMulLanguage.{u1} α)) m (l i)))
Case conversion may be inaccurate. Consider using '#align language.mul_supr Language.mul_supᵢₓ'. -/
theorem mul_supᵢ {ι : Sort v} (l : ι → Language α) (m : Language α) :
    (m * ⨆ i, l i) = ⨆ i, m * l i :=
  image2_unionᵢ_right _ _ _
#align language.mul_supr Language.mul_supᵢ

/- warning: language.supr_add -> Language.supᵢ_add is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u2}} {ι : Sort.{u1}} [_inst_1 : Nonempty.{u1} ι] (l : ι -> (Language.{u2} α)) (m : Language.{u2} α), Eq.{succ u2} (Language.{u2} α) (HAdd.hAdd.{u2, u2, u2} (Language.{u2} α) (Language.{u2} α) (Language.{u2} α) (instHAdd.{u2} (Language.{u2} α) (Language.hasAdd.{u2} α)) (supᵢ.{u2, u1} (Language.{u2} α) (Set.hasSup.{u2} (List.{u2} α)) ι (fun (i : ι) => l i)) m) (supᵢ.{u2, u1} (Language.{u2} α) (Set.hasSup.{u2} (List.{u2} α)) ι (fun (i : ι) => HAdd.hAdd.{u2, u2, u2} (Language.{u2} α) (Language.{u2} α) (Language.{u2} α) (instHAdd.{u2} (Language.{u2} α) (Language.hasAdd.{u2} α)) (l i) m))
but is expected to have type
  forall {α : Type.{u1}} {ι : Sort.{u2}} [_inst_1 : Nonempty.{u2} ι] (l : ι -> (Language.{u1} α)) (m : Language.{u1} α), Eq.{succ u1} (Language.{u1} α) (HAdd.hAdd.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHAdd.{u1} (Language.{u1} α) (Language.instAddLanguage.{u1} α)) (supᵢ.{u1, u2} (Language.{u1} α) (CompleteBooleanAlgebra.toSupSet.{u1} (Language.{u1} α) (instCompleteBooleanAlgebraLanguage.{u1} α)) ι (fun (i : ι) => l i)) m) (supᵢ.{u1, u2} (Language.{u1} α) (CompleteBooleanAlgebra.toSupSet.{u1} (Language.{u1} α) (instCompleteBooleanAlgebraLanguage.{u1} α)) ι (fun (i : ι) => HAdd.hAdd.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHAdd.{u1} (Language.{u1} α) (Language.instAddLanguage.{u1} α)) (l i) m))
Case conversion may be inaccurate. Consider using '#align language.supr_add Language.supᵢ_addₓ'. -/
theorem supᵢ_add {ι : Sort v} [Nonempty ι] (l : ι → Language α) (m : Language α) :
    (⨆ i, l i) + m = ⨆ i, l i + m :=
  supᵢ_sup
#align language.supr_add Language.supᵢ_add

/- warning: language.add_supr -> Language.add_supᵢ is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u2}} {ι : Sort.{u1}} [_inst_1 : Nonempty.{u1} ι] (l : ι -> (Language.{u2} α)) (m : Language.{u2} α), Eq.{succ u2} (Language.{u2} α) (HAdd.hAdd.{u2, u2, u2} (Language.{u2} α) (Language.{u2} α) (Language.{u2} α) (instHAdd.{u2} (Language.{u2} α) (Language.hasAdd.{u2} α)) m (supᵢ.{u2, u1} (Language.{u2} α) (Set.hasSup.{u2} (List.{u2} α)) ι (fun (i : ι) => l i))) (supᵢ.{u2, u1} (Language.{u2} α) (Set.hasSup.{u2} (List.{u2} α)) ι (fun (i : ι) => HAdd.hAdd.{u2, u2, u2} (Language.{u2} α) (Language.{u2} α) (Language.{u2} α) (instHAdd.{u2} (Language.{u2} α) (Language.hasAdd.{u2} α)) m (l i)))
but is expected to have type
  forall {α : Type.{u1}} {ι : Sort.{u2}} [_inst_1 : Nonempty.{u2} ι] (l : ι -> (Language.{u1} α)) (m : Language.{u1} α), Eq.{succ u1} (Language.{u1} α) (HAdd.hAdd.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHAdd.{u1} (Language.{u1} α) (Language.instAddLanguage.{u1} α)) m (supᵢ.{u1, u2} (Language.{u1} α) (CompleteBooleanAlgebra.toSupSet.{u1} (Language.{u1} α) (instCompleteBooleanAlgebraLanguage.{u1} α)) ι (fun (i : ι) => l i))) (supᵢ.{u1, u2} (Language.{u1} α) (CompleteBooleanAlgebra.toSupSet.{u1} (Language.{u1} α) (instCompleteBooleanAlgebraLanguage.{u1} α)) ι (fun (i : ι) => HAdd.hAdd.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHAdd.{u1} (Language.{u1} α) (Language.instAddLanguage.{u1} α)) m (l i)))
Case conversion may be inaccurate. Consider using '#align language.add_supr Language.add_supᵢₓ'. -/
theorem add_supᵢ {ι : Sort v} [Nonempty ι] (l : ι → Language α) (m : Language α) :
    (m + ⨆ i, l i) = ⨆ i, m + l i :=
  sup_supᵢ
#align language.add_supr Language.add_supᵢ

/- warning: language.mem_pow -> Language.mem_pow is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {l : Language.{u1} α} {x : List.{u1} α} {n : Nat}, Iff (Membership.Mem.{u1, u1} (List.{u1} α) (Language.{u1} α) (Language.hasMem.{u1} α) x (HPow.hPow.{u1, 0, u1} (Language.{u1} α) Nat (Language.{u1} α) (instHPow.{u1, 0} (Language.{u1} α) Nat (Monoid.Pow.{u1} (Language.{u1} α) (MonoidWithZero.toMonoid.{u1} (Language.{u1} α) (Semiring.toMonoidWithZero.{u1} (Language.{u1} α) (Language.semiring.{u1} α))))) l n)) (Exists.{succ u1} (List.{u1} (List.{u1} α)) (fun (S : List.{u1} (List.{u1} α)) => And (Eq.{succ u1} (List.{u1} α) x (List.join.{u1} α S)) (And (Eq.{1} Nat (List.length.{u1} (List.{u1} α) S) n) (forall (y : List.{u1} α), (Membership.Mem.{u1, u1} (List.{u1} α) (List.{u1} (List.{u1} α)) (List.hasMem.{u1} (List.{u1} α)) y S) -> (Membership.Mem.{u1, u1} (List.{u1} α) (Language.{u1} α) (Language.hasMem.{u1} α) y l)))))
but is expected to have type
  forall {α : Type.{u1}} {l : Language.{u1} α} {x : List.{u1} α} {n : Nat}, Iff (Membership.mem.{u1, u1} (List.{u1} α) (Language.{u1} α) (instMembershipListLanguage.{u1} α) x (HPow.hPow.{u1, 0, u1} (Language.{u1} α) Nat (Language.{u1} α) (instHPow.{u1, 0} (Language.{u1} α) Nat (Monoid.Pow.{u1} (Language.{u1} α) (MonoidWithZero.toMonoid.{u1} (Language.{u1} α) (Semiring.toMonoidWithZero.{u1} (Language.{u1} α) (Language.instSemiringLanguage.{u1} α))))) l n)) (Exists.{succ u1} (List.{u1} (List.{u1} α)) (fun (S : List.{u1} (List.{u1} α)) => And (Eq.{succ u1} (List.{u1} α) x (List.join.{u1} α S)) (And (Eq.{1} Nat (List.length.{u1} (List.{u1} α) S) n) (forall (y : List.{u1} α), (Membership.mem.{u1, u1} (List.{u1} α) (List.{u1} (List.{u1} α)) (List.instMembershipList.{u1} (List.{u1} α)) y S) -> (Membership.mem.{u1, u1} (List.{u1} α) (Language.{u1} α) (instMembershipListLanguage.{u1} α) y l)))))
Case conversion may be inaccurate. Consider using '#align language.mem_pow Language.mem_powₓ'. -/
theorem mem_pow {l : Language α} {x : List α} {n : ℕ} :
    x ∈ l ^ n ↔ ∃ S : List (List α), x = S.join ∧ S.length = n ∧ ∀ y ∈ S, y ∈ l :=
  by
  induction' n with n ihn generalizing x
  · simp only [mem_one, pow_zero, length_eq_zero]
    constructor
    · rintro rfl
      exact ⟨[], rfl, rfl, fun y h => h.elim⟩
    · rintro ⟨_, rfl, rfl, _⟩
      rfl
  · simp only [pow_succ, mem_mul, ihn]
    constructor
    · rintro ⟨a, b, ha, ⟨S, rfl, rfl, hS⟩, rfl⟩
      exact ⟨a :: S, rfl, rfl, forall_mem_cons.2 ⟨ha, hS⟩⟩
    · rintro ⟨_ | ⟨a, S⟩, rfl, hn, hS⟩ <;> cases hn
      rw [forall_mem_cons] at hS
      exact ⟨a, _, hS.1, ⟨S, rfl, rfl, hS.2⟩, rfl⟩
#align language.mem_pow Language.mem_pow

/- warning: language.kstar_eq_supr_pow -> Language.kstar_eq_supᵢ_pow is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (l : Language.{u1} α), Eq.{succ u1} (Language.{u1} α) (KStar.kstar.{u1} (Language.{u1} α) (Language.hasKstar.{u1} α) l) (supᵢ.{u1, 1} (Language.{u1} α) (Set.hasSup.{u1} (List.{u1} α)) Nat (fun (i : Nat) => HPow.hPow.{u1, 0, u1} (Language.{u1} α) Nat (Language.{u1} α) (instHPow.{u1, 0} (Language.{u1} α) Nat (Monoid.Pow.{u1} (Language.{u1} α) (MonoidWithZero.toMonoid.{u1} (Language.{u1} α) (Semiring.toMonoidWithZero.{u1} (Language.{u1} α) (Language.semiring.{u1} α))))) l i))
but is expected to have type
  forall {α : Type.{u1}} (l : Language.{u1} α), Eq.{succ u1} (Language.{u1} α) (KStar.kstar.{u1} (Language.{u1} α) (Language.instKStarLanguage.{u1} α) l) (supᵢ.{u1, 1} (Language.{u1} α) (CompleteBooleanAlgebra.toSupSet.{u1} (Language.{u1} α) (instCompleteBooleanAlgebraLanguage.{u1} α)) Nat (fun (i : Nat) => HPow.hPow.{u1, 0, u1} (Language.{u1} α) Nat (Language.{u1} α) (instHPow.{u1, 0} (Language.{u1} α) Nat (Monoid.Pow.{u1} (Language.{u1} α) (MonoidWithZero.toMonoid.{u1} (Language.{u1} α) (Semiring.toMonoidWithZero.{u1} (Language.{u1} α) (Language.instSemiringLanguage.{u1} α))))) l i))
Case conversion may be inaccurate. Consider using '#align language.kstar_eq_supr_pow Language.kstar_eq_supᵢ_powₓ'. -/
theorem kstar_eq_supᵢ_pow (l : Language α) : l∗ = ⨆ i : ℕ, l ^ i :=
  by
  ext x
  simp only [mem_kstar, mem_supr, mem_pow]
  constructor
  · rintro ⟨S, rfl, hS⟩
    exact ⟨_, S, rfl, rfl, hS⟩
  · rintro ⟨_, S, rfl, rfl, hS⟩
    exact ⟨S, rfl, hS⟩
#align language.kstar_eq_supr_pow Language.kstar_eq_supᵢ_pow

/- warning: language.map_kstar -> Language.map_kstar is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} (f : α -> β) (l : Language.{u1} α), Eq.{succ u2} (Language.{u2} β) (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (RingHom.{u1, u2} (Language.{u1} α) (Language.{u2} β) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.semiring.{u1} α)) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} β) (Language.semiring.{u2} β))) (fun (_x : RingHom.{u1, u2} (Language.{u1} α) (Language.{u2} β) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.semiring.{u1} α)) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} β) (Language.semiring.{u2} β))) => (Language.{u1} α) -> (Language.{u2} β)) (RingHom.hasCoeToFun.{u1, u2} (Language.{u1} α) (Language.{u2} β) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.semiring.{u1} α)) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} β) (Language.semiring.{u2} β))) (Language.map.{u1, u2} α β f) (KStar.kstar.{u1} (Language.{u1} α) (Language.hasKstar.{u1} α) l)) (KStar.kstar.{u2} (Language.{u2} β) (Language.hasKstar.{u2} β) (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (RingHom.{u1, u2} (Language.{u1} α) (Language.{u2} β) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.semiring.{u1} α)) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} β) (Language.semiring.{u2} β))) (fun (_x : RingHom.{u1, u2} (Language.{u1} α) (Language.{u2} β) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.semiring.{u1} α)) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} β) (Language.semiring.{u2} β))) => (Language.{u1} α) -> (Language.{u2} β)) (RingHom.hasCoeToFun.{u1, u2} (Language.{u1} α) (Language.{u2} β) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} α) (Language.semiring.{u1} α)) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} β) (Language.semiring.{u2} β))) (Language.map.{u1, u2} α β f) l))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} (f : α -> β) (l : Language.{u2} α), Eq.{succ u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : Language.{u2} α) => Language.{u1} β) (KStar.kstar.{u2} (Language.{u2} α) (Language.instKStarLanguage.{u2} α) l)) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (RingHom.{u2, u1} (Language.{u2} α) (Language.{u1} β) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} α) (Language.instSemiringLanguage.{u2} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β))) (Language.{u2} α) (fun (_x : Language.{u2} α) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : Language.{u2} α) => Language.{u1} β) _x) (MulHomClass.toFunLike.{max u2 u1, u2, u1} (RingHom.{u2, u1} (Language.{u2} α) (Language.{u1} β) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} α) (Language.instSemiringLanguage.{u2} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β))) (Language.{u2} α) (Language.{u1} β) (NonUnitalNonAssocSemiring.toMul.{u2} (Language.{u2} α) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} (Language.{u2} α) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} α) (Language.instSemiringLanguage.{u2} α)))) (NonUnitalNonAssocSemiring.toMul.{u1} (Language.{u1} β) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Language.{u1} β) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β)))) (NonUnitalRingHomClass.toMulHomClass.{max u2 u1, u2, u1} (RingHom.{u2, u1} (Language.{u2} α) (Language.{u1} β) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} α) (Language.instSemiringLanguage.{u2} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β))) (Language.{u2} α) (Language.{u1} β) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} (Language.{u2} α) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} α) (Language.instSemiringLanguage.{u2} α))) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Language.{u1} β) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β))) (RingHomClass.toNonUnitalRingHomClass.{max u2 u1, u2, u1} (RingHom.{u2, u1} (Language.{u2} α) (Language.{u1} β) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} α) (Language.instSemiringLanguage.{u2} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β))) (Language.{u2} α) (Language.{u1} β) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} α) (Language.instSemiringLanguage.{u2} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β)) (RingHom.instRingHomClassRingHom.{u2, u1} (Language.{u2} α) (Language.{u1} β) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} α) (Language.instSemiringLanguage.{u2} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β)))))) (Language.map.{u2, u1} α β f) (KStar.kstar.{u2} (Language.{u2} α) (Language.instKStarLanguage.{u2} α) l)) (KStar.kstar.{u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : Language.{u2} α) => Language.{u1} β) l) (Language.instKStarLanguage.{u1} β) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (RingHom.{u2, u1} (Language.{u2} α) (Language.{u1} β) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} α) (Language.instSemiringLanguage.{u2} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β))) (Language.{u2} α) (fun (_x : Language.{u2} α) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : Language.{u2} α) => Language.{u1} β) _x) (MulHomClass.toFunLike.{max u2 u1, u2, u1} (RingHom.{u2, u1} (Language.{u2} α) (Language.{u1} β) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} α) (Language.instSemiringLanguage.{u2} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β))) (Language.{u2} α) (Language.{u1} β) (NonUnitalNonAssocSemiring.toMul.{u2} (Language.{u2} α) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} (Language.{u2} α) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} α) (Language.instSemiringLanguage.{u2} α)))) (NonUnitalNonAssocSemiring.toMul.{u1} (Language.{u1} β) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Language.{u1} β) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β)))) (NonUnitalRingHomClass.toMulHomClass.{max u2 u1, u2, u1} (RingHom.{u2, u1} (Language.{u2} α) (Language.{u1} β) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} α) (Language.instSemiringLanguage.{u2} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β))) (Language.{u2} α) (Language.{u1} β) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u2} (Language.{u2} α) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} α) (Language.instSemiringLanguage.{u2} α))) (NonAssocSemiring.toNonUnitalNonAssocSemiring.{u1} (Language.{u1} β) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β))) (RingHomClass.toNonUnitalRingHomClass.{max u2 u1, u2, u1} (RingHom.{u2, u1} (Language.{u2} α) (Language.{u1} β) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} α) (Language.instSemiringLanguage.{u2} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β))) (Language.{u2} α) (Language.{u1} β) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} α) (Language.instSemiringLanguage.{u2} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β)) (RingHom.instRingHomClassRingHom.{u2, u1} (Language.{u2} α) (Language.{u1} β) (Semiring.toNonAssocSemiring.{u2} (Language.{u2} α) (Language.instSemiringLanguage.{u2} α)) (Semiring.toNonAssocSemiring.{u1} (Language.{u1} β) (Language.instSemiringLanguage.{u1} β)))))) (Language.map.{u2, u1} α β f) l))
Case conversion may be inaccurate. Consider using '#align language.map_kstar Language.map_kstarₓ'. -/
@[simp]
theorem map_kstar (f : α → β) (l : Language α) : map f l∗ = (map f l)∗ :=
  by
  rw [kstar_eq_supr_pow, kstar_eq_supr_pow]
  simp_rw [← map_pow]
  exact image_Union
#align language.map_kstar Language.map_kstar

#print Language.mul_self_kstar_comm /-
theorem mul_self_kstar_comm (l : Language α) : l∗ * l = l * l∗ := by
  simp only [kstar_eq_supr_pow, mul_supr, supr_mul, ← pow_succ, ← pow_succ']
#align language.mul_self_kstar_comm Language.mul_self_kstar_comm
-/

/- warning: language.one_add_self_mul_kstar_eq_kstar -> Language.one_add_self_mul_kstar_eq_kstar is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (l : Language.{u1} α), Eq.{succ u1} (Language.{u1} α) (HAdd.hAdd.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHAdd.{u1} (Language.{u1} α) (Language.hasAdd.{u1} α)) (OfNat.ofNat.{u1} (Language.{u1} α) 1 (OfNat.mk.{u1} (Language.{u1} α) 1 (One.one.{u1} (Language.{u1} α) (Language.hasOne.{u1} α)))) (HMul.hMul.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHMul.{u1} (Language.{u1} α) (Language.hasMul.{u1} α)) l (KStar.kstar.{u1} (Language.{u1} α) (Language.hasKstar.{u1} α) l))) (KStar.kstar.{u1} (Language.{u1} α) (Language.hasKstar.{u1} α) l)
but is expected to have type
  forall {α : Type.{u1}} (l : Language.{u1} α), Eq.{succ u1} (Language.{u1} α) (HAdd.hAdd.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHAdd.{u1} (Language.{u1} α) (Language.instAddLanguage.{u1} α)) (OfNat.ofNat.{u1} (Language.{u1} α) 1 (One.toOfNat1.{u1} (Language.{u1} α) (Language.instOneLanguage.{u1} α))) (HMul.hMul.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHMul.{u1} (Language.{u1} α) (Language.instMulLanguage.{u1} α)) l (KStar.kstar.{u1} (Language.{u1} α) (Language.instKStarLanguage.{u1} α) l))) (KStar.kstar.{u1} (Language.{u1} α) (Language.instKStarLanguage.{u1} α) l)
Case conversion may be inaccurate. Consider using '#align language.one_add_self_mul_kstar_eq_kstar Language.one_add_self_mul_kstar_eq_kstarₓ'. -/
@[simp]
theorem one_add_self_mul_kstar_eq_kstar (l : Language α) : 1 + l * l∗ = l∗ :=
  by
  simp only [kstar_eq_supr_pow, mul_supr, ← pow_succ, ← pow_zero l]
  exact sup_supᵢ_nat_succ _
#align language.one_add_self_mul_kstar_eq_kstar Language.one_add_self_mul_kstar_eq_kstar

/- warning: language.one_add_kstar_mul_self_eq_kstar -> Language.one_add_kstar_mul_self_eq_kstar is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (l : Language.{u1} α), Eq.{succ u1} (Language.{u1} α) (HAdd.hAdd.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHAdd.{u1} (Language.{u1} α) (Language.hasAdd.{u1} α)) (OfNat.ofNat.{u1} (Language.{u1} α) 1 (OfNat.mk.{u1} (Language.{u1} α) 1 (One.one.{u1} (Language.{u1} α) (Language.hasOne.{u1} α)))) (HMul.hMul.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHMul.{u1} (Language.{u1} α) (Language.hasMul.{u1} α)) (KStar.kstar.{u1} (Language.{u1} α) (Language.hasKstar.{u1} α) l) l)) (KStar.kstar.{u1} (Language.{u1} α) (Language.hasKstar.{u1} α) l)
but is expected to have type
  forall {α : Type.{u1}} (l : Language.{u1} α), Eq.{succ u1} (Language.{u1} α) (HAdd.hAdd.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHAdd.{u1} (Language.{u1} α) (Language.instAddLanguage.{u1} α)) (OfNat.ofNat.{u1} (Language.{u1} α) 1 (One.toOfNat1.{u1} (Language.{u1} α) (Language.instOneLanguage.{u1} α))) (HMul.hMul.{u1, u1, u1} (Language.{u1} α) (Language.{u1} α) (Language.{u1} α) (instHMul.{u1} (Language.{u1} α) (Language.instMulLanguage.{u1} α)) (KStar.kstar.{u1} (Language.{u1} α) (Language.instKStarLanguage.{u1} α) l) l)) (KStar.kstar.{u1} (Language.{u1} α) (Language.instKStarLanguage.{u1} α) l)
Case conversion may be inaccurate. Consider using '#align language.one_add_kstar_mul_self_eq_kstar Language.one_add_kstar_mul_self_eq_kstarₓ'. -/
@[simp]
theorem one_add_kstar_mul_self_eq_kstar (l : Language α) : 1 + l∗ * l = l∗ := by
  rw [mul_self_kstar_comm, one_add_self_mul_kstar_eq_kstar]
#align language.one_add_kstar_mul_self_eq_kstar Language.one_add_kstar_mul_self_eq_kstar

instance : KleeneAlgebra (Language α) :=
  { Language.semiring, Set.completeBooleanAlgebra,
    Language.hasKstar with
    one_le_kstar := fun a l hl => ⟨[], hl, by simp⟩
    mul_kstar_le_kstar := fun a => (one_add_self_mul_kstar_eq_kstar a).le.trans' le_sup_right
    kstar_mul_le_kstar := fun a => (one_add_kstar_mul_self_eq_kstar a).le.trans' le_sup_right
    kstar_mul_le_self := fun l m h =>
      by
      rw [kstar_eq_supr_pow, supr_mul]
      refine' supᵢ_le fun n => _
      induction' n with n ih
      · simp
      rw [pow_succ', mul_assoc (l ^ n) l m]
      exact le_trans (le_mul_congr le_rfl h) ih
    mul_kstar_le_self := fun l m h =>
      by
      rw [kstar_eq_supr_pow, mul_supr]
      refine' supᵢ_le fun n => _
      induction' n with n ih
      · simp
      rw [pow_succ, ← mul_assoc m l (l ^ n)]
      exact le_trans (le_mul_congr h le_rfl) ih }

end Language


/-
Copyright (c) 2019 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Patrick Massot, Casper Putz, Anne Baanen
-/
import linear_algebra.multilinear.basis
import linear_algebra.matrix.reindex
import ring_theory.algebra_tower
import linear_algebra.matrix.nonsingular_inverse
import linear_algebra.matrix.basis

/-!
# Determinant of families of vectors

This file defines the determinant of an endomorphism, and of a family of vectors
with respect to some basis. For the determinant of a matrix, see the file
`linear_algebra.matrix.determinant`.

## Main definitions

In the list below, and in all this file, `R` is a commutative ring (semiring
is sometimes enough), `M` and its variations are `R`-modules, `ι`, `κ`, `n` and `m` are finite
types used for indexing.

 * `basis.det`: the determinant of a family of vectors with respect to a basis,
   as a multilinear map
 * `linear_map.det`: the determinant of an endomorphism `f : End R M` as a
   multiplicative homomorphism (if `M` does not have a finite `R`-basis, the
   result is `1` instead)

## Tags

basis, det, determinant
-/

noncomputable theory

open_locale big_operators
open_locale matrix

open linear_map
open submodule

universes u v w

open linear_map matrix set function

variables {R : Type*} [comm_ring R]
variables {M : Type*} [add_comm_group M] [module R M]
variables {M' : Type*} [add_comm_group M'] [module R M']
variables {ι : Type*} [decidable_eq ι] [fintype ι]
variables (e : basis ι R M)

section conjugate

variables {A : Type*} [comm_ring A]
variables {m n : Type*} [fintype m] [fintype n]

/-- If `R^m` and `R^n` are linearly equivalent, then `m` and `n` are also equivalent. -/
def equiv_of_pi_lequiv_pi {R : Type*} [comm_ring R] [is_domain R]
  (e : (m → R) ≃ₗ[R] (n → R)) : m ≃ n :=
basis.index_equiv (basis.of_equiv_fun e.symm) (pi.basis_fun _ _)

namespace matrix

/-- If `M` and `M'` are each other's inverse matrices, they are square matrices up to
equivalence of types. -/
def index_equiv_of_inv [is_domain A] [decidable_eq m] [decidable_eq n]
  {M : matrix m n A} {M' : matrix n m A}
  (hMM' : M ⬝ M' = 1) (hM'M : M' ⬝ M = 1) :
  m ≃ n :=
equiv_of_pi_lequiv_pi (to_lin'_of_inv hMM' hM'M)

lemma det_comm [decidable_eq n] (M N : matrix n n A) : det (M ⬝ N) = det (N ⬝ M) :=
by rw [det_mul, det_mul, mul_comm]

/-- If there exists a two-sided inverse `M'` for `M` (indexed differently),
then `det (N ⬝ M) = det (M ⬝ N)`. -/
lemma det_comm' [is_domain A] [decidable_eq m] [decidable_eq n]
  {M : matrix n m A} {N : matrix m n A} {M' : matrix m n A}
  (hMM' : M ⬝ M' = 1) (hM'M : M' ⬝ M = 1) :
  det (M ⬝ N) = det (N ⬝ M) :=
-- Although `m` and `n` are different a priori, we will show they have the same cardinality.
-- This turns the problem into one for square matrices, which is easy.
let e := index_equiv_of_inv hMM' hM'M in
by rw [← det_minor_equiv_self e, minor_mul_equiv _ _ _ (equiv.refl n) _, det_comm,
  ← minor_mul_equiv, equiv.coe_refl, minor_id_id]

/-- If `M'` is a two-sided inverse for `M` (indexed differently), `det (M ⬝ N ⬝ M') = det N`. -/
lemma det_conj [is_domain A] [decidable_eq m] [decidable_eq n]
  {M : matrix m n A} {M' : matrix n m A} {N : matrix n n A}
  (hMM' : M ⬝ M' = 1) (hM'M : M' ⬝ M = 1) :
  det (M ⬝ N ⬝ M') = det N :=
by rw [← det_comm' hM'M hMM', ← matrix.mul_assoc, hM'M, matrix.one_mul]

end matrix

end conjugate

namespace linear_map

/-! ### Determinant of a linear map -/

variables {A : Type*} [comm_ring A] [is_domain A] [module A M]
variables {κ : Type*} [fintype κ]

/-- The determinant of `linear_map.to_matrix` does not depend on the choice of basis. -/
lemma det_to_matrix_eq_det_to_matrix [decidable_eq κ]
  (b : basis ι A M) (c : basis κ A M) (f : M →ₗ[A] M) :
  det (linear_map.to_matrix b b f) = det (linear_map.to_matrix c c f) :=
by rw [← linear_map_to_matrix_mul_basis_to_matrix c b c,
       ← basis_to_matrix_mul_linear_map_to_matrix b c b,
       matrix.det_conj]; rw [basis.to_matrix_mul_to_matrix, basis.to_matrix_self]

/-- The determinant of an endomorphism given a basis.

See `linear_map.det` for a version that populates the basis non-computably.

Although the `trunc (basis ι A M)` parameter makes it slightly more convenient to switch bases,
there is no good way to generalize over universe parameters, so we can't fully state in `det_aux`'s
type that it does not depend on the choice of basis. Instead you can use the `det_aux_def'` lemma,
or avoid mentioning a basis at all using `linear_map.det`.
-/
def det_aux : trunc (basis ι A M) → (M →ₗ[A] M) →* A :=
trunc.lift
  (λ b : basis ι A M,
    (det_monoid_hom).comp (to_matrix_alg_equiv b : (M →ₗ[A] M) →* matrix ι ι A))
  (λ b c, monoid_hom.ext $ det_to_matrix_eq_det_to_matrix b c)

/-- Unfold lemma for `det_aux`.

See also `det_aux_def'` which allows you to vary the basis.
-/
lemma det_aux_def (b : basis ι A M) (f : M →ₗ[A] M) :
  linear_map.det_aux (trunc.mk b) f = matrix.det (linear_map.to_matrix b b f) :=
rfl

-- Discourage the elaborator from unfolding `det_aux` and producing a huge term.
attribute [irreducible] linear_map.det_aux

lemma det_aux_def' {ι' : Type*} [fintype ι'] [decidable_eq ι']
  (tb : trunc $ basis ι A M) (b' : basis ι' A M) (f : M →ₗ[A] M) :
  linear_map.det_aux tb f = matrix.det (linear_map.to_matrix b' b' f) :=
by { apply trunc.induction_on tb, intro b, rw [det_aux_def, det_to_matrix_eq_det_to_matrix b b'] }

@[simp]
lemma det_aux_id (b : trunc $ basis ι A M) : linear_map.det_aux b (linear_map.id) = 1 :=
(linear_map.det_aux b).map_one

@[simp]
lemma det_aux_comp (b : trunc $ basis ι A M) (f g : M →ₗ[A] M) :
  linear_map.det_aux b (f.comp g) = linear_map.det_aux b f * linear_map.det_aux b g :=
(linear_map.det_aux b).map_mul f g

section
open_locale classical

-- Discourage the elaborator from unfolding `det` and producing a huge term by marking it
-- as irreducible.
/-- The determinant of an endomorphism independent of basis.

If there is no finite basis on `M`, the result is `1` instead.
-/
@[irreducible] protected def det : (M →ₗ[A] M) →* A :=
if H : ∃ (s : finset M), nonempty (basis s A M)
then linear_map.det_aux (trunc.mk H.some_spec.some)
else 1

lemma coe_det [decidable_eq M] : ⇑(linear_map.det : (M →ₗ[A] M) →* A) =
  if H : ∃ (s : finset M), nonempty (basis s A M)
  then linear_map.det_aux (trunc.mk H.some_spec.some)
  else 1 :=
by { ext, unfold linear_map.det,
     split_ifs,
     { congr }, -- use the correct `decidable_eq` instance
     refl }

end

-- Auxiliary lemma, the `simp` normal form goes in the other direction
-- (using `linear_map.det_to_matrix`)
lemma det_eq_det_to_matrix_of_finset [decidable_eq M]
  {s : finset M} (b : basis s A M) (f : M →ₗ[A] M) :
  f.det = matrix.det (linear_map.to_matrix b b f) :=
have ∃ (s : finset M), nonempty (basis s A M),
from ⟨s, ⟨b⟩⟩,
by rw [linear_map.coe_det, dif_pos, det_aux_def' _ b]; assumption

@[simp] lemma det_to_matrix
  (b : basis ι A M) (f : M →ₗ[A] M) :
  matrix.det (to_matrix b b f) = f.det :=
by { haveI := classical.dec_eq M,
     rw [det_eq_det_to_matrix_of_finset b.reindex_finset_range, det_to_matrix_eq_det_to_matrix b] }

@[simp] lemma det_to_matrix' {ι : Type*} [fintype ι] [decidable_eq ι]
  (f : (ι → A) →ₗ[A] (ι → A)) :
  det f.to_matrix' = f.det :=
by simp [← to_matrix_eq_to_matrix']

/-- To show `P f.det` it suffices to consider `P (to_matrix _ _ f).det` and `P 1`. -/
@[elab_as_eliminator]
lemma det_cases [decidable_eq M] {P : A → Prop} (f : M →ₗ[A] M)
  (hb : ∀ (s : finset M) (b : basis s A M), P (to_matrix b b f).det) (h1 : P 1) :
  P f.det :=
begin
  unfold linear_map.det,
  split_ifs with h,
  { convert hb _ h.some_spec.some,
    apply det_aux_def' },
  { exact h1 }
end

@[simp]
lemma det_comp (f g : M →ₗ[A] M) : (f.comp g).det = f.det * g.det :=
linear_map.det.map_mul f g

@[simp]
lemma det_id : (linear_map.id : M →ₗ[A] M).det = 1 :=
linear_map.det.map_one

/-- Multiplying a map by a scalar `c` multiplies its determinant by `c ^ dim M`. -/
@[simp] lemma det_smul {𝕜 : Type*} [field 𝕜] {M : Type*} [add_comm_group M] [module 𝕜 M]
  (c : 𝕜) (f : M →ₗ[𝕜] M) :
  linear_map.det (c • f) = c ^ (finite_dimensional.finrank 𝕜 M) * linear_map.det f :=
begin
  by_cases H : ∃ (s : finset M), nonempty (basis s 𝕜 M),
  { haveI : finite_dimensional 𝕜 M,
    { rcases H with ⟨s, ⟨hs⟩⟩, exact finite_dimensional.of_finset_basis hs },
    simp only [← det_to_matrix (finite_dimensional.fin_basis 𝕜 M), linear_equiv.map_smul,
              fintype.card_fin, det_smul] },
  { classical,
    have : finite_dimensional.finrank 𝕜 M = 0 := finrank_eq_zero_of_not_exists_basis H,
    simp [coe_det, H, this] }
end

lemma det_zero' {ι : Type*} [fintype ι] [nonempty ι] (b : basis ι A M) :
  linear_map.det (0 : M →ₗ[A] M) = 0 :=
by { haveI := classical.dec_eq ι,
     rw [← det_to_matrix b, linear_equiv.map_zero, det_zero],
     assumption }

/-- In a finite-dimensional vector space, the zero map has determinant `1` in dimension `0`,
and `0` otherwise. -/
@[simp] lemma det_zero {𝕜 : Type*} [field 𝕜] {M : Type*} [add_comm_group M] [module 𝕜 M] :
  linear_map.det (0 : M →ₗ[𝕜] M) = (0 : 𝕜) ^ (finite_dimensional.finrank 𝕜 M) :=
by simp only [← zero_smul 𝕜 (1 : M →ₗ[𝕜] M), det_smul, mul_one, monoid_hom.map_one]

/-- Conjugating a linear map by a linear equiv does not change its determinant. -/
@[simp] lemma det_conj {N : Type*} [add_comm_group N] [module A N]
  (f : M →ₗ[A] M) (e : M ≃ₗ[A] N) :
  linear_map.det ((e : M →ₗ[A] N) ∘ₗ (f ∘ₗ (e.symm : N →ₗ[A] M))) = linear_map.det f :=
begin
  classical,
  by_cases H : ∃ (s : finset M), nonempty (basis s A M),
  { rcases H with ⟨s, ⟨b⟩⟩,
    rw [← det_to_matrix b f, ← det_to_matrix (b.map e), to_matrix_comp (b.map e) b (b.map e),
        to_matrix_comp (b.map e) b b, ← matrix.mul_assoc, matrix.det_conj],
    { rw [← to_matrix_comp, linear_equiv.comp_coe, e.symm_trans_self,
          linear_equiv.refl_to_linear_map, to_matrix_id] },
    { rw [← to_matrix_comp, linear_equiv.comp_coe, e.self_trans_symm,
          linear_equiv.refl_to_linear_map, to_matrix_id] } },
  { have H' : ¬ (∃ (t : finset N), nonempty (basis t A N)),
    { contrapose! H,
      rcases H with ⟨s, ⟨b⟩⟩,
      exact ⟨_, ⟨(b.map e.symm).reindex_finset_range⟩⟩ },
    simp only [coe_det, H, H', pi.one_apply, dif_neg, not_false_iff] }
end

end linear_map

-- Cannot be stated using `linear_map.det` because `f` is not an endomorphism.
lemma linear_equiv.is_unit_det (f : M ≃ₗ[R] M') (v : basis ι R M) (v' : basis ι R M') :
  is_unit (linear_map.to_matrix v v' f).det :=
begin
  apply is_unit_det_of_left_inverse,
  simpa using (linear_map.to_matrix_comp v v' v f.symm f).symm
end

/-- Specialization of `linear_equiv.is_unit_det` -/
lemma linear_equiv.is_unit_det' {A : Type*} [comm_ring A] [is_domain A] [module A M]
  (f : M ≃ₗ[A] M) : is_unit (linear_map.det (f : M →ₗ[A] M)) :=
by haveI := classical.dec_eq M; exact
(f : M →ₗ[A] M).det_cases (λ s b, f.is_unit_det _ _) is_unit_one

/-- Builds a linear equivalence from a linear map whose determinant in some bases is a unit. -/
@[simps]
def linear_equiv.of_is_unit_det {f : M →ₗ[R] M'} {v : basis ι R M} {v' : basis ι R M'}
  (h : is_unit (linear_map.to_matrix v v' f).det) : M ≃ₗ[R] M' :=
{ to_fun := f,
  map_add' := f.map_add,
  map_smul' := f.map_smul,
  inv_fun := to_lin v' v (to_matrix v v' f)⁻¹,
  left_inv := λ x,
    calc to_lin v' v (to_matrix v v' f)⁻¹ (f x)
        = to_lin v v ((to_matrix v v' f)⁻¹ ⬝ to_matrix v v' f) x :
      by { rw [to_lin_mul v v' v, to_lin_to_matrix, linear_map.comp_apply] }
    ... = x : by simp [h],
  right_inv := λ x,
    calc f (to_lin v' v (to_matrix v v' f)⁻¹ x)
        = to_lin v' v' (to_matrix v v' f ⬝ (to_matrix v v' f)⁻¹) x :
      by { rw [to_lin_mul v' v v', linear_map.comp_apply, to_lin_to_matrix v v'] }
    ... = x : by simp [h] }

/-- Builds a linear equivalence from a linear map on a finite-dimensional vector space whose
determinant is nonzero. -/
@[reducible] def linear_map.equiv_of_det_ne_zero
  {𝕜 : Type*} [field 𝕜] {M : Type*} [add_comm_group M] [module 𝕜 M]
  [finite_dimensional 𝕜 M] (f : M →ₗ[𝕜] M) (hf : linear_map.det f ≠ 0) :
  M ≃ₗ[𝕜] M :=
have is_unit (linear_map.to_matrix (finite_dimensional.fin_basis 𝕜 M)
  (finite_dimensional.fin_basis 𝕜 M) f).det :=
    by simp only [linear_map.det_to_matrix, is_unit_iff_ne_zero.2 hf],
linear_equiv.of_is_unit_det this

/-- The determinant of a family of vectors with respect to some basis, as an alternating
multilinear map. -/
def basis.det : alternating_map R M R ι :=
{ to_fun := λ v, det (e.to_matrix v),
  map_add' := begin
    intros v i x y,
    simp only [e.to_matrix_update, linear_equiv.map_add],
    apply det_update_column_add
  end,
  map_smul' := begin
    intros u i c x,
    simp only [e.to_matrix_update, algebra.id.smul_eq_mul, linear_equiv.map_smul],
    apply det_update_column_smul
  end,
  map_eq_zero_of_eq' := begin
    intros v i j h hij,
    rw [←function.update_eq_self i v, h, ←det_transpose, e.to_matrix_update,
        ←update_row_transpose, ←e.to_matrix_transpose_apply],
    apply det_zero_of_row_eq hij,
    rw [update_row_ne hij.symm, update_row_self],
  end }

lemma basis.det_apply (v : ι → M) : e.det v = det (e.to_matrix v) := rfl

lemma basis.det_self : e.det e = 1 :=
by simp [e.det_apply]

/-- `basis.det` is not the zero map. -/
lemma basis.det_ne_zero [nontrivial R] : e.det ≠ 0 :=
λ h, by simpa [h] using e.det_self

lemma is_basis_iff_det {v : ι → M} :
  linear_independent R v ∧ span R (set.range v) = ⊤ ↔ is_unit (e.det v) :=
begin
  split,
  { rintro ⟨hli, hspan⟩,
    set v' := basis.mk hli hspan with v'_eq,
    rw e.det_apply,
    convert linear_equiv.is_unit_det (linear_equiv.refl _ _) v' e using 2,
    ext i j,
    simp },
  { intro h,
    rw [basis.det_apply, basis.to_matrix_eq_to_matrix_constr] at h,
    set v' := basis.map e (linear_equiv.of_is_unit_det h) with v'_def,
    have : ⇑ v' = v,
    { ext i, rw [v'_def, basis.map_apply, linear_equiv.of_is_unit_det_apply, e.constr_basis] },
    rw ← this,
    exact ⟨v'.linear_independent, v'.span_eq⟩ },
end

lemma basis.is_unit_det (e' : basis ι R M) : is_unit (e.det e') :=
(is_basis_iff_det e).mp ⟨e'.linear_independent, e'.span_eq⟩

/-- Any alternating map to `R` where `ι` has the cardinality of a basis equals the determinant
map with respect to that basis, multiplied by the value of that alternating map on that basis. -/
lemma alternating_map.eq_smul_basis_det (f : alternating_map R M R ι) : f = f e • e.det :=
begin
  refine basis.ext_alternating e (λ i h, _),
  let σ : equiv.perm ι := equiv.of_bijective i (fintype.injective_iff_bijective.1 h),
  change f (e ∘ σ) = (f e • e.det) (e ∘ σ),
  simp [alternating_map.map_perm, basis.det_self]
end

variables {A : Type*} [comm_ring A] [is_domain A] [module A M]

@[simp] lemma basis.det_comp (e : basis ι A M) (f : M →ₗ[A] M) (v : ι → M) :
  e.det (f ∘ v) = f.det * e.det v :=
by { rw [basis.det_apply, basis.det_apply, ← f.det_to_matrix e, ← matrix.det_mul,
         e.to_matrix_eq_to_matrix_constr (f ∘ v), e.to_matrix_eq_to_matrix_constr v,
         ← to_matrix_comp, e.constr_comp] }

lemma basis.det_reindex {ι' : Type*} [fintype ι'] [decidable_eq ι']
  (b : basis ι R M) (v : ι' → M) (e : ι ≃ ι') :
  (b.reindex e).det v = b.det (v ∘ e) :=
by rw [basis.det_apply, basis.to_matrix_reindex', det_reindex_alg_equiv, basis.det_apply]

lemma basis.det_reindex_symm {ι' : Type*} [fintype ι'] [decidable_eq ι']
  (b : basis ι R M) (v : ι → M) (e : ι' ≃ ι) :
  (b.reindex e.symm).det (v ∘ e) = b.det v :=
by rw [basis.det_reindex, function.comp.assoc, e.self_comp_symm, function.comp.right_id]

@[simp]
lemma basis.det_map (b : basis ι R M) (f : M ≃ₗ[R] M') (v : ι → M') :
  (b.map f).det v = b.det (f.symm ∘ v) :=
by { rw [basis.det_apply, basis.to_matrix_map, basis.det_apply] }

@[simp] lemma pi.basis_fun_det : (pi.basis_fun R ι).det = matrix.det_row_alternating :=
begin
  ext M,
  rw [basis.det_apply, basis.coe_pi_basis_fun.to_matrix_eq_transpose, det_transpose],
end

/-- If we fix a background basis `e`, then for any other basis `v`, we can characterise the
coordinates provided by `v` in terms of determinants relative to `e`. -/
lemma basis.det_smul_mk_coord_eq_det_update {v : ι → M}
  (hli : linear_independent R v) (hsp : span R (range v) = ⊤) (i : ι) :
  (e.det v) • (basis.mk hli hsp).coord i = e.det.to_multilinear_map.to_linear_map v i :=
begin
  apply (basis.mk hli hsp).ext,
  intros k,
  rcases eq_or_ne k i with rfl | hik;
  simp only [algebra.id.smul_eq_mul, basis.coe_mk, linear_map.smul_apply, linear_map.coe_mk,
    multilinear_map.to_linear_map_apply],
  { rw [basis.mk_coord_apply_eq, mul_one, update_eq_self], congr, },
  { rw [basis.mk_coord_apply_ne hik, mul_zero, eq_comm],
    exact e.det.map_eq_zero_of_eq _ (by simp [hik, function.update_apply]) hik, },
end


class category (C : Type) :=
( hom : C → C → Type )
( id : (X : C) → hom X X )
( comp : {X Y Z : C} → hom X Y → hom Y Z → hom X Z )
( id_comp {X Y : C} (f : hom X Y) : comp (id X) f = f )
( comp_id {X Y : C} (f : hom X Y) : comp f (id Y) = f )
( assoc {W X Y Z : C} (f : hom W X) (g : hom X Y) (h : hom Y Z) :
    comp (comp f g) h = comp f (comp g h) )

notation " 𝟙 " => category.id
infixr: 80 " ≫ " => category.comp
infixr: 10 " ⟶ " => category.hom

variable (C : Type) [category C]

inductive prod_coprod : Type
| of_cat' : C → prod_coprod
| prod : prod_coprod → prod_coprod → prod_coprod
| coprod : prod_coprod → prod_coprod → prod_coprod

variable {C}

namespace prod_coprod

@[simp] def size : prod_coprod C → Nat
| of_cat' _ => 1
| prod X Y => size X + size Y + 1
| coprod X Y => size X + size Y + 1

inductive syn : (X Y : prod_coprod C) → Type
| of_cat {X Y : C} : (X ⟶ Y) → syn (of_cat' X) (of_cat' Y)
| prod_mk {X Y Z : prod_coprod C} : syn X Y → syn X Z → syn X (Y.prod Z)
| fst {X Y : prod_coprod C} : syn (X.prod Y) X
| snd {X Y : prod_coprod C} : syn (X.prod Y) Y
| coprod_mk {X Y Z : prod_coprod C} : syn X Z → syn Y Z → syn (X.coprod Y) Z
| inl {X Y : prod_coprod C} : syn X (X.coprod Y)
| inr {X Y : prod_coprod C} : syn Y (X.coprod Y)
| id (X : prod_coprod C) : syn X X
| comp {X Y Z : prod_coprod C} : syn X Y → syn Y Z → syn X Z

namespace syn

inductive rel : {X Y : prod_coprod C} → syn X Y → syn X Y → Prop
| refl {X Y : prod_coprod C} (f : syn X Y) : rel f f
| symm {X Y : prod_coprod C} {f g : syn X Y} : rel f g → rel g f
| trans {X Y : prod_coprod C} {f g h : syn X Y} : rel f g → rel g h → rel f h
| comp_congr {X Y Z : prod_coprod C} {f₁ f₂ : syn X Y} {g₁ g₂ : syn Y Z} :
  rel f₁ f₂ → rel g₁ g₂ → rel (f₁.comp g₁) (f₂.comp g₂)
| prod_mk_congr {X Y Z : prod_coprod C} {f₁ f₂ : syn X Y} {g₁ g₂ : syn X Z} :
  rel f₁ f₂ → rel g₁ g₂ → rel (f₁.prod_mk g₁) (f₂.prod_mk g₂)
| coprod_mk_congr {X Y Z : prod_coprod C} {f₁ f₂ : syn X Z} {g₁ g₂ : syn Y Z} :
  rel f₁ f₂ → rel g₁ g₂ → rel (f₁.coprod_mk g₁) (f₂.coprod_mk g₂)
| id_comp {X Y : prod_coprod C} (f : syn X Y) : rel ((syn.id X).comp f) f
| comp_id {X Y : prod_coprod C} (f : syn X Y) : rel (f.comp (syn.id Y)) f
| assoc {W X Y Z : prod_coprod C} (f : syn W X) (g : syn X Y) (h : syn Y Z) :
  rel ((f.comp g).comp h) (f.comp (g.comp h))
| of_cat_id {X : C} : rel (syn.of_cat (𝟙 X)) (syn.id (of_cat' X))
| of_cat_comp {X Y Z : C} (f : X ⟶ Y) (g : Y ⟶ Z) :
  rel (syn.of_cat (f ≫ g)) (syn.comp (syn.of_cat f) (syn.of_cat g))
| mk_fst_comp {X Y Z : prod_coprod C} (f : syn X Y) (g : syn X Z) :
  rel (syn.comp (syn.prod_mk f g) syn.fst) f
| mk_snd_comp {X Y Z : prod_coprod C} (f : syn X Y) (g : syn X Z) :
  rel (syn.comp (syn.prod_mk f g) syn.snd) g
| prod_eta {X Y Z : prod_coprod C} (f : syn X (Y.prod Z)) :
  rel (syn.prod_mk (f.comp syn.fst) (f.comp syn.snd)) f
| inl_comp_mk {X Y Z : prod_coprod C} (f : syn X Z) (g : syn Y Z) :
  rel (syn.comp syn.inl (syn.coprod_mk f g)) f
| inr_comp_mk {X Y Z : prod_coprod C} (f : syn X Z) (g : syn Y Z) :
  rel (syn.comp syn.inr (syn.coprod_mk f g)) g
| coprod_eta {X Y Z : prod_coprod C} (f : syn (X.coprod Y) Z) :
  rel (syn.coprod_mk (syn.inl.comp f) (syn.inr.comp f)) f

infixl:50 " ♥ " => rel

instance : Trans (@rel C _ X Y) (@rel C _ X Y) (@rel C _ X Y) where
  trans := rel.trans

theorem rel_prod {X Y Z : prod_coprod C} {f g : syn X (Y.prod Z)}
  (h₁ : rel (f.comp syn.fst) (g.comp syn.fst))
  (h₂ : rel (f.comp syn.snd) (g.comp syn.snd)) :
  rel f g :=
rel.trans (rel.symm (rel.prod_eta f)) (rel.trans (rel.prod_mk_congr h₁ h₂) (rel.prod_eta g))

theorem rel_coprod {X Y Z : prod_coprod C} {f g : syn (X.coprod Y) Z}
  (h₁ : rel (syn.inl.comp f) (syn.inl.comp g))
  (h₂ : rel (syn.inr.comp f) (syn.inr.comp g)) :
  rel f g :=
rel.trans (rel.symm (rel.coprod_eta f)) (rel.trans (rel.coprod_mk_congr h₁ h₂) (rel.coprod_eta g))

end syn

inductive norm_hom : (X Y : prod_coprod C) → Type
| of_cat {X Y : C} (f : X ⟶ Y) : norm_hom (of_cat' X) (of_cat' Y)
| coprod_mk {X Y Z : prod_coprod C} (f : norm_hom X Z) (g : norm_hom Y Z) :
  norm_hom (X.coprod Y) Z
| prod_mk {X Y Z : prod_coprod C} (f : norm_hom X Y) (g : norm_hom X Z) :
  norm_hom X (prod Y Z)
| comp_inl {X Y Z : prod_coprod C} (f : norm_hom X Y) :
  norm_hom X (coprod Y Z)
| comp_inr {X Y Z : prod_coprod C} (f : norm_hom X Z) :
  norm_hom X (coprod Y Z)
| fst_comp {X Y Z : prod_coprod C} (f : norm_hom X Z) :
  norm_hom (prod X Y) Z
| snd_comp {X Y Z : prod_coprod C} (f : norm_hom Y Z) :
  norm_hom (prod X Y) Z

namespace norm_hom

inductive rel : {X Y : prod_coprod C} → norm_hom X Y → norm_hom X Y → Prop
| refl {X Y : prod_coprod C} (f : norm_hom X Y) : rel f f
| symm {X Y : prod_coprod C} {f g : norm_hom X Y} : rel g f → rel f g
| trans {X Y : prod_coprod C} {f g h : norm_hom X Y} : rel f g → rel g h → rel f h
| coprod_mk_congr {X Y Z : prod_coprod C} {f₁ f₂ : norm_hom X Z} {g₁ g₂ : norm_hom Y Z} :
  rel f₁ f₂ → rel g₁ g₂ → rel (coprod_mk f₁ g₁) (coprod_mk f₂ g₂)
| prod_mk_congr {X Y Z : prod_coprod C} {f₁ f₂ : norm_hom X Y} {g₁ g₂ : norm_hom X Z} :
  rel f₁ f₂ → rel g₁ g₂ → rel (prod_mk f₁ g₁) (prod_mk f₂ g₂)
| comp_inl_congr {X Y Z : prod_coprod C} {f₁ f₂ : norm_hom X Y} :
  rel f₁ f₂ → rel (comp_inl f₁ : norm_hom X (coprod Y Z)) (comp_inl f₂)
| comp_inr_congr {X Y Z : prod_coprod C} {f₁ f₂ : norm_hom X Z} :
  rel f₁ f₂ → rel (comp_inr f₁ : norm_hom X (coprod Y Z)) (comp_inr f₂)
| fst_comp_congr {X Y Z : prod_coprod C} {f₁ f₂ : norm_hom X Z} :
  rel f₁ f₂ → rel (fst_comp f₁ : norm_hom (prod X Y) Z) (fst_comp f₂)
| snd_comp_congr {X Y Z : prod_coprod C} {f₁ f₂ : norm_hom Y Z} :
  rel f₁ f₂ → rel (snd_comp f₁ : norm_hom (prod X Y) Z) (snd_comp f₂)
| fst_comp_prod_mk {W X Y Z : prod_coprod C} (f : norm_hom X Y) (g : norm_hom X Z) :
  rel (fst_comp (prod_mk f g) : norm_hom (prod X W) (prod Y Z)) (prod_mk f.fst_comp g.fst_comp)
| snd_comp_prod_mk {W X Y Z : prod_coprod C} (f : norm_hom X Y) (g : norm_hom X Z) :
  rel (snd_comp (prod_mk f g) : norm_hom (prod W X) (prod Y Z)) (prod_mk f.snd_comp g.snd_comp)
| comp_inl_coprod_mk {W X Y Z : prod_coprod C} (f : norm_hom W Y) (g : norm_hom X Y) :
  rel (comp_inl (coprod_mk f g) : norm_hom (coprod W X) (coprod Y Z))
    (coprod_mk f.comp_inl g.comp_inl)
| comp_inr_coprod_mk {W X Y Z : prod_coprod C} (f : norm_hom W Y) (g : norm_hom X Y) :
  rel (comp_inr (coprod_mk f g) : norm_hom (coprod W X) (coprod Z Y))
    (coprod_mk f.comp_inr g.comp_inr)
| fst_comp_comp_inl {W X Y Z : prod_coprod C} (f : norm_hom W Y) :
  rel (f.fst_comp.comp_inl : norm_hom (prod W X) (coprod Y Z)) f.comp_inl.fst_comp
| snd_comp_comp_inl {W X Y Z : prod_coprod C} (f : norm_hom X Y) :
  rel (f.snd_comp.comp_inl : norm_hom (prod W X) (coprod Y Z)) f.comp_inl.snd_comp
| fst_comp_comp_inr {W X Y Z : prod_coprod C} (f : norm_hom W Z) :
  rel (f.fst_comp.comp_inr : norm_hom (prod W X) (coprod Y Z)) f.comp_inr.fst_comp
| snd_comp_comp_inr {W X Y Z : prod_coprod C} (f : norm_hom X Z) :
  rel (f.snd_comp.comp_inr : norm_hom (prod W X) (coprod Y Z)) f.comp_inr.snd_comp

def to_inj : {X Y Z : prod_coprod C} → (f : norm_hom X (coprod Y Z)) →
  Option ((norm_hom X Y) ⊕ (norm_hom X Z))
| _, _, _, comp_inl f => some (Sum.inl f)
| _, _, _, comp_inr f => some (Sum.inr f)
| _, _, _, fst_comp f =>
  match to_inj f with
  | none => none
  | some (Sum.inl f) => some (Sum.inl (fst_comp f))
  | some (Sum.inr f) => some (Sum.inr (fst_comp f))
| _, _, _, snd_comp f =>
  match to_inj f with
  | none => none
  | some (Sum.inl f) => some (Sum.inl (snd_comp f))
  | some (Sum.inr f) => some (Sum.inr (snd_comp f))
| _, _, _, coprod_mk f g =>
  match to_inj f, to_inj g with
  | some (Sum.inl f), some (Sum.inl g) => some (Sum.inl (coprod_mk f g))
  | some (Sum.inr f), some (Sum.inr g) => some (Sum.inr (coprod_mk f g))
  | _, _ => none

theorem to_inj_eq_inl : {X Y Z : prod_coprod C} → {f : norm_hom X (coprod Y Z)} →
  {g : norm_hom X Y} → to_inj f = some (Sum.inl g) → rel f g.comp_inl
| _, _, _, comp_inl f, g, h => by
  simp [to_inj] at h
  simp [h]
  exact rel.refl _
  | _, _, _, comp_inr f, g, h => by
  simp [to_inj] at h
| _, _, _, snd_comp f, g, h =>
  have hi : ∃ i, to_inj f = some (Sum.inl i) := by
  { simp [to_inj] at h
    revert h
    match to_inj f with
    | some (Sum.inl i) => intro h; exact ⟨i, rfl⟩
    | some (Sum.inr _) => simp
    | none => simp }
  match hi with
  | ⟨i, hi⟩ => by
  simp [hi, to_inj] at h
  rw [← h]
  exact rel.trans (rel.snd_comp_congr (to_inj_eq_inl hi))
    (rel.snd_comp_comp_inl i).symm
| _, _, _, fst_comp f, g, h =>
  have hi : ∃ i, to_inj f = some (Sum.inl i) := by
  { simp [to_inj] at h
    revert h
    match to_inj f with
    | some (Sum.inl i) => intro h; exact ⟨i, rfl⟩
    | some (Sum.inr _) => simp
    | none => simp }
  match hi with
  | ⟨i, hi⟩ => by
  simp [hi, to_inj] at h
  rw [← h]
  exact rel.trans (rel.fst_comp_congr (to_inj_eq_inl hi))
    (rel.fst_comp_comp_inl i).symm
| _, _, _, coprod_mk f g, i, h =>
  have hi : ∃ f' g', to_inj f = some (Sum.inl f') ∧ to_inj g = some (Sum.inl g') := by
  { simp [to_inj] at h
    revert h
    match to_inj f, to_inj g with
    | some (Sum.inl f'), some (Sum.inl g') => intro h; exact ⟨f', g', rfl, rfl⟩
    | some (Sum.inr _), some (Sum.inr _) => simp
    | none, _ => simp
    | _, none => simp
    | some (Sum.inl _), some (Sum.inr _) => simp
    | some (Sum.inr _), some (Sum.inl _) => simp }
  match hi with
  | ⟨f', g', hf, hg⟩ => by
  simp [hf, hg, to_inj] at h
  rw [← h]
  exact rel.trans (rel.coprod_mk_congr (to_inj_eq_inl hf) (to_inj_eq_inl hg))
    (rel.comp_inl_coprod_mk _ _).symm

theorem to_inj_eq_inr : {X Y Z : prod_coprod C} → {f : norm_hom X (coprod Y Z)} →
  {g : norm_hom X Z} → to_inj f = some (Sum.inr g) → rel f g.comp_inr
| _, _, _, comp_inr f, g, h => by
  simp [to_inj] at h
  simp [h]
  exact rel.refl _
  | _, _, _, comp_inl f, g, h => by
  simp [to_inj] at h
| _, _, _, snd_comp f, g, h =>
  have hi : ∃ i, to_inj f = some (Sum.inr i) := by
  { simp [to_inj] at h
    revert h
    match to_inj f with
    | some (Sum.inr i) => intro h; exact ⟨i, rfl⟩
    | some (Sum.inl _) => simp
    | none => simp }
  match hi with
  | ⟨i, hi⟩ => by
  simp [hi, to_inj] at h
  rw [← h]
  exact rel.trans (rel.snd_comp_congr (to_inj_eq_inr hi))
    (rel.snd_comp_comp_inr i).symm
| _, _, _, fst_comp f, g, h =>
  have hi : ∃ i, to_inj f = some (Sum.inr i) := by
  { simp [to_inj] at h
    revert h
    match to_inj f with
    | some (Sum.inr i) => intro h; exact ⟨i, rfl⟩
    | some (Sum.inl _) => simp
    | none => simp }
  match hi with
  | ⟨i, hi⟩ => by
  simp [hi, to_inj] at h
  rw [← h]
  exact rel.trans (rel.fst_comp_congr (to_inj_eq_inr hi))
    (rel.fst_comp_comp_inr i).symm
| _, _, _, coprod_mk f g, i, h =>
  have hi : ∃ f' g', to_inj f = some (Sum.inr f') ∧ to_inj g = some (Sum.inr g') := by
  { simp [to_inj] at h
    revert h
    match to_inj f, to_inj g with
    | some (Sum.inr f'), some (Sum.inr g') => intro _; exact ⟨f', g', rfl, rfl⟩
    | some (Sum.inl _), some (Sum.inl _) => simp
    | none, _ => simp
    | _, none => simp
    | some (Sum.inr _), some (Sum.inl _) => simp
    | some (Sum.inl _), some (Sum.inr _) => simp }
  match hi with
  | ⟨f', g', hf, hg⟩ => by
  simp [hf, hg, to_inj] at h
  rw [← h]
  exact rel.trans (rel.coprod_mk_congr (to_inj_eq_inr hf) (to_inj_eq_inr hg))
    (rel.comp_inr_coprod_mk _ _).symm

theorem to_inj_eq_none {X Y Z : prod_coprod C} {f : norm_hom X (coprod Y Z)}
  (hf : to_inj f = none) {g : norm_hom X Z} : ¬rel f g.comp_inr := by
intro h
cases h
simp at hf

end norm_hom


/-
Copyright (c) 2019 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.linear_algebra.finite_dimensional
import Mathlib.ring_theory.integral_closure
import Mathlib.data.polynomial.integral_normalization
import Mathlib.PostPort

universes u v u_1 u_2 u_3 u_4

namespace Mathlib

/-!
# Algebraic elements and algebraic extensions

An element of an R-algebra is algebraic over R if it is the root of a nonzero polynomial.
An R-algebra is algebraic over R if and only if all its elements are algebraic over R.
The main result in this file proves transitivity of algebraicity:
a tower of algebraic field extensions is algebraic.
-/

/-- An element of an R-algebra is algebraic over R if it is the root of a nonzero polynomial. -/
def is_algebraic (R : Type u) {A : Type v} [comm_ring R] [ring A] [algebra R A] (x : A) :=
  ∃ (p : polynomial R), p ≠ 0 ∧ coe_fn (polynomial.aeval x) p = 0

/-- A subalgebra is algebraic if all its elements are algebraic. -/
def subalgebra.is_algebraic {R : Type u} {A : Type v} [comm_ring R] [ring A] [algebra R A]
    (S : subalgebra R A) :=
  ∀ (x : A), x ∈ S → is_algebraic R x

/-- An algebra is algebraic if all its elements are algebraic. -/
def algebra.is_algebraic (R : Type u) (A : Type v) [comm_ring R] [ring A] [algebra R A] :=
  ∀ (x : A), is_algebraic R x

/-- A subalgebra is algebraic if and only if it is algebraic an algebra. -/
theorem subalgebra.is_algebraic_iff {R : Type u} {A : Type v} [comm_ring R] [ring A] [algebra R A]
    (S : subalgebra R A) : subalgebra.is_algebraic S ↔ algebra.is_algebraic R ↥S :=
  sorry

/-- An algebra is algebraic if and only if it is algebraic as a subalgebra. -/
theorem algebra.is_algebraic_iff {R : Type u} {A : Type v} [comm_ring R] [ring A] [algebra R A] :
    algebra.is_algebraic R A ↔ subalgebra.is_algebraic ⊤ :=
  sorry

/-- An integral element of an algebra is algebraic.-/
theorem is_integral.is_algebraic (R : Type u) {A : Type v} [comm_ring R] [nontrivial R] [ring A]
    [algebra R A] {x : A} (h : is_integral R x) : is_algebraic R x :=
  sorry

/-- An element of an algebra over a field is algebraic if and only if it is integral.-/
theorem is_algebraic_iff_is_integral (K : Type u) {A : Type v} [field K] [ring A] [algebra K A]
    {x : A} : is_algebraic K x ↔ is_integral K x :=
  sorry

theorem is_algebraic_iff_is_integral' (K : Type u) {A : Type v} [field K] [ring A] [algebra K A] :
    algebra.is_algebraic K A ↔ algebra.is_integral K A :=
  { mp :=
      fun (h : algebra.is_algebraic K A) (x : A) => iff.mp (is_algebraic_iff_is_integral K) (h x),
    mpr :=
      fun (h : algebra.is_integral K A) (x : A) => iff.mpr (is_algebraic_iff_is_integral K) (h x) }

namespace algebra

/-- If L is an algebraic field extension of K and A is an algebraic algebra over L,
then A is algebraic over K. -/
theorem is_algebraic_trans {K : Type u_1} {L : Type u_2} {A : Type u_3} [field K] [field L]
    [comm_ring A] [algebra K L] [algebra L A] [algebra K A] [is_scalar_tower K L A]
    (L_alg : is_algebraic K L) (A_alg : is_algebraic L A) : is_algebraic K A :=
  sorry

/-- A field extension is algebraic if it is finite. -/
theorem is_algebraic_of_finite {K : Type u_1} {L : Type u_2} [field K] [field L] [algebra K L]
    [finite : finite_dimensional K L] : is_algebraic K L :=
  fun (x : L) =>
    iff.mpr (is_algebraic_iff_is_integral K)
      (is_integral_of_submodule_noetherian ⊤
        (is_noetherian_of_submodule_of_noetherian K L (↑⊤) finite) x mem_top)

end algebra

theorem exists_integral_multiple {R : Type u_1} {S : Type u_2} [integral_domain R] [comm_ring S]
    [algebra R S] {z : S} (hz : is_algebraic R z)
    (inj : ∀ (x : R), coe_fn (algebra_map R S) x = 0 → x = 0) :
    ∃ (x : ↥(integral_closure R S)), ∃ (y : ↥(integral_closure R S)), ∃ (H : y ≠ 0), z * ↑y = ↑x :=
  sorry

theorem inv_eq_of_aeval_div_X_ne_zero {K : Type u_3} {L : Type u_4} [field K] [field L]
    [algebra K L] {x : L} {p : polynomial K}
    (aeval_ne : coe_fn (polynomial.aeval x) (polynomial.div_X p) ≠ 0) :
    x⁻¹ =
        coe_fn (polynomial.aeval x) (polynomial.div_X p) /
          (coe_fn (polynomial.aeval x) p - coe_fn (algebra_map K L) (polynomial.coeff p 0)) :=
  sorry

theorem inv_eq_of_root_of_coeff_zero_ne_zero {K : Type u_3} {L : Type u_4} [field K] [field L]
    [algebra K L] {x : L} {p : polynomial K} (aeval_eq : coe_fn (polynomial.aeval x) p = 0)
    (coeff_zero_ne : polynomial.coeff p 0 ≠ 0) :
    x⁻¹ =
        -(coe_fn (polynomial.aeval x) (polynomial.div_X p) /
            coe_fn (algebra_map K L) (polynomial.coeff p 0)) :=
  sorry

theorem subalgebra.inv_mem_of_root_of_coeff_zero_ne_zero {K : Type u_3} {L : Type u_4} [field K]
    [field L] [algebra K L] (A : subalgebra K L) {x : ↥A} {p : polynomial K}
    (aeval_eq : coe_fn (polynomial.aeval x) p = 0) (coeff_zero_ne : polynomial.coeff p 0 ≠ 0) :
    ↑x⁻¹ ∈ A :=
  sorry

theorem subalgebra.inv_mem_of_algebraic {K : Type u_3} {L : Type u_4} [field K] [field L]
    [algebra K L] (A : subalgebra K L) {x : ↥A} (hx : is_algebraic K ↑x) : ↑x⁻¹ ∈ A :=
  sorry

/-- In an algebraic extension L/K, an intermediate subalgebra is a field. -/
theorem subalgebra.is_field_of_algebraic {K : Type u_3} {L : Type u_4} [field K] [field L]
    [algebra K L] (A : subalgebra K L) (hKL : algebra.is_algebraic K L) : is_field ↥A :=
  sorry

end Mathlib


/-
Copyright (c) 2020 Bhavik Mehta. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Bhavik Mehta
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.category_theory.limits.shapes.binary_products
import Mathlib.category_theory.limits.preserves.basic
import Mathlib.PostPort

universes u₁ u₂ v

namespace Mathlib

/-!
# Preserving binary products

Constructions to relate the notions of preserving binary products and reflecting binary products
to concrete binary fans.

In particular, we show that `prod_comparison G X Y` is an isomorphism iff `G` preserves
the product of `X` and `Y`.
-/

namespace category_theory.limits

/--
The map of a binary fan is a limit iff the fork consisting of the mapped morphisms is a limit. This
essentially lets us commute `binary_fan.mk` with `functor.map_cone`.
-/
def is_limit_map_cone_binary_fan_equiv {C : Type u₁} [category C] {D : Type u₂} [category D]
    (G : C ⥤ D) {P : C} {X : C} {Y : C} (f : P ⟶ X) (g : P ⟶ Y) :
    is_limit (functor.map_cone G (binary_fan.mk f g)) ≃
        is_limit (binary_fan.mk (functor.map G f) (functor.map G g)) :=
  equiv.trans
    (equiv.symm
      (is_limit.postcompose_hom_equiv (diagram_iso_pair (pair X Y ⋙ G))
        (functor.map_cone G (binary_fan.mk f g))))
    (is_limit.equiv_iso_limit
      (cones.ext
        (iso.refl
          (cone.X
            (functor.obj (cones.postcompose (iso.hom (diagram_iso_pair (pair X Y ⋙ G))))
              (functor.map_cone G (binary_fan.mk f g)))))
        sorry))

/-- The property of preserving products expressed in terms of binary fans. -/
def map_is_limit_of_preserves_of_is_limit {C : Type u₁} [category C] {D : Type u₂} [category D]
    (G : C ⥤ D) {P : C} {X : C} {Y : C} (f : P ⟶ X) (g : P ⟶ Y) [preserves_limit (pair X Y) G]
    (l : is_limit (binary_fan.mk f g)) :
    is_limit (binary_fan.mk (functor.map G f) (functor.map G g)) :=
  coe_fn (is_limit_map_cone_binary_fan_equiv G f g) (preserves_limit.preserves l)

/-- The property of reflecting products expressed in terms of binary fans. -/
def is_limit_of_reflects_of_map_is_limit {C : Type u₁} [category C] {D : Type u₂} [category D]
    (G : C ⥤ D) {P : C} {X : C} {Y : C} (f : P ⟶ X) (g : P ⟶ Y) [reflects_limit (pair X Y) G]
    (l : is_limit (binary_fan.mk (functor.map G f) (functor.map G g))) :
    is_limit (binary_fan.mk f g) :=
  reflects_limit.reflects (coe_fn (equiv.symm (is_limit_map_cone_binary_fan_equiv G f g)) l)

/--
If `G` preserves binary products and `C` has them, then the binary fan constructed of the mapped
morphisms of the binary product cone is a limit.
-/
def is_limit_of_has_binary_product_of_preserves_limit {C : Type u₁} [category C] {D : Type u₂}
    [category D] (G : C ⥤ D) (X : C) (Y : C) [has_binary_product X Y]
    [preserves_limit (pair X Y) G] :
    is_limit (binary_fan.mk (functor.map G prod.fst) (functor.map G prod.snd)) :=
  map_is_limit_of_preserves_of_is_limit G prod.fst prod.snd (prod_is_prod X Y)

/--
If the product comparison map for `G` at `(X,Y)` is an isomorphism, then `G` preserves the
pair of `(X,Y)`.
-/
def preserves_pair.of_iso_comparison {C : Type u₁} [category C] {D : Type u₂} [category D]
    (G : C ⥤ D) (X : C) (Y : C) [has_binary_product X Y]
    [has_binary_product (functor.obj G X) (functor.obj G Y)] [i : is_iso (prod_comparison G X Y)] :
    preserves_limit (pair X Y) G :=
  preserves_limit_of_preserves_limit_cone (prod_is_prod X Y)
    (coe_fn (equiv.symm (is_limit_map_cone_binary_fan_equiv G prod.fst prod.snd))
      (is_limit.of_point_iso (limit.is_limit (pair (functor.obj G X) (functor.obj G Y)))))

/--
If `G` preserves the product of `(X,Y)`, then the product comparison map for `G` at `(X,Y)` is
an isomorphism.
-/
def preserves_pair.iso {C : Type u₁} [category C] {D : Type u₂} [category D] (G : C ⥤ D) (X : C)
    (Y : C) [has_binary_product X Y] [has_binary_product (functor.obj G X) (functor.obj G Y)]
    [preserves_limit (pair X Y) G] : functor.obj G (X ⨯ Y) ≅ functor.obj G X ⨯ functor.obj G Y :=
  is_limit.cone_point_unique_up_to_iso (is_limit_of_has_binary_product_of_preserves_limit G X Y)
    (limit.is_limit (pair (functor.obj G X) (functor.obj G Y)))

@[simp] theorem preserves_pair.iso_hom {C : Type u₁} [category C] {D : Type u₂} [category D]
    (G : C ⥤ D) (X : C) (Y : C) [has_binary_product X Y]
    [has_binary_product (functor.obj G X) (functor.obj G Y)] [preserves_limit (pair X Y) G] :
    iso.hom (preserves_pair.iso G X Y) = prod_comparison G X Y :=
  rfl

protected instance prod_comparison.category_theory.is_iso {C : Type u₁} [category C] {D : Type u₂}
    [category D] (G : C ⥤ D) (X : C) (Y : C) [has_binary_product X Y]
    [has_binary_product (functor.obj G X) (functor.obj G Y)] [preserves_limit (pair X Y) G] :
    is_iso (prod_comparison G X Y) :=
  eq.mpr sorry (is_iso.of_iso (preserves_pair.iso G X Y))

end Mathlib