/-
Copyright 2020 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
 -/
import order.complete_lattice
import order.basic

lemma Sup_image_le {α β:Type*} [complete_lattice β] {f:α → β} {S:set α} {x:β}:
  (∀ s∈ S, f s ≤ x) → (Sup (f '' S)≤ x) :=
begin
  intro A1,
  apply Sup_le,
  intros b A2,
  cases A2 with a A2,
  rw ← A2.right,
  apply (A1 a A2.left),
end

lemma le_Sup_image {α β:Type*} [complete_lattice β] {f:α → β} {S:set α} {a:α}:
  (a∈ S) →
  (f a) ≤ Sup (f '' S) :=
begin
  intro A1,
  apply le_Sup,
  simp,
  apply exists.intro a,
  split,
  apply A1,
  refl,
end

lemma Sup_Sup_image_image_le {α β γ:Type*} [complete_lattice γ] {f:α → β → γ} {S:set α}
    {T:set β} {x:γ}:
    (∀ a∈ S, ∀ b∈T, f a b ≤ x) →
    (Sup ((λ a:α, Sup  ((f a) '' T)) '' S) ≤ x) :=
begin
  intro A1,
  apply Sup_image_le,
  intros a A2,
  apply Sup_image_le,
  intros b A3,
  apply A1 a A2 b A3,
end

lemma le_Sup_Sup_image_image {α β γ:Type*} [complete_lattice γ] {f:α → β → γ}
    {S:set α} {T:set β} {a:α} {b:β}:
    (a∈ S) → (b∈ T) →
    (f a b) ≤ (Sup ((λ a:α, Sup  ((f a) '' T)) '' S)) :=
begin
  intros A1 A2,
  apply le_trans,
  apply @le_Sup_image β γ _ (f a) T b A2,
  apply @le_Sup_image α γ _ ((λ a:α, Sup  ((f a) '' T))) S a A1,
end

lemma Sup_le_Sup_of_monotone {α β:Type*} [complete_lattice α]
    [complete_lattice β]
    {f:α → β} {s:set α}:
   (monotone f) →
    Sup (f '' s) ≤ f (Sup s) :=
begin
  intro A1,
  apply Sup_le,
  intros b A2,
  simp at A2,
  cases A2 with x A2,
  cases A2 with A2 A3,
  subst b,
  apply A1,
  apply le_Sup,
  apply A2,
end

lemma supr_le_supr_of_monotone {α β γ:Type*} [complete_lattice α]
    [complete_lattice β]
    {f:α → β} {g:γ → α}:
   (monotone f) →
    supr (f ∘ g) ≤ f (supr g) :=
begin
  intro A1,
  apply Sup_le,
  intros b A2,
  simp at A2,
  cases A2 with x A2,
  subst b,
  apply A1,
  apply le_Sup,
  simp,
end

lemma infi_le_infi_of_monotone {α β γ:Type*} [complete_lattice α]
    [complete_lattice β]
    {f:α → β} {g:γ → α}:
   (monotone f) →
     f (infi g) ≤ infi (f ∘ g) :=
begin
  intro A1,
  apply le_infi,
  intros b,
  apply A1,
  apply infi_le,
end

lemma Inf_le_Inf_of_monotone {α β:Type*} [complete_lattice α]
    [complete_lattice β]
    {f:α → β} {s:set α}:
   (monotone f) →
     f (Inf s) ≤ Inf (f '' s) :=
begin
  intro A1,
  apply le_Inf,
  intros b A2,
  simp at A2,
  cases A2 with b' A2,
  cases A2 with A2 A3,
  subst b,
  apply A1,
  apply Inf_le,
  apply A2,
end

lemma supr_prop_def {α:Type*} [complete_lattice α]
    {v:α} {P:Prop} (H:P):(⨆ (H2:P), v) = v :=
begin
  apply le_antisymm,
  {
    apply supr_le,
    intro A1,
    apply le_refl v,
  },
  {
    apply @le_supr α P _ (λ H2:P, v),
    apply H,
  },
end

lemma infi_prop_def {α:Type*} [complete_lattice α]
    {v:α} {P:Prop} (H:P):(⨅ (H2:P), v) = v :=
begin
  apply le_antisymm,
  {
    apply infi_le,
    apply H,
  },
  {
    apply @le_infi,
    intro A1,
    apply le_refl v,
  },
end

lemma supr_prop_false {α:Type*} [complete_lattice α]
    {v:α} {P:Prop} (H:¬P):(⨆ (H2:P), v) = ⊥ :=
begin
  apply le_antisymm,
  {
    apply supr_le,
    intro A1,
    exfalso,
    apply H,
    apply A1,
  },
  {
    apply bot_le,
  },
end

lemma infi_prop_false {α:Type*} [complete_lattice α]
    {v:α} {P:Prop} (H:¬P):(⨅ (H2:P), v) = ⊤ :=
begin
  apply le_antisymm,
  {
    apply le_top,
  },
  {
    apply le_infi,
    intro A1,
    exfalso,
    apply H,
    apply A1,
  },
end


/-
Copyright (c) 2022 Yaël Dillies. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yaël Dillies

! This file was ported from Lean 3 source module topology.hom.open
! leanprover-community/mathlib commit ad0089aca372256fe53dde13ca0dfea569bf5ac7
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Topology.ContinuousFunction.Basic

/-!
# Continuous open maps

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

This file defines bundled continuous open maps.

We use the `fun_like` design, so each type of morphisms has a companion typeclass which is meant to
be satisfied by itself and all stricter types.

## Types of morphisms

* `continuous_open_map`: Continuous open maps.

## Typeclasses

* `continuous_open_map_class`
-/

open Function

variable {F α β γ δ : Type _}

#print ContinuousOpenMap /-
/-- The type of continuous open maps from `α` to `β`, aka Priestley homomorphisms. -/
structure ContinuousOpenMap (α β : Type _) [TopologicalSpace α] [TopologicalSpace β] extends
  ContinuousMap α β where
  map_open' : IsOpenMap to_fun
#align continuous_open_map ContinuousOpenMap
-/

-- mathport name: «expr →CO »
infixr:25 " →CO " => ContinuousOpenMap

section

#print ContinuousOpenMapClass /-
/-- `continuous_open_map_class F α β` states that `F` is a type of continuous open maps.

You should extend this class when you extend `continuous_open_map`. -/
class ContinuousOpenMapClass (F : Type _) (α β : outParam <| Type _) [TopologicalSpace α]
  [TopologicalSpace β] extends ContinuousMapClass F α β where
  map_open (f : F) : IsOpenMap f
#align continuous_open_map_class ContinuousOpenMapClass
-/

end

export ContinuousOpenMapClass (map_open)

instance [TopologicalSpace α] [TopologicalSpace β] [ContinuousOpenMapClass F α β] :
    CoeTC F (α →CO β) :=
  ⟨fun f => ⟨f, map_open f⟩⟩

/-! ### Continuous open maps -/

namespace ContinuousOpenMap

variable [TopologicalSpace α] [TopologicalSpace β] [TopologicalSpace γ] [TopologicalSpace δ]

instance : ContinuousOpenMapClass (α →CO β) α β
    where
  coe f := f.toFun
  coe_injective' f g h := by
    obtain ⟨⟨_, _⟩, _⟩ := f
    obtain ⟨⟨_, _⟩, _⟩ := g
    congr
  map_continuous f := f.continuous_toFun
  map_open f := f.map_open'

/-- Helper instance for when there's too many metavariables to apply `fun_like.has_coe_to_fun`
directly. -/
instance : CoeFun (α →CO β) fun _ => α → β :=
  FunLike.hasCoeToFun

/- warning: continuous_open_map.to_fun_eq_coe -> ContinuousOpenMap.toFun_eq_coe is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {f : ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2}, Eq.{max (succ u1) (succ u2)} (α -> β) (ContinuousMap.toFun.{u1, u2} α β _inst_1 _inst_2 (ContinuousOpenMap.toContinuousMap.{u1, u2} α β _inst_1 _inst_2 f)) (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) (fun (_x : ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) => α -> β) (ContinuousOpenMap.hasCoeToFun.{u1, u2} α β _inst_1 _inst_2) f)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {f : ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2}, Eq.{max (succ u2) (succ u1)} (α -> β) (ContinuousMap.toFun.{u2, u1} α β _inst_1 _inst_2 (ContinuousOpenMap.toContinuousMap.{u2, u1} α β _inst_1 _inst_2 f)) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.ContinuousFunction.Basic._hyg.699 : α) => β) _x) (ContinuousMapClass.toFunLike.{max u2 u1, u2, u1} (ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (ContinuousOpenMapClass.toContinuousMapClass.{max u2 u1, u2, u1} (ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (ContinuousOpenMap.instContinuousOpenMapClassContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2))) f)
Case conversion may be inaccurate. Consider using '#align continuous_open_map.to_fun_eq_coe ContinuousOpenMap.toFun_eq_coeₓ'. -/
@[simp]
theorem toFun_eq_coe {f : α →CO β} : f.toFun = (f : α → β) :=
  rfl
#align continuous_open_map.to_fun_eq_coe ContinuousOpenMap.toFun_eq_coe

/- warning: continuous_open_map.ext -> ContinuousOpenMap.ext is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {f : ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2} {g : ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2}, (forall (a : α), Eq.{succ u2} β (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) (fun (_x : ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) => α -> β) (ContinuousOpenMap.hasCoeToFun.{u1, u2} α β _inst_1 _inst_2) f a) (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) (fun (_x : ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) => α -> β) (ContinuousOpenMap.hasCoeToFun.{u1, u2} α β _inst_1 _inst_2) g a)) -> (Eq.{max (succ u1) (succ u2)} (ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) f g)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {f : ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2} {g : ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2}, (forall (a : α), Eq.{succ u1} ((fun (x._@.Mathlib.Topology.ContinuousFunction.Basic._hyg.699 : α) => β) a) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.ContinuousFunction.Basic._hyg.699 : α) => β) _x) (ContinuousMapClass.toFunLike.{max u2 u1, u2, u1} (ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (ContinuousOpenMapClass.toContinuousMapClass.{max u2 u1, u2, u1} (ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (ContinuousOpenMap.instContinuousOpenMapClassContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2))) f a) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.ContinuousFunction.Basic._hyg.699 : α) => β) _x) (ContinuousMapClass.toFunLike.{max u2 u1, u2, u1} (ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (ContinuousOpenMapClass.toContinuousMapClass.{max u2 u1, u2, u1} (ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (ContinuousOpenMap.instContinuousOpenMapClassContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2))) g a)) -> (Eq.{max (succ u2) (succ u1)} (ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2) f g)
Case conversion may be inaccurate. Consider using '#align continuous_open_map.ext ContinuousOpenMap.extₓ'. -/
@[ext]
theorem ext {f g : α →CO β} (h : ∀ a, f a = g a) : f = g :=
  FunLike.ext f g h
#align continuous_open_map.ext ContinuousOpenMap.ext

/- warning: continuous_open_map.copy -> ContinuousOpenMap.copy is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] (f : ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) (f' : α -> β), (Eq.{max (succ u1) (succ u2)} (α -> β) f' (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) (fun (_x : ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) => α -> β) (ContinuousOpenMap.hasCoeToFun.{u1, u2} α β _inst_1 _inst_2) f)) -> (ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2)
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] (f : ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) (f' : α -> β), (Eq.{max (succ u1) (succ u2)} (α -> β) f' (FunLike.coe.{max (succ u1) (succ u2), succ u1, succ u2} (ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.ContinuousFunction.Basic._hyg.699 : α) => β) _x) (ContinuousMapClass.toFunLike.{max u1 u2, u1, u2} (ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (ContinuousOpenMapClass.toContinuousMapClass.{max u1 u2, u1, u2} (ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (ContinuousOpenMap.instContinuousOpenMapClassContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2))) f)) -> (ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2)
Case conversion may be inaccurate. Consider using '#align continuous_open_map.copy ContinuousOpenMap.copyₓ'. -/
/-- Copy of a `continuous_open_map` with a new `continuous_map` equal to the old one. Useful to fix
definitional equalities. -/
protected def copy (f : α →CO β) (f' : α → β) (h : f' = f) : α →CO β :=
  ⟨f.toContinuousMap.copy f' <| h, h.symm.subst f.map_open'⟩
#align continuous_open_map.copy ContinuousOpenMap.copy

/- warning: continuous_open_map.coe_copy -> ContinuousOpenMap.coe_copy is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] (f : ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) (f' : α -> β) (h : Eq.{max (succ u1) (succ u2)} (α -> β) f' (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) (fun (_x : ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) => α -> β) (ContinuousOpenMap.hasCoeToFun.{u1, u2} α β _inst_1 _inst_2) f)), Eq.{max (succ u1) (succ u2)} (α -> β) (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) (fun (_x : ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) => α -> β) (ContinuousOpenMap.hasCoeToFun.{u1, u2} α β _inst_1 _inst_2) (ContinuousOpenMap.copy.{u1, u2} α β _inst_1 _inst_2 f f' h)) f'
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] (f : ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2) (f' : α -> β) (h : Eq.{max (succ u2) (succ u1)} (α -> β) f' (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.ContinuousFunction.Basic._hyg.699 : α) => β) _x) (ContinuousMapClass.toFunLike.{max u2 u1, u2, u1} (ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (ContinuousOpenMapClass.toContinuousMapClass.{max u2 u1, u2, u1} (ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (ContinuousOpenMap.instContinuousOpenMapClassContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2))) f)), Eq.{max (succ u2) (succ u1)} (forall (ᾰ : α), (fun (x._@.Mathlib.Topology.ContinuousFunction.Basic._hyg.699 : α) => β) ᾰ) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.ContinuousFunction.Basic._hyg.699 : α) => β) _x) (ContinuousMapClass.toFunLike.{max u2 u1, u2, u1} (ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (ContinuousOpenMapClass.toContinuousMapClass.{max u2 u1, u2, u1} (ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (ContinuousOpenMap.instContinuousOpenMapClassContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2))) (ContinuousOpenMap.copy.{u2, u1} α β _inst_1 _inst_2 f f' h)) f'
Case conversion may be inaccurate. Consider using '#align continuous_open_map.coe_copy ContinuousOpenMap.coe_copyₓ'. -/
@[simp]
theorem coe_copy (f : α →CO β) (f' : α → β) (h : f' = f) : ⇑(f.copy f' h) = f' :=
  rfl
#align continuous_open_map.coe_copy ContinuousOpenMap.coe_copy

/- warning: continuous_open_map.copy_eq -> ContinuousOpenMap.copy_eq is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] (f : ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) (f' : α -> β) (h : Eq.{max (succ u1) (succ u2)} (α -> β) f' (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) (fun (_x : ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) => α -> β) (ContinuousOpenMap.hasCoeToFun.{u1, u2} α β _inst_1 _inst_2) f)), Eq.{max (succ u1) (succ u2)} (ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) (ContinuousOpenMap.copy.{u1, u2} α β _inst_1 _inst_2 f f' h) f
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] (f : ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2) (f' : α -> β) (h : Eq.{max (succ u2) (succ u1)} (α -> β) f' (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.ContinuousFunction.Basic._hyg.699 : α) => β) _x) (ContinuousMapClass.toFunLike.{max u2 u1, u2, u1} (ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (ContinuousOpenMapClass.toContinuousMapClass.{max u2 u1, u2, u1} (ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (ContinuousOpenMap.instContinuousOpenMapClassContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2))) f)), Eq.{max (succ u2) (succ u1)} (ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2) (ContinuousOpenMap.copy.{u2, u1} α β _inst_1 _inst_2 f f' h) f
Case conversion may be inaccurate. Consider using '#align continuous_open_map.copy_eq ContinuousOpenMap.copy_eqₓ'. -/
theorem copy_eq (f : α →CO β) (f' : α → β) (h : f' = f) : f.copy f' h = f :=
  FunLike.ext' h
#align continuous_open_map.copy_eq ContinuousOpenMap.copy_eq

variable (α)

#print ContinuousOpenMap.id /-
/-- `id` as a `continuous_open_map`. -/
protected def id : α →CO α :=
  ⟨ContinuousMap.id _, IsOpenMap.id⟩
#align continuous_open_map.id ContinuousOpenMap.id
-/

instance : Inhabited (α →CO α) :=
  ⟨ContinuousOpenMap.id _⟩

/- warning: continuous_open_map.coe_id -> ContinuousOpenMap.coe_id is a dubious translation:
lean 3 declaration is
  forall (α : Type.{u1}) [_inst_1 : TopologicalSpace.{u1} α], Eq.{succ u1} (α -> α) (coeFn.{succ u1, succ u1} (ContinuousOpenMap.{u1, u1} α α _inst_1 _inst_1) (fun (_x : ContinuousOpenMap.{u1, u1} α α _inst_1 _inst_1) => α -> α) (ContinuousOpenMap.hasCoeToFun.{u1, u1} α α _inst_1 _inst_1) (ContinuousOpenMap.id.{u1} α _inst_1)) (id.{succ u1} α)
but is expected to have type
  forall (α : Type.{u1}) [_inst_1 : TopologicalSpace.{u1} α], Eq.{succ u1} (forall (ᾰ : α), (fun (x._@.Mathlib.Topology.ContinuousFunction.Basic._hyg.699 : α) => α) ᾰ) (FunLike.coe.{succ u1, succ u1, succ u1} (ContinuousOpenMap.{u1, u1} α α _inst_1 _inst_1) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.ContinuousFunction.Basic._hyg.699 : α) => α) _x) (ContinuousMapClass.toFunLike.{u1, u1, u1} (ContinuousOpenMap.{u1, u1} α α _inst_1 _inst_1) α α _inst_1 _inst_1 (ContinuousOpenMapClass.toContinuousMapClass.{u1, u1, u1} (ContinuousOpenMap.{u1, u1} α α _inst_1 _inst_1) α α _inst_1 _inst_1 (ContinuousOpenMap.instContinuousOpenMapClassContinuousOpenMap.{u1, u1} α α _inst_1 _inst_1))) (ContinuousOpenMap.id.{u1} α _inst_1)) (id.{succ u1} α)
Case conversion may be inaccurate. Consider using '#align continuous_open_map.coe_id ContinuousOpenMap.coe_idₓ'. -/
@[simp]
theorem coe_id : ⇑(ContinuousOpenMap.id α) = id :=
  rfl
#align continuous_open_map.coe_id ContinuousOpenMap.coe_id

variable {α}

/- warning: continuous_open_map.id_apply -> ContinuousOpenMap.id_apply is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] (a : α), Eq.{succ u1} α (coeFn.{succ u1, succ u1} (ContinuousOpenMap.{u1, u1} α α _inst_1 _inst_1) (fun (_x : ContinuousOpenMap.{u1, u1} α α _inst_1 _inst_1) => α -> α) (ContinuousOpenMap.hasCoeToFun.{u1, u1} α α _inst_1 _inst_1) (ContinuousOpenMap.id.{u1} α _inst_1) a) a
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] (a : α), Eq.{succ u1} ((fun (x._@.Mathlib.Topology.ContinuousFunction.Basic._hyg.699 : α) => α) a) (FunLike.coe.{succ u1, succ u1, succ u1} (ContinuousOpenMap.{u1, u1} α α _inst_1 _inst_1) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.ContinuousFunction.Basic._hyg.699 : α) => α) _x) (ContinuousMapClass.toFunLike.{u1, u1, u1} (ContinuousOpenMap.{u1, u1} α α _inst_1 _inst_1) α α _inst_1 _inst_1 (ContinuousOpenMapClass.toContinuousMapClass.{u1, u1, u1} (ContinuousOpenMap.{u1, u1} α α _inst_1 _inst_1) α α _inst_1 _inst_1 (ContinuousOpenMap.instContinuousOpenMapClassContinuousOpenMap.{u1, u1} α α _inst_1 _inst_1))) (ContinuousOpenMap.id.{u1} α _inst_1) a) a
Case conversion may be inaccurate. Consider using '#align continuous_open_map.id_apply ContinuousOpenMap.id_applyₓ'. -/
@[simp]
theorem id_apply (a : α) : ContinuousOpenMap.id α a = a :=
  rfl
#align continuous_open_map.id_apply ContinuousOpenMap.id_apply

#print ContinuousOpenMap.comp /-
/-- Composition of `continuous_open_map`s as a `continuous_open_map`. -/
def comp (f : β →CO γ) (g : α →CO β) : ContinuousOpenMap α γ :=
  ⟨f.toContinuousMap.comp g.toContinuousMap, f.map_open'.comp g.map_open'⟩
#align continuous_open_map.comp ContinuousOpenMap.comp
-/

/- warning: continuous_open_map.coe_comp -> ContinuousOpenMap.coe_comp is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] [_inst_3 : TopologicalSpace.{u3} γ] (f : ContinuousOpenMap.{u2, u3} β γ _inst_2 _inst_3) (g : ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2), Eq.{max (succ u1) (succ u3)} ((fun (_x : ContinuousOpenMap.{u1, u3} α γ _inst_1 _inst_3) => α -> γ) (ContinuousOpenMap.comp.{u1, u2, u3} α β γ _inst_1 _inst_2 _inst_3 f g)) (coeFn.{max (succ u1) (succ u3), max (succ u1) (succ u3)} (ContinuousOpenMap.{u1, u3} α γ _inst_1 _inst_3) (fun (_x : ContinuousOpenMap.{u1, u3} α γ _inst_1 _inst_3) => α -> γ) (ContinuousOpenMap.hasCoeToFun.{u1, u3} α γ _inst_1 _inst_3) (ContinuousOpenMap.comp.{u1, u2, u3} α β γ _inst_1 _inst_2 _inst_3 f g)) (Function.comp.{succ u1, succ u2, succ u3} α β γ (coeFn.{max (succ u2) (succ u3), max (succ u2) (succ u3)} (ContinuousOpenMap.{u2, u3} β γ _inst_2 _inst_3) (fun (_x : ContinuousOpenMap.{u2, u3} β γ _inst_2 _inst_3) => β -> γ) (ContinuousOpenMap.hasCoeToFun.{u2, u3} β γ _inst_2 _inst_3) f) (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) (fun (_x : ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) => α -> β) (ContinuousOpenMap.hasCoeToFun.{u1, u2} α β _inst_1 _inst_2) g))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u3}} {γ : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u3} β] [_inst_3 : TopologicalSpace.{u2} γ] (f : ContinuousOpenMap.{u3, u2} β γ _inst_2 _inst_3) (g : ContinuousOpenMap.{u1, u3} α β _inst_1 _inst_2), Eq.{max (succ u1) (succ u2)} (forall (a : α), (fun (x._@.Mathlib.Topology.ContinuousFunction.Basic._hyg.699 : α) => γ) a) (FunLike.coe.{max (succ u1) (succ u2), succ u1, succ u2} (ContinuousOpenMap.{u1, u2} α γ _inst_1 _inst_3) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.ContinuousFunction.Basic._hyg.699 : α) => γ) _x) (ContinuousMapClass.toFunLike.{max u1 u2, u1, u2} (ContinuousOpenMap.{u1, u2} α γ _inst_1 _inst_3) α γ _inst_1 _inst_3 (ContinuousOpenMapClass.toContinuousMapClass.{max u1 u2, u1, u2} (ContinuousOpenMap.{u1, u2} α γ _inst_1 _inst_3) α γ _inst_1 _inst_3 (ContinuousOpenMap.instContinuousOpenMapClassContinuousOpenMap.{u1, u2} α γ _inst_1 _inst_3))) (ContinuousOpenMap.comp.{u1, u3, u2} α β γ _inst_1 _inst_2 _inst_3 f g)) (Function.comp.{succ u1, succ u3, succ u2} α β γ (FunLike.coe.{max (succ u3) (succ u2), succ u3, succ u2} (ContinuousOpenMap.{u3, u2} β γ _inst_2 _inst_3) β (fun (_x : β) => (fun (x._@.Mathlib.Topology.ContinuousFunction.Basic._hyg.699 : β) => γ) _x) (ContinuousMapClass.toFunLike.{max u3 u2, u3, u2} (ContinuousOpenMap.{u3, u2} β γ _inst_2 _inst_3) β γ _inst_2 _inst_3 (ContinuousOpenMapClass.toContinuousMapClass.{max u3 u2, u3, u2} (ContinuousOpenMap.{u3, u2} β γ _inst_2 _inst_3) β γ _inst_2 _inst_3 (ContinuousOpenMap.instContinuousOpenMapClassContinuousOpenMap.{u3, u2} β γ _inst_2 _inst_3))) f) (FunLike.coe.{max (succ u1) (succ u3), succ u1, succ u3} (ContinuousOpenMap.{u1, u3} α β _inst_1 _inst_2) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.ContinuousFunction.Basic._hyg.699 : α) => β) _x) (ContinuousMapClass.toFunLike.{max u1 u3, u1, u3} (ContinuousOpenMap.{u1, u3} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (ContinuousOpenMapClass.toContinuousMapClass.{max u1 u3, u1, u3} (ContinuousOpenMap.{u1, u3} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (ContinuousOpenMap.instContinuousOpenMapClassContinuousOpenMap.{u1, u3} α β _inst_1 _inst_2))) g))
Case conversion may be inaccurate. Consider using '#align continuous_open_map.coe_comp ContinuousOpenMap.coe_compₓ'. -/
@[simp]
theorem coe_comp (f : β →CO γ) (g : α →CO β) : (f.comp g : α → γ) = f ∘ g :=
  rfl
#align continuous_open_map.coe_comp ContinuousOpenMap.coe_comp

/- warning: continuous_open_map.comp_apply -> ContinuousOpenMap.comp_apply is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] [_inst_3 : TopologicalSpace.{u3} γ] (f : ContinuousOpenMap.{u2, u3} β γ _inst_2 _inst_3) (g : ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) (a : α), Eq.{succ u3} γ (coeFn.{max (succ u1) (succ u3), max (succ u1) (succ u3)} (ContinuousOpenMap.{u1, u3} α γ _inst_1 _inst_3) (fun (_x : ContinuousOpenMap.{u1, u3} α γ _inst_1 _inst_3) => α -> γ) (ContinuousOpenMap.hasCoeToFun.{u1, u3} α γ _inst_1 _inst_3) (ContinuousOpenMap.comp.{u1, u2, u3} α β γ _inst_1 _inst_2 _inst_3 f g) a) (coeFn.{max (succ u2) (succ u3), max (succ u2) (succ u3)} (ContinuousOpenMap.{u2, u3} β γ _inst_2 _inst_3) (fun (_x : ContinuousOpenMap.{u2, u3} β γ _inst_2 _inst_3) => β -> γ) (ContinuousOpenMap.hasCoeToFun.{u2, u3} β γ _inst_2 _inst_3) f (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) (fun (_x : ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) => α -> β) (ContinuousOpenMap.hasCoeToFun.{u1, u2} α β _inst_1 _inst_2) g a))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u3}} {γ : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u3} β] [_inst_3 : TopologicalSpace.{u2} γ] (f : ContinuousOpenMap.{u3, u2} β γ _inst_2 _inst_3) (g : ContinuousOpenMap.{u1, u3} α β _inst_1 _inst_2) (a : α), Eq.{succ u2} ((fun (x._@.Mathlib.Topology.ContinuousFunction.Basic._hyg.699 : α) => γ) a) (FunLike.coe.{max (succ u1) (succ u2), succ u1, succ u2} (ContinuousOpenMap.{u1, u2} α γ _inst_1 _inst_3) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.ContinuousFunction.Basic._hyg.699 : α) => γ) _x) (ContinuousMapClass.toFunLike.{max u1 u2, u1, u2} (ContinuousOpenMap.{u1, u2} α γ _inst_1 _inst_3) α γ _inst_1 _inst_3 (ContinuousOpenMapClass.toContinuousMapClass.{max u1 u2, u1, u2} (ContinuousOpenMap.{u1, u2} α γ _inst_1 _inst_3) α γ _inst_1 _inst_3 (ContinuousOpenMap.instContinuousOpenMapClassContinuousOpenMap.{u1, u2} α γ _inst_1 _inst_3))) (ContinuousOpenMap.comp.{u1, u3, u2} α β γ _inst_1 _inst_2 _inst_3 f g) a) (FunLike.coe.{max (succ u3) (succ u2), succ u3, succ u2} (ContinuousOpenMap.{u3, u2} β γ _inst_2 _inst_3) β (fun (_x : β) => (fun (x._@.Mathlib.Topology.ContinuousFunction.Basic._hyg.699 : β) => γ) _x) (ContinuousMapClass.toFunLike.{max u3 u2, u3, u2} (ContinuousOpenMap.{u3, u2} β γ _inst_2 _inst_3) β γ _inst_2 _inst_3 (ContinuousOpenMapClass.toContinuousMapClass.{max u3 u2, u3, u2} (ContinuousOpenMap.{u3, u2} β γ _inst_2 _inst_3) β γ _inst_2 _inst_3 (ContinuousOpenMap.instContinuousOpenMapClassContinuousOpenMap.{u3, u2} β γ _inst_2 _inst_3))) f (FunLike.coe.{max (succ u1) (succ u3), succ u1, succ u3} (ContinuousOpenMap.{u1, u3} α β _inst_1 _inst_2) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.ContinuousFunction.Basic._hyg.699 : α) => β) _x) (ContinuousMapClass.toFunLike.{max u1 u3, u1, u3} (ContinuousOpenMap.{u1, u3} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (ContinuousOpenMapClass.toContinuousMapClass.{max u1 u3, u1, u3} (ContinuousOpenMap.{u1, u3} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (ContinuousOpenMap.instContinuousOpenMapClassContinuousOpenMap.{u1, u3} α β _inst_1 _inst_2))) g a))
Case conversion may be inaccurate. Consider using '#align continuous_open_map.comp_apply ContinuousOpenMap.comp_applyₓ'. -/
@[simp]
theorem comp_apply (f : β →CO γ) (g : α →CO β) (a : α) : (f.comp g) a = f (g a) :=
  rfl
#align continuous_open_map.comp_apply ContinuousOpenMap.comp_apply

/- warning: continuous_open_map.comp_assoc -> ContinuousOpenMap.comp_assoc is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {δ : Type.{u4}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] [_inst_3 : TopologicalSpace.{u3} γ] [_inst_4 : TopologicalSpace.{u4} δ] (f : ContinuousOpenMap.{u3, u4} γ δ _inst_3 _inst_4) (g : ContinuousOpenMap.{u2, u3} β γ _inst_2 _inst_3) (h : ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2), Eq.{max (succ u1) (succ u4)} (ContinuousOpenMap.{u1, u4} α δ _inst_1 _inst_4) (ContinuousOpenMap.comp.{u1, u2, u4} α β δ _inst_1 _inst_2 _inst_4 (ContinuousOpenMap.comp.{u2, u3, u4} β γ δ _inst_2 _inst_3 _inst_4 f g) h) (ContinuousOpenMap.comp.{u1, u3, u4} α γ δ _inst_1 _inst_3 _inst_4 f (ContinuousOpenMap.comp.{u1, u2, u3} α β γ _inst_1 _inst_2 _inst_3 g h))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u4}} {δ : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] [_inst_3 : TopologicalSpace.{u4} γ] [_inst_4 : TopologicalSpace.{u3} δ] (f : ContinuousOpenMap.{u4, u3} γ δ _inst_3 _inst_4) (g : ContinuousOpenMap.{u2, u4} β γ _inst_2 _inst_3) (h : ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2), Eq.{max (succ u1) (succ u3)} (ContinuousOpenMap.{u1, u3} α δ _inst_1 _inst_4) (ContinuousOpenMap.comp.{u1, u2, u3} α β δ _inst_1 _inst_2 _inst_4 (ContinuousOpenMap.comp.{u2, u4, u3} β γ δ _inst_2 _inst_3 _inst_4 f g) h) (ContinuousOpenMap.comp.{u1, u4, u3} α γ δ _inst_1 _inst_3 _inst_4 f (ContinuousOpenMap.comp.{u1, u2, u4} α β γ _inst_1 _inst_2 _inst_3 g h))
Case conversion may be inaccurate. Consider using '#align continuous_open_map.comp_assoc ContinuousOpenMap.comp_assocₓ'. -/
@[simp]
theorem comp_assoc (f : γ →CO δ) (g : β →CO γ) (h : α →CO β) :
    (f.comp g).comp h = f.comp (g.comp h) :=
  rfl
#align continuous_open_map.comp_assoc ContinuousOpenMap.comp_assoc

/- warning: continuous_open_map.comp_id -> ContinuousOpenMap.comp_id is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] (f : ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2), Eq.{max (succ u1) (succ u2)} (ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) (ContinuousOpenMap.comp.{u1, u1, u2} α α β _inst_1 _inst_1 _inst_2 f (ContinuousOpenMap.id.{u1} α _inst_1)) f
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] (f : ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2), Eq.{max (succ u2) (succ u1)} (ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2) (ContinuousOpenMap.comp.{u2, u2, u1} α α β _inst_1 _inst_1 _inst_2 f (ContinuousOpenMap.id.{u2} α _inst_1)) f
Case conversion may be inaccurate. Consider using '#align continuous_open_map.comp_id ContinuousOpenMap.comp_idₓ'. -/
@[simp]
theorem comp_id (f : α →CO β) : f.comp (ContinuousOpenMap.id α) = f :=
  ext fun a => rfl
#align continuous_open_map.comp_id ContinuousOpenMap.comp_id

/- warning: continuous_open_map.id_comp -> ContinuousOpenMap.id_comp is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] (f : ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2), Eq.{max (succ u1) (succ u2)} (ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) (ContinuousOpenMap.comp.{u1, u2, u2} α β β _inst_1 _inst_2 _inst_2 (ContinuousOpenMap.id.{u2} β _inst_2) f) f
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] (f : ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2), Eq.{max (succ u2) (succ u1)} (ContinuousOpenMap.{u2, u1} α β _inst_1 _inst_2) (ContinuousOpenMap.comp.{u2, u1, u1} α β β _inst_1 _inst_2 _inst_2 (ContinuousOpenMap.id.{u1} β _inst_2) f) f
Case conversion may be inaccurate. Consider using '#align continuous_open_map.id_comp ContinuousOpenMap.id_compₓ'. -/
@[simp]
theorem id_comp (f : α →CO β) : (ContinuousOpenMap.id β).comp f = f :=
  ext fun a => rfl
#align continuous_open_map.id_comp ContinuousOpenMap.id_comp

/- warning: continuous_open_map.cancel_right -> ContinuousOpenMap.cancel_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] [_inst_3 : TopologicalSpace.{u3} γ] {g₁ : ContinuousOpenMap.{u2, u3} β γ _inst_2 _inst_3} {g₂ : ContinuousOpenMap.{u2, u3} β γ _inst_2 _inst_3} {f : ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2}, (Function.Surjective.{succ u1, succ u2} α β (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) (fun (_x : ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) => α -> β) (ContinuousOpenMap.hasCoeToFun.{u1, u2} α β _inst_1 _inst_2) f)) -> (Iff (Eq.{max (succ u1) (succ u3)} (ContinuousOpenMap.{u1, u3} α γ _inst_1 _inst_3) (ContinuousOpenMap.comp.{u1, u2, u3} α β γ _inst_1 _inst_2 _inst_3 g₁ f) (ContinuousOpenMap.comp.{u1, u2, u3} α β γ _inst_1 _inst_2 _inst_3 g₂ f)) (Eq.{max (succ u2) (succ u3)} (ContinuousOpenMap.{u2, u3} β γ _inst_2 _inst_3) g₁ g₂))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u3}} {γ : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u3} β] [_inst_3 : TopologicalSpace.{u2} γ] {g₁ : ContinuousOpenMap.{u3, u2} β γ _inst_2 _inst_3} {g₂ : ContinuousOpenMap.{u3, u2} β γ _inst_2 _inst_3} {f : ContinuousOpenMap.{u1, u3} α β _inst_1 _inst_2}, (Function.Surjective.{succ u1, succ u3} α β (FunLike.coe.{max (succ u1) (succ u3), succ u1, succ u3} (ContinuousOpenMap.{u1, u3} α β _inst_1 _inst_2) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.ContinuousFunction.Basic._hyg.699 : α) => β) _x) (ContinuousMapClass.toFunLike.{max u1 u3, u1, u3} (ContinuousOpenMap.{u1, u3} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (ContinuousOpenMapClass.toContinuousMapClass.{max u1 u3, u1, u3} (ContinuousOpenMap.{u1, u3} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (ContinuousOpenMap.instContinuousOpenMapClassContinuousOpenMap.{u1, u3} α β _inst_1 _inst_2))) f)) -> (Iff (Eq.{max (succ u1) (succ u2)} (ContinuousOpenMap.{u1, u2} α γ _inst_1 _inst_3) (ContinuousOpenMap.comp.{u1, u3, u2} α β γ _inst_1 _inst_2 _inst_3 g₁ f) (ContinuousOpenMap.comp.{u1, u3, u2} α β γ _inst_1 _inst_2 _inst_3 g₂ f)) (Eq.{max (succ u3) (succ u2)} (ContinuousOpenMap.{u3, u2} β γ _inst_2 _inst_3) g₁ g₂))
Case conversion may be inaccurate. Consider using '#align continuous_open_map.cancel_right ContinuousOpenMap.cancel_rightₓ'. -/
theorem cancel_right {g₁ g₂ : β →CO γ} {f : α →CO β} (hf : Surjective f) :
    g₁.comp f = g₂.comp f ↔ g₁ = g₂ :=
  ⟨fun h => ext <| hf.forall.2 <| FunLike.ext_iff.1 h, congr_arg _⟩
#align continuous_open_map.cancel_right ContinuousOpenMap.cancel_right

/- warning: continuous_open_map.cancel_left -> ContinuousOpenMap.cancel_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] [_inst_3 : TopologicalSpace.{u3} γ] {g : ContinuousOpenMap.{u2, u3} β γ _inst_2 _inst_3} {f₁ : ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2} {f₂ : ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2}, (Function.Injective.{succ u2, succ u3} β γ (coeFn.{max (succ u2) (succ u3), max (succ u2) (succ u3)} (ContinuousOpenMap.{u2, u3} β γ _inst_2 _inst_3) (fun (_x : ContinuousOpenMap.{u2, u3} β γ _inst_2 _inst_3) => β -> γ) (ContinuousOpenMap.hasCoeToFun.{u2, u3} β γ _inst_2 _inst_3) g)) -> (Iff (Eq.{max (succ u1) (succ u3)} (ContinuousOpenMap.{u1, u3} α γ _inst_1 _inst_3) (ContinuousOpenMap.comp.{u1, u2, u3} α β γ _inst_1 _inst_2 _inst_3 g f₁) (ContinuousOpenMap.comp.{u1, u2, u3} α β γ _inst_1 _inst_2 _inst_3 g f₂)) (Eq.{max (succ u1) (succ u2)} (ContinuousOpenMap.{u1, u2} α β _inst_1 _inst_2) f₁ f₂))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u3}} {γ : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u3} β] [_inst_3 : TopologicalSpace.{u2} γ] {g : ContinuousOpenMap.{u3, u2} β γ _inst_2 _inst_3} {f₁ : ContinuousOpenMap.{u1, u3} α β _inst_1 _inst_2} {f₂ : ContinuousOpenMap.{u1, u3} α β _inst_1 _inst_2}, (Function.Injective.{succ u3, succ u2} β γ (FunLike.coe.{max (succ u3) (succ u2), succ u3, succ u2} (ContinuousOpenMap.{u3, u2} β γ _inst_2 _inst_3) β (fun (_x : β) => (fun (x._@.Mathlib.Topology.ContinuousFunction.Basic._hyg.699 : β) => γ) _x) (ContinuousMapClass.toFunLike.{max u3 u2, u3, u2} (ContinuousOpenMap.{u3, u2} β γ _inst_2 _inst_3) β γ _inst_2 _inst_3 (ContinuousOpenMapClass.toContinuousMapClass.{max u3 u2, u3, u2} (ContinuousOpenMap.{u3, u2} β γ _inst_2 _inst_3) β γ _inst_2 _inst_3 (ContinuousOpenMap.instContinuousOpenMapClassContinuousOpenMap.{u3, u2} β γ _inst_2 _inst_3))) g)) -> (Iff (Eq.{max (succ u1) (succ u2)} (ContinuousOpenMap.{u1, u2} α γ _inst_1 _inst_3) (ContinuousOpenMap.comp.{u1, u3, u2} α β γ _inst_1 _inst_2 _inst_3 g f₁) (ContinuousOpenMap.comp.{u1, u3, u2} α β γ _inst_1 _inst_2 _inst_3 g f₂)) (Eq.{max (succ u1) (succ u3)} (ContinuousOpenMap.{u1, u3} α β _inst_1 _inst_2) f₁ f₂))
Case conversion may be inaccurate. Consider using '#align continuous_open_map.cancel_left ContinuousOpenMap.cancel_leftₓ'. -/
theorem cancel_left {g : β →CO γ} {f₁ f₂ : α →CO β} (hg : Injective g) :
    g.comp f₁ = g.comp f₂ ↔ f₁ = f₂ :=
  ⟨fun h => ext fun a => hg <| by rw [← comp_apply, h, comp_apply], congr_arg _⟩
#align continuous_open_map.cancel_left ContinuousOpenMap.cancel_left

end ContinuousOpenMap


import .odd_and_even_nat

example : ∀ a b, odd a → even b → even (a * b) :=
begin
    assume a b,
    assume odda,
    assume evenb,
    cases odda,
    cases evenb,
        rw odda_h,
        rw evenb_h,

        apply exists.intro ((2 * odda_w + 1) * evenb_w),
            rw ←(mul_assoc 2),
            rw mul_comm 2 (2 * odda_w + 1),
            rw mul_assoc,
end


lemma add_right_eq_zero {a b : mynat} : a + b = 0 → a = 0 :=
begin
rw add_comm,
intro x,
exact add_left_eq_zero x,
end


import Mathlib.Combinatorics.SimpleGraph.Coloring
import Mathlib.Tactic.LibrarySearch

universe u w

open SimpleGraph Hom Iso Embedding

variable {V : Type u} {G : SimpleGraph V} {α : Type v}

def closedNbhdSubgraph (v : V) : SimpleGraph {w | G.Adj v w ∨ w = v} where
  Adj := fun w₁ w₂ => G.Adj w₁ w₂ ∧ (w₁ = v ∨ w₂ = v)
  symm := by
    intro v₁ v₂ h
    constructor
    · apply G.symm <| h.left
    · match h.right with
      | .inl h => exact .inr h
      | .inr h => exact .inl h

def AdjacentAt (v : V) (e₁ e₂ : edgeSet G) : Prop := Sym2.Mem v e₁ ∧ Sym2.Mem v e₂

theorem AdjacentAt.symm {v : V} {e₁ e₂ : edgeSet G} (h : AdjacentAt v e₁ e₂) : AdjacentAt v e₂ e₁ := ⟨h.right, h.left⟩

def Adjacent (e₁ e₂ : edgeSet G) : Prop := ∃ v, AdjacentAt v e₁ e₂

@[symm, aesop unsafe 10% apply (rule_sets [SimpleGraph])]
theorem Adjacent.symm {e₁ e₂ : edgeSet G} (h : Adjacent e₁ e₂) : Adjacent e₂ e₁ := by
  obtain ⟨w,h'⟩ := h
  exact ⟨w,AdjacentAt.symm h'⟩

def lineGraph (G : SimpleGraph V) : SimpleGraph (edgeSet G) where
  Adj := fun e₁ e₂ => Adjacent e₁ e₂ ∧ e₁ ≠ e₂

variable (G)

abbrev EdgeColoring (α : Type v) := Coloring (lineGraph G) α

theorem EdgeColoring.valid {α : Type v} (G : SimpleGraph V)
    (c : EdgeColoring G α) {e₁ e₂ : edgeSet G} (h : e₁ ≠ e₂)
    (adj : Adjacent e₁ e₂ ) : c e₁ ≠ c e₂ :=
  Coloring.valid c ⟨adj,h⟩

noncomputable def edgeChromaticNumber : ℕ := chromaticNumber (lineGraph G)

variable (v : V) [Fintype (neighborSet G v)]
open Fintype Finset

def edgeSpan : Set (edgeSet G) := fun e => Sym2.Mem v e

instance : Fintype (edgeSpan G v) := sorry

theorem something : edgeSpan G v = (edgeSpan G v).toFinset := sorry
#check edgeSet
def neighborSettoEdge (v' : neighborSet G v) : Sym2 V := ⟦(v,v')⟧
#print Subtype
noncomputable def neighborSetedgeSpanEquiv : (neighborSet G v) ≃ (edgeSpan G v) where
  toFun := fun ⟨v',hv'⟩ => by
    refine ⟨⟨⟦(v,v')⟧,hv'⟩,?_⟩
    · change v ∈ Quotient.mk (Sym2.Rel.setoid V) (v, ↑v')
      rw [Sym2.mem_iff]
      exact Or.inl rfl
  invFun := fun ⟨⟨e,he⟩,he'⟩ => by
    refine ⟨Sym2.Mem.other he',?_⟩
    dsimp [neighborSet]
    rw [←mem_edgeSet,Sym2.other_spec he']
    exact he
  left_inv := fun v' => by sorry
  right_inv := sorry

example (W : Type) (s : Set W) [Fintype s] : Finset W := s.toFinset
  -- @Finset.map s W ⟨(↑),Subtype.coe_injective⟩ elems

theorem edgeSpan_isClique : IsClique (lineGraph G) <| edgeSpan G v := fun _ he₁ _ he₂ ne => ⟨⟨v,⟨he₁,he₂⟩⟩,ne⟩

#check IsClique.card_le_of_coloring

theorem degree_le_edgeColoring [Fintype α] (c : EdgeColoring G α) : G.degree v ≤ Fintype.card α := by
  change (neighborFinset G v).card ≤ Fintype.card α
  -- have : (edgeSpan G v).toFinset = neighborFinset G v := sorry
  -- refine @IsClique.card_le_of_coloring (edgeSet G) (lineGraph G) α (edgeSpan G v).toFinset ?_ _  ?_
  --
  sorry
  -- apply IsClique.card_le_of_coloring (G := lineGraph G)
  -- · exact edgeSpan_isClique G v
  -- · sorry

def restrictedColoring (c : EdgeColoring G α) : G.neighborSet v → α := sorry

theorem ge_degree_of_coloring (n : ℕ) (c : EdgeColoring G (Fin n)) :
    n ≥ G.degree v := by
  by_contra h
  sorry

theorem ge_degree_of_colorable (n : ℕ) (h : G.Colorable n) :
    n ≥ G.degree v := sorry

theorem edgeChromaticNumber_ge_degree :
    edgeChromaticNumber G ≥ G.degree v := by
  sorry


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

! This file was ported from Lean 3 source module order.semiconj_Sup
! leanprover-community/mathlib commit 422e70f7ce183d2900c586a8cda8381e788a0c62
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Logic.Function.Conjugate
import Mathlib.Order.Bounds.OrderIso
import Mathlib.Order.ConditionallyCompleteLattice.Basic
import Mathlib.Order.RelIso.Group
import Mathlib.Order.OrdContinuous
import Mathlib.Algebra.Hom.Equiv.Units.Basic

/-!
# Semiconjugate by `supₛ`

In this file we prove two facts about semiconjugate (families of) functions.

First, if an order isomorphism `fa : α → α` is semiconjugate to an order embedding `fb : β → β` by
`g : α → β`, then `fb` is semiconjugate to `fa` by `y ↦ supₛ {x | g x ≤ y}`, see
`Semiconj.symm_adjoint`.

Second, consider two actions `f₁ f₂ : G → α → α` of a group on a complete lattice by order
isomorphisms. Then the map `x ↦ ⨆ g : G, (f₁ g)⁻¹ (f₂ g x)` semiconjugates each `f₁ g'` to `f₂ g'`,
see `Function.supₛ_div_semiconj`.  In the case of a conditionally complete lattice, a similar
statement holds true under an additional assumption that each set `{(f₁ g)⁻¹ (f₂ g x) | g : G}` is
bounded above, see `Function.csupₛ_div_semiconj`.

The lemmas come from [Étienne Ghys, Groupes d'homéomorphismes du cercle et cohomologie
bornée][ghys87:groupes], Proposition 2.1 and 5.4 respectively. In the paper they are formulated for
homeomorphisms of the circle, so in order to apply results from this file one has to lift these
homeomorphisms to the real line first.
-/

variable {α β γ : Type _}

open Set

/-- We say that `g : β → α` is an order right adjoint function for `f : α → β` if it sends each `y`
to a least upper bound for `{x | f x ≤ y}`. If `α` is a partial order, and `f : α → β` has
a right adjoint, then this right adjoint is unique. -/
def IsOrderRightAdjoint [Preorder α] [Preorder β] (f : α → β) (g : β → α) :=
  ∀ y, IsLUB { x | f x ≤ y } (g y)
#align is_order_right_adjoint IsOrderRightAdjoint

theorem isOrderRightAdjoint_supₛ [CompleteLattice α] [Preorder β] (f : α → β) :
    IsOrderRightAdjoint f fun y => supₛ { x | f x ≤ y } := fun _ => isLUB_supₛ _
#align is_order_right_adjoint_Sup isOrderRightAdjoint_supₛ

theorem isOrderRightAdjoint_csupₛ [ConditionallyCompleteLattice α] [Preorder β] (f : α → β)
    (hne : ∀ y, ∃ x, f x ≤ y) (hbdd : ∀ y, BddAbove { x | f x ≤ y }) :
    IsOrderRightAdjoint f fun y => supₛ { x | f x ≤ y } := fun y => isLUB_csupₛ (hne y) (hbdd y)
#align is_order_right_adjoint_cSup isOrderRightAdjoint_csupₛ

namespace IsOrderRightAdjoint

protected theorem unique [PartialOrder α] [Preorder β] {f : α → β} {g₁ g₂ : β → α}
    (h₁ : IsOrderRightAdjoint f g₁) (h₂ : IsOrderRightAdjoint f g₂) : g₁ = g₂ :=
  funext fun y => (h₁ y).unique (h₂ y)
#align is_order_right_adjoint.unique IsOrderRightAdjoint.unique

theorem right_mono [Preorder α] [Preorder β] {f : α → β} {g : β → α} (h : IsOrderRightAdjoint f g) :
    Monotone g := fun y₁ y₂ hy => ((h y₁).mono (h y₂)) fun _ hx => le_trans hx hy
#align is_order_right_adjoint.right_mono IsOrderRightAdjoint.right_mono

theorem orderIso_comp [Preorder α] [Preorder β] [Preorder γ] {f : α → β} {g : β → α}
    (h : IsOrderRightAdjoint f g) (e : β ≃o γ) : IsOrderRightAdjoint (e ∘ f) (g ∘ e.symm) :=
  fun y => by simpa [e.le_symm_apply] using h (e.symm y)
#align is_order_right_adjoint.order_iso_comp IsOrderRightAdjoint.orderIso_comp

theorem comp_orderIso [Preorder α] [Preorder β] [Preorder γ] {f : α → β} {g : β → α}
    (h : IsOrderRightAdjoint f g) (e : γ ≃o α) : IsOrderRightAdjoint (f ∘ e) (e.symm ∘ g) := by
  intro y
  change IsLUB (e ⁻¹' { x | f x ≤ y }) (e.symm (g y))
  rw [e.isLUB_preimage, e.apply_symm_apply]
  exact h y
#align is_order_right_adjoint.comp_order_iso IsOrderRightAdjoint.comp_orderIso

end IsOrderRightAdjoint

namespace Function

/-- If an order automorphism `fa` is semiconjugate to an order embedding `fb` by a function `g`
and `g'` is an order right adjoint of `g` (i.e. `g' y = supₛ {x | f x ≤ y}`), then `fb` is
semiconjugate to `fa` by `g'`.

This is a version of Proposition 2.1 from [Étienne Ghys, Groupes d'homéomorphismes du cercle et
cohomologie bornée][ghys87:groupes]. -/
theorem Semiconj.symm_adjoint [PartialOrder α] [Preorder β] {fa : α ≃o α} {fb : β ↪o β} {g : α → β}
    (h : Function.Semiconj g fa fb) {g' : β → α} (hg' : IsOrderRightAdjoint g g') :
    Function.Semiconj g' fb fa := by
  refine' fun y => (hg' _).unique _
  rw [← fa.surjective.image_preimage { x | g x ≤ fb y }, preimage_setOf_eq]
  simp only [h.eq, fb.le_iff_le, fa.leftOrdContinuous (hg' _)]
#align function.semiconj.symm_adjoint Function.Semiconj.symm_adjoint

variable {G : Type _}

theorem semiconj_of_isLUB [PartialOrder α] [Group G] (f₁ f₂ : G →* α ≃o α) {h : α → α}
    (H : ∀ x, IsLUB (range fun g' => (f₁ g')⁻¹ (f₂ g' x)) (h x)) (g : G) :
    Function.Semiconj h (f₂ g) (f₁ g) := by
  refine' fun y => (H _).unique _
  have := (f₁ g).leftOrdContinuous (H y)
  rw [← range_comp, ← (Equiv.mulRight g).surjective.range_comp _] at this
  simpa [(· ∘ ·)] using this
#align function.semiconj_of_is_lub Function.semiconj_of_isLUB

/-- Consider two actions `f₁ f₂ : G → α → α` of a group on a complete lattice by order
isomorphisms. Then the map `x ↦ ⨆ g : G, (f₁ g)⁻¹ (f₂ g x)` semiconjugates each `f₁ g'` to `f₂ g'`.

This is a version of Proposition 5.4 from [Étienne Ghys, Groupes d'homéomorphismes du cercle et
cohomologie bornée][ghys87:groupes]. -/
theorem supₛ_div_semiconj [CompleteLattice α] [Group G] (f₁ f₂ : G →* α ≃o α) (g : G) :
    Function.Semiconj (fun x => ⨆ g' : G, (f₁ g')⁻¹ (f₂ g' x)) (f₂ g) (f₁ g) :=
  semiconj_of_isLUB f₁ f₂ (fun _ => isLUB_supᵢ) _
#align function.Sup_div_semiconj Function.supₛ_div_semiconj

/-- Consider two actions `f₁ f₂ : G → α → α` of a group on a conditionally complete lattice by order
isomorphisms. Suppose that each set $s(x)=\{f_1(g)^{-1} (f_2(g)(x)) | g \in G\}$ is bounded above.
Then the map `x ↦ supₛ s(x)` semiconjugates each `f₁ g'` to `f₂ g'`.

This is a version of Proposition 5.4 from [Étienne Ghys, Groupes d'homéomorphismes du cercle et
cohomologie bornée][ghys87:groupes]. -/
theorem csupₛ_div_semiconj [ConditionallyCompleteLattice α] [Group G] (f₁ f₂ : G →* α ≃o α)
    (hbdd : ∀ x, BddAbove (range fun g => (f₁ g)⁻¹ (f₂ g x))) (g : G) :
    Function.Semiconj (fun x => ⨆ g' : G, (f₁ g')⁻¹ (f₂ g' x)) (f₂ g) (f₁ g) :=
  semiconj_of_isLUB f₁ f₂ (fun x => isLUB_csupₛ (range_nonempty _) (hbdd x)) _
#align function.cSup_div_semiconj Function.csupₛ_div_semiconj

-- Guard against import creep
-- Porting note: not implemented yet
-- assert_not_exists finset


/-
Copyright (c) 2015 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura

Tuples are lists of a fixed size.
It is implemented as a subtype.
-/
prelude
import init.data.list init.data.subtype init.meta.interactive init.data.fin

universes u v w

def vector (α : Type u) (n : ℕ) := { l : list α // l.length = n }

namespace vector
variables {α : Type u} {β : Type v} {φ : Type w}
variable {n : ℕ}

instance [decidable_eq α] : decidable_eq (vector α n) :=
begin unfold vector, apply_instance end

@[pattern] def nil : vector α 0 := ⟨[],  rfl⟩

@[pattern] def cons : α → vector α n → vector α (nat.succ n)
| a ⟨ v, h ⟩ := ⟨ a::v, congr_arg nat.succ h ⟩

@[reducible] def length (v : vector α n) : ℕ := n

open nat

def head : vector α (nat.succ n) → α
| ⟨ [],    h ⟩ := by contradiction
| ⟨ a :: v, h ⟩ := a

theorem head_cons (a : α) : Π (v : vector α n), head (cons a v) = a
| ⟨ l, h ⟩ := rfl

def tail : vector α n → vector α (n - 1)
| ⟨ [],     h ⟩ := ⟨ [], congr_arg pred h ⟩
| ⟨ a :: v, h ⟩ := ⟨ v, congr_arg pred h ⟩

theorem tail_cons (a : α) : Π (v : vector α n), tail (cons a v) = v
| ⟨ l, h ⟩ := rfl

@[simp] theorem cons_head_tail : ∀ v : vector α (succ n), (cons (head v) (tail v)) = v
| ⟨ [],     h ⟩ := by contradiction
| ⟨ a :: v, h ⟩ := rfl

def to_list (v : vector α n) : list α := v.1

def nth : Π (v : vector α n), fin n → α | ⟨ l, h ⟩ i := l.nth_le i.1 (by rw h; exact i.2)

def append {n m : nat} : vector α n → vector α m → vector α (n + m)
| ⟨ l₁, h₁ ⟩ ⟨ l₂, h₂ ⟩ := ⟨ l₁ ++ l₂, by simp * ⟩

@[elab_as_eliminator] def elim {α} {C : Π {n}, vector α n → Sort u} (H : ∀l : list α, C ⟨l, rfl⟩)
  {n : nat} : Π (v : vector α n), C v
| ⟨l, h⟩ := match n, h with ._, rfl := H l end

/- map -/

def map (f : α → β) : vector α n → vector β n
| ⟨ l, h ⟩  := ⟨ list.map f l, by simp * ⟩

@[simp]

theorem map_cons (f : α → β) (a : α) : Π (v : vector α n), map f (cons a v) = cons (f a) (map f v)
| ⟨l,h⟩ := rfl

def map₂ (f : α → β → φ) : vector α n → vector β n → vector φ n
| ⟨ x, _ ⟩ ⟨ y, _ ⟩ := ⟨ list.map₂ f x y, by simp * ⟩

def repeat (a : α) (n : ℕ) : vector α n :=
⟨ list.repeat a n, list.length_repeat a n ⟩

def drop (i : ℕ) : vector α n → vector α (n - i)
| ⟨l, p⟩ := ⟨ list.drop i l, by simp * ⟩

def take (i : ℕ) : vector α n → vector α (min i n)
| ⟨l, p⟩ := ⟨ list.take i l, by simp * ⟩

def remove_nth (i : fin n) : vector α n → vector α (n - 1)
| ⟨l, p⟩ := ⟨ list.remove_nth l i.1, by rw [l.length_remove_nth i.1]; rw p; exact i.2 ⟩

def of_fn : Π {n}, (fin n → α) → vector α n
| 0 f := nil
| (n+1) f := cons (f 0) (of_fn (λi, f i.succ))

section accum
  open prod
  variable {σ : Type}

  def map_accumr (f : α → σ → σ × β) : vector α n → σ → σ × vector β n
  | ⟨ x, px ⟩ c :=
    let res := list.map_accumr f x c in
    ⟨ res.1, res.2, by simp * ⟩

  def map_accumr₂ {α β σ φ : Type} (f : α → β → σ → σ × φ)
   : vector α n → vector β n → σ → σ × vector φ n
  | ⟨ x, px ⟩ ⟨ y, py ⟩ c :=
    let res := list.map_accumr₂ f x y c in
    ⟨ res.1, res.2, by simp * ⟩

end accum

protected theorem eq {n : ℕ} : ∀ (a1 a2 : vector α n), to_list a1 = to_list a2 → a1 = a2
| ⟨x, h1⟩ ⟨._, h2⟩ rfl := rfl

protected theorem eq_nil (v : vector α 0) : v = nil :=
v.eq nil (list.eq_nil_of_length_eq_zero v.2)

@[simp] theorem to_list_mk (v : list α) (P : list.length v = n) : to_list (subtype.mk v P) = v :=
rfl

@[simp] theorem to_list_nil : to_list nil = @list.nil α :=
rfl

@[simp] theorem to_list_length (v : vector α n) : (to_list v).length = n := v.2

@[simp] theorem to_list_cons (a : α) (v : vector α n) : to_list (cons a v) = a :: to_list v :=
begin cases v, reflexivity end

@[simp] theorem to_list_append {n m : nat} (v : vector α n) (w : vector α m) : to_list (append v w) = to_list v ++ to_list w :=
begin cases v, cases w, reflexivity end

@[simp] theorem to_list_drop {n m : ℕ} (v : vector α m) : to_list (drop n v) = list.drop n (to_list v) :=
begin cases v, reflexivity end

@[simp] theorem to_list_take {n m : ℕ} (v : vector α m) : to_list (take n v) = list.take n (to_list v) :=
begin cases v, reflexivity end

end vector


-- Copyright (c) 2017 Scott Morrison. All rights reserved.
-- Released under Apache 2.0 license as described in the file LICENSE.
-- Authors: Stephen Morgan, Scott Morrison
import .braided_monoidal_category
import .tensor_with_object

open categories
open categories.functor
open categories.products
open categories.natural_transformation
open categories.monoidal_category

namespace categories.drinfeld_centre

universes u v

structure HalfBraiding (C : Type u) [monoidal_category.{u v} C] : Type (max u v) :=
    (object   : C)
    (commutor : (tensor_on_left object) ⇔ (tensor_on_right object))

variables {C : Type u} [monoidal_category.{u v} C]

structure HalfBraidingMorphism (X Y : HalfBraiding C) : Type v :=
  (morphism : X.object ⟶ Y.object)
  (witness : ∀ Z : C, (X.commutor.morphism.components Z) ≫ ((𝟙 Z) ⊗ morphism) = (morphism ⊗ (𝟙 Z)) ≫ (Y.commutor.morphism.components Z) . obviously)

make_lemma HalfBraidingMorphism.witness
attribute [simp,ematch] HalfBraidingMorphism.witness_lemma

@[applicable] lemma HalfBraidingMorphism_equal
  { X Y : HalfBraiding.{u v} C }
  { f g : HalfBraidingMorphism X Y }
  ( w : f.morphism = g.morphism ) : f = g :=
  begin
    induction f,
    induction g,
    tidy,
  end

instance DrinfeldCentre : category.{(max u v) v} (HalfBraiding C) :=
{ Hom      := λ X Y, HalfBraidingMorphism X Y,
  identity := λ X, { morphism := 𝟙 (X.object) },
  compose  := λ _ _ _ f g, { morphism := f.morphism ≫ g.morphism } }

end categories.drinfeld_centre


/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Johannes Hölzl

Quotient construction on modules
-/

import linear_algebra.basic
import linear_algebra.prod_module
import linear_algebra.subtype_module

universes u v w
variables {α : Type u} {β : Type v} {γ : Type w}
variables [ring α] [module α β] [module α γ] (s : set β) [is_submodule s]
include α

open function

namespace is_submodule

def quotient_rel : setoid β :=
⟨λb₁ b₂, b₁ - b₂ ∈ s,
  assume b, by simp [zero],
  assume b₁ b₂ hb,
  have - (b₁ - b₂) ∈ s, from is_submodule.neg hb,
  by simpa using this,
  assume b₁ b₂ b₃ hb₁₂ hb₂₃,
  have (b₁ - b₂) + (b₂ - b₃) ∈ s, from add hb₁₂ hb₂₃,
  by simpa using this⟩

end is_submodule

namespace quotient_module
open is_submodule

section
variable (β)
/-- Quotient module. `quotient β s` is the quotient of the module `β` by the submodule `s`. -/
def quotient (s : set β) [is_submodule s] : Type v := quotient (quotient_rel s)
end

local notation ` Q ` := quotient β s

def mk {s : set β} [is_submodule s] : β → quotient β s := quotient.mk'

instance {α} {β} {r : ring α} [module α β] (s : set β) [is_submodule s] : has_coe β (quotient β s) := ⟨mk⟩

protected def eq {s : set β} [is_submodule s] {a b : β} : (a : quotient β s) = b ↔ a - b ∈ s :=
quotient.eq'

instance quotient.has_zero : has_zero Q := ⟨mk 0⟩

instance quotient.has_add : has_add Q :=
⟨λa b, quotient.lift_on₂' a b (λa b, ((a + b : β ) : Q)) $
  assume a₁ a₂ b₁ b₂ (h₁ : a₁ - b₁ ∈ s) (h₂ : a₂ - b₂ ∈ s),
  quotient.sound' $
  have (a₁ - b₁) + (a₂ - b₂) ∈ s, from add h₁ h₂,
  show (a₁ + a₂) - (b₁ + b₂) ∈ s, by simpa⟩

instance quotient.has_neg : has_neg Q :=
⟨λa, quotient.lift_on' a (λa, mk (- a)) $ assume a b (h : a - b ∈ s),
  quotient.sound' $
  have - (a - b) ∈ s, from neg h,
  show (-a) - (-b) ∈ s, by simpa⟩

instance quotient.add_comm_group : add_comm_group Q :=
{ zero := 0,
  add  := (+),
  neg  := has_neg.neg,
  add_assoc    := assume a b c, quotient.induction_on₃' a b c $
    assume a b c, quotient_module.eq.2 $
    by simp [is_submodule.zero],
  add_comm     := assume a b, quotient.induction_on₂' a b $
    assume a b, quotient_module.eq.2 $ by simp [is_submodule.zero],
  add_zero     := assume a, quotient.induction_on' a $
    assume a, quotient_module.eq.2 $ by simp [is_submodule.zero],
  zero_add     := assume a, quotient.induction_on' a $
    assume a, quotient_module.eq.2 $ by simp [is_submodule.zero],
  add_left_neg := assume a, quotient.induction_on' a $
    assume a, quotient_module.eq.2 $ by simp [is_submodule.zero] }

instance quotient.has_scalar : has_scalar α Q :=
⟨λa b, quotient.lift_on' b (λb, ((a • b : β) : Q)) $ assume b₁ b₂ (h : b₁ - b₂ ∈ s),
  quotient.sound' $
  have a • (b₁ - b₂) ∈ s, from is_submodule.smul a h,
  show a • b₁ - a • b₂ ∈ s, by simpa [smul_add]⟩

instance quotient.module : module α Q :=
{ smul := (•),
  one_smul     := assume a, quotient.induction_on' a $
    assume a, quotient_module.eq.2 $ by simp [is_submodule.zero],
  mul_smul     := assume a b c, quotient.induction_on' c $
    assume c, quotient_module.eq.2 $ by simp [is_submodule.zero, mul_smul],
  smul_add     := assume a b c, quotient.induction_on₂' b c $
    assume b c, quotient_module.eq.2 $ by simp [is_submodule.zero, smul_add],
  add_smul     := assume a b c, quotient.induction_on' c $
    assume c, quotient_module.eq.2 $ by simp [is_submodule.zero, add_smul], }

@[simp] lemma coe_zero : ((0 : β) : Q) = 0 := rfl
@[simp] lemma coe_smul (a : α) (b : β) : ((a • b : β) : Q) = a • b := rfl
@[simp] lemma coe_add (a b : β) : ((a + b : β) : Q) = a + b := rfl

lemma coe_eq_zero (b : β) : (b : quotient β s) = 0 ↔ b ∈ s :=
by rw [← (coe_zero s), quotient_module.eq]; simp

instance quotient.inhabited : inhabited Q := ⟨0⟩

lemma is_linear_map_quotient_mk : @is_linear_map _ _ Q _ _ _ (λb, mk b : β → Q) :=
by refine {..}; intros; refl

def quotient.lift {f : β → γ} (hf : is_linear_map f) (h : ∀x∈s, f x = 0) (b : Q) : γ :=
b.lift_on' f $ assume a b (hab : a - b ∈ s),
  have f a - f b = 0, by rw [←hf.sub]; exact h _ hab,
  show f a = f b, from eq_of_sub_eq_zero this

@[simp] lemma quotient.lift_mk {f : β → γ} (hf : is_linear_map f) (h : ∀x∈s, f x = 0) (b : β) :
  quotient.lift s hf h (b : Q) = f b :=
rfl

lemma is_linear_map_quotient_lift {f : β → γ} {h : ∀x y, x - y ∈ s → f x = f y}
  (hf : is_linear_map f) : is_linear_map (λq:Q, quotient.lift_on' q f h) :=
⟨assume b₁ b₂, quotient.induction_on₂' b₁ b₂ $ assume b₁ b₂, hf.add b₁ b₂,
  assume a b, quotient.induction_on' b $ assume b, hf.smul a b⟩

lemma quotient.injective_lift [is_submodule s] {f : β → γ} (hf : is_linear_map f)
  (hs : s = {x | f x = 0}) : injective (quotient.lift s hf $ le_of_eq hs) :=
assume a b, quotient.induction_on₂' a b $ assume a b (h : f a = f b), quotient.sound' $
  have f (a - b) = 0, by rw [hf.sub]; simp [h],
  show a - b ∈ s, from hs.symm ▸ this

lemma quotient.exists_rep {s : set β} [is_submodule s] : ∀ q : quotient β s, ∃ b : β, mk b = q :=
@_root_.quotient.exists_rep _ (quotient_rel s)

section vector_space
variables {α' : Type u} {β' : Type v}
variables [field α'] [vector_space α' β'] (s' : set β') [is_submodule s']
omit α
include α'

instance quotient.vector_space : vector_space α' (quotient β' s') := {}

theorem quotient_prod_linear_equiv :
  nonempty ((quotient_module.quotient β' s' × s') ≃ₗ β') :=
let ⟨g, H1, H2⟩ := exists_right_inverse_linear_map_of_surjective
  (quotient_module.is_linear_map_quotient_mk s')
  (quotient_module.quotient.exists_rep) in
have H3 : ∀ b, quotient_module.mk (g b) = b := λ b, congr_fun H2 b,
⟨{ to_fun := λ b, g b.1 + b.2.1,
  inv_fun := λ b, (quotient_module.mk b, ⟨b - g (quotient_module.mk b),
    (quotient_module.coe_eq_zero _ _).1 $
    ((quotient_module.is_linear_map_quotient_mk _).sub _ _).trans $
    by rw [H3, sub_self]⟩),
  left_inv := λ ⟨q, b, h⟩,
    have H4 : quotient_module.mk b = 0,
      from (quotient_module.coe_eq_zero s' b).2 h,
    have H5 : quotient_module.mk (g q + b) = q,
      from ((quotient_module.is_linear_map_quotient_mk _).add _ _).trans $
      by simp only [H3, H4, add_zero],
    prod.ext H5 (subtype.eq $ by simp only [H5, add_sub_cancel']),
  right_inv := λ b, add_sub_cancel'_right _ _,
  linear_fun := ⟨λ ⟨q1, b1, h1⟩ ⟨q2, b2, h2⟩,
      show g (q1 + q2) + (b1 + b2) = (g q1 + b1) + (g q2 + b2),
      by rw [H1.add]; simp only [add_left_comm, add_assoc],
    λ c ⟨q, b, h⟩, show g (c • q) + (c • b) = c • (g q + b),
      by rw [H1.smul, smul_add]⟩ }⟩

end vector_space

end quotient_module


/-
  Specifications file for field_spec.cairo

  Do not modify the constant definitions, structure definitions, or automatic specifications.
  Do not change the name or arguments of the user specifications and soundness theorems.

  You may freely move the definitions around in the file.
  You may add definitions and theorems wherever you wish in this file.
-/
import starkware.cairo.lean.semantics.soundness.prelude
import starkware.cairo.common.cairo_secp.bigint_spec
import starkware.cairo.common.cairo_secp.constants_spec

import starkware.cairo.common.math_spec

open starkware.cairo.common.math
open starkware.cairo.common.cairo_secp.bigint
open starkware.cairo.common.cairo_secp.constants

namespace starkware.cairo.common.cairo_secp.field

variables {F : Type} [field F] [decidable_eq F] [prelude_hyps F]

-- End of automatically generated prelude.

/-
-- Function: unreduced_mul
-/

/- unreduced_mul autogenerated specification -/

-- Do not change this definition.
def auto_spec_unreduced_mul (mem : F → F) (κ : ℕ) (a b : BigInt3 F) (ρ_res_low : UnreducedBigInt3 F) : Prop :=
  18 ≤ κ ∧
  ρ_res_low = {
    d0 := a.d0 * b.d0 + (a.d1 * b.d2 + a.d2 * b.d1) * (4 * SECP_REM),
    d1 := a.d0 * b.d1 + a.d1 * b.d0 + (a.d2 * b.d2) * (4 * SECP_REM),
    d2 := a.d0 * b.d2 + a.d1 * b.d1 + a.d2 * b.d0
  }

-- You may change anything in this definition except the name and arguments.
def spec_unreduced_mul (mem : F → F) (κ : ℕ) (a b : BigInt3 F) (ρ_res_low : UnreducedBigInt3 F) : Prop :=
  ∀ x y : bigint3, a = x.toBigInt3 → b = y.toBigInt3 →
    ρ_res_low = (x.mul y).toUnreducedBigInt3

/- unreduced_mul soundness theorem -/

-- Do not change the statement of this theorem. You may change the proof.
theorem sound_unreduced_mul
    {mem : F → F}
    (κ : ℕ)
    (a b : BigInt3 F) (ρ_res_low : UnreducedBigInt3 F)
    (h_auto : auto_spec_unreduced_mul mem κ a b ρ_res_low) :
  spec_unreduced_mul mem κ a b ρ_res_low :=
begin
  intros x y aeq beq,
  apply eq.trans h_auto.2,
  rw [bigint3.mul, aeq, beq], simp [bigint3.toBigInt3, bigint3.toUnreducedBigInt3]
end

/-
-- Function: unreduced_sqr
-/

/- unreduced_sqr autogenerated specification -/

-- Do not change this definition.
def auto_spec_unreduced_sqr (mem : F → F) (κ : ℕ) (a : BigInt3 F) (ρ_res_low : UnreducedBigInt3 F) : Prop :=
  ∃ twice_d0 : F, twice_d0 = a.d0 * 2 ∧
  13 ≤ κ ∧
  ρ_res_low = {
    d0 := a.d0 * a.d0 + (a.d1 * a.d2) * (2 * 4 * SECP_REM),
    d1 := twice_d0 * a.d1 + (a.d2 * a.d2) * (4 * SECP_REM),
    d2 := twice_d0 * a.d2 + a.d1 * a.d1
  }

-- You may change anything in this definition except the name and arguments.
def spec_unreduced_sqr (mem : F → F) (κ : ℕ) (a : BigInt3 F) (ρ_res_low : UnreducedBigInt3 F) : Prop :=
  ∀ x : bigint3, a = x.toBigInt3 →
    ρ_res_low = (x.sqr).toUnreducedBigInt3

/- unreduced_sqr soundness theorem -/

-- Do not change the statement of this theorem. You may change the proof.
theorem sound_unreduced_sqr
    {mem : F → F}
    (κ : ℕ)
    (a : BigInt3 F) (ρ_res_low : UnreducedBigInt3 F)
    (h_auto : auto_spec_unreduced_sqr mem κ a ρ_res_low) :
  spec_unreduced_sqr mem κ a ρ_res_low :=
begin
  rintros x xeq,
  rcases h_auto with ⟨twice_d0, rfl, _, rfl⟩,
  subst xeq,
  ext; simp [bigint3.toUnreducedBigInt3, bigint3.toBigInt3, bigint3.sqr, bigint3.mul, int.cast_mul, SECP_REM]; ring
end

/-
-- Function: verify_zero
-/

/- verify_zero autogenerated specification -/

-- Do not change this definition.
def auto_spec_verify_zero (mem : F → F) (κ : ℕ) (range_check_ptr : F) (val : UnreducedBigInt3 F) (ρ_range_check_ptr : F) : Prop :=
  ∃ q : F,
  ∃ q_biased : F,
  q_biased = q + 2 ^ 127 ∧
  mem (range_check_ptr) = q_biased ∧
  is_range_checked (rc_bound F) (q_biased) ∧
  ∃ r1 : F, r1 = (val.d0 + q * SECP_REM) / (BASE : ℤ) ∧
  mem (range_check_ptr + 1) = r1 + 2 ^ 127 ∧
  is_range_checked (rc_bound F) (r1 + 2 ^ 127) ∧
  ∃ r2 : F, r2 = (val.d1 + r1) / (BASE : ℤ) ∧
  mem (range_check_ptr + 2) = r2 + 2 ^ 127 ∧
  is_range_checked (rc_bound F) (r2 + 2 ^ 127) ∧
  val.d2 = q * ((BASE : ℤ) / (4 : ℤ)) - r2 ∧
  ∃ range_check_ptr₁ : F, range_check_ptr₁ = range_check_ptr + 3 ∧
  15 ≤ κ ∧
  ρ_range_check_ptr = range_check_ptr₁

-- You may change anything in this definition except the name and arguments.
def spec_verify_zero (mem : F → F) (κ : ℕ) (range_check_ptr : F) (val : UnreducedBigInt3 F) (ρ_range_check_ptr : F) : Prop :=
  ∀ ix : bigint3, val = ix.toUnreducedBigInt3 → ix.bounded (2^250) → ix.val % SECP_PRIME = 0

/- verify_zero soundness theorem -/

noncomputable def rc_to_int {x : F} (h : is_range_checked (rc_bound F) (x + 2^127)) :
  { i : ℤ // x = i ∧ abs i ≤ 2^127 } :=
begin
  rcases classical.some_spec h with ⟨n_ltn, neq⟩,
  use ↑(classical.some h) - 2^127,
  split, { simp [neq.symm] },
  rw abs_le, split, { rw le_sub, simp },
  have h1 : classical.some h ≤ 2^128 := le_trans (le_of_lt n_ltn) (rc_bound_hyp F),
  apply le_trans (sub_le_sub_right (int.coe_nat_le.mpr h1) _),
  simp, norm_num
end

lemma BASE_calc_aux: (↑BASE : ℤ) / 4 * (↑BASE * ↑BASE) = ↑BASE^3 / 4 :=
begin
  rw [pow_succ, mul_comm _ (↑BASE^2), int.mul_div_assoc, mul_comm, pow_two],
  unfold BASE, norm_cast, norm_num
end

-- Do not change the statement of this theorem. You may change the proof.
theorem sound_verify_zero
    {mem : F → F}
    (κ : ℕ)
    (range_check_ptr : F) (val : UnreducedBigInt3 F) (ρ_range_check_ptr : F)
    (h_auto : auto_spec_verify_zero mem κ range_check_ptr val ρ_range_check_ptr) :
  spec_verify_zero mem κ range_check_ptr val ρ_range_check_ptr :=
begin
  rintros ⟨id0, id1, id2⟩ deq abs_id_le,
  rcases unreduced_bigint3_eqs deq with ⟨d0eq, d1eq, d2eq⟩,
  rcases h_auto with
  ⟨q, q_biased, rfl, _, rcq,
   r1, r1_eq, _, rcr1,
   r2, r2_eq, _, rcr2,
   d2_eq, _, ret0_eq⟩,
  rcases rc_to_int rcq with ⟨iq, qeq, abs_iq_le⟩,
  rcases rc_to_int rcr1 with ⟨ir1, r1eq, abs_ir1_le⟩,
  rcases rc_to_int rcr2 with ⟨ir2, r2eq, abs_ir2_le⟩,
  apply int.mod_eq_zero_of_dvd,
  use iq,
  have: r1 * ↑BASE = (val.d0 + q * ↑SECP_REM),
  { rw [r1_eq], field_simp [BASE_ne_zero_aux'] },
  have: (↑(ir1 * ↑BASE) : F) = ↑(id0 + iq * ↑SECP_REM),
  { simp only [qeq.symm, r1eq.symm, d0eq, this, int.cast_mul, int.cast_coe_nat, int.cast_add] },
  have e1 : ir1 * ↑BASE = id0 + iq * ↑SECP_REM,
  { apply PRIME.int_coe_inj this,
    have: abs (id0 + iq * ↑SECP_REM - (ir1 * ↑BASE)) ≤
      2^250 + 2^127 * ↑SECP_REM + 2^127 * ↑BASE,
    { rw sub_eq_add_neg,
      apply le_trans (abs_add _ _),
      apply add_le_add,
      apply le_trans (abs_add _ _),
      rw [abs_mul, abs_of_nonneg SECP_REM_nonneg],
      apply add_le_add abs_id_le.1,
      exact mul_le_mul_of_nonneg_right abs_iq_le SECP_REM_nonneg,
      rw [abs_neg, abs_mul, abs_of_nonneg BASE_nonneg],
      exact mul_le_mul_of_nonneg_right abs_ir1_le BASE_nonneg },
    apply lt_of_le_of_lt this,
    simp only [SECP_REM, BASE, PRIME], simp_int_casts, norm_num1 },
  have: (↑(ir2 * ↑BASE) : F) = ↑(id1 + ir1),
  { simp [r1eq.symm, r2eq.symm, r2_eq, d1eq], field_simp [BASE_ne_zero_aux'] },
  have e2 : ir2 * ↑BASE = id1 + ir1,
  { apply PRIME.int_coe_inj this,
    have: abs (id1 + ir1 - ir2 * ↑BASE) ≤ 2^250 + 2^127 + 2^127 * ↑BASE,
    { apply le_trans (abs_add _ _),
      apply add_le_add,
      apply le_trans (abs_add _ _),
      exact add_le_add abs_id_le.2.1 abs_ir1_le,
      rw [abs_neg, abs_mul, abs_of_nonneg BASE_nonneg],
      exact mul_le_mul_of_nonneg_right abs_ir2_le BASE_nonneg },
    apply lt_of_le_of_lt this,
    simp only [BASE, PRIME], simp_int_casts, norm_num1 },
  have: (4 : ℤ) ∣ ↑BASE, by { simp only [BASE], simp_int_casts, norm_num1 },
  have: (↑(iq * (↑BASE / 4)) : F) = ↑(id2 + ir2),
  { simp [qeq.symm, r2eq.symm, d2eq.symm, d2_eq, sub_mul, mul_assoc],
    rw [int.cast_div], simp, norm_num1, simp [PRIME.four_ne_zero F] },
  have e3 : iq * (↑BASE / 4) = id2 + ir2,
  { apply PRIME.int_coe_inj this,
    have: abs (id2 + ir2 - iq * (↑BASE / 4)) ≤ 2^250 + 2^127 + 2^127 * (↑BASE / 4),
    { apply le_trans (abs_add _ _),
      apply add_le_add,
      { apply le_trans (abs_add _ _),
        apply add_le_add abs_id_le.2.2 abs_ir2_le },
      rw [abs_neg, abs_mul, abs_of_nonneg BASE_div_4_nonneg],
      exact mul_le_mul_of_nonneg_right abs_iq_le BASE_div_4_nonneg },
    apply lt_of_le_of_lt this,
    simp only [BASE, PRIME], simp_int_casts, norm_num1 },
  have e4 := congr_arg (λ x, x * (↑BASE * ↑BASE : int)) e3,
  dsimp at e4,
  have: (↑BASE : ℤ) / 4 * (↑BASE * ↑BASE) = ↑BASE^3 / 4,
  { rw [pow_succ, mul_comm _ (↑BASE^2), int.mul_div_assoc, mul_comm, pow_two],
    unfold BASE, norm_cast, norm_num},
  rw [add_mul, ←mul_assoc ir2, e2, add_mul, e1, mul_assoc iq, this, ←sub_eq_zero] at e4,
  -- show bigint_val id0 id1 id2 = ((↑BASE : ℤ)^3 / 4 - ↑SECP_REM) * iq,
  rw [SECP_PRIME_eq, bigint3.val, ←sub_eq_zero, ←neg_eq_zero, ←e4, pow_two], ring
end

/-
-- Function: is_zero
-/

/- is_zero autogenerated specification -/

-- Do not change this definition.
def auto_spec_is_zero (mem : F → F) (κ : ℕ) (range_check_ptr : F) (x : BigInt3 F) (ρ_range_check_ptr ρ_res : F) : Prop :=
  ∃ ιχ__temp40 : F,
  ((ιχ__temp40 ≠ 0 ∧
    ∃ (κ₁ : ℕ) (range_check_ptr₁ : F), spec_verify_zero mem κ₁ range_check_ptr {
      d0 := x.d0,
      d1 := x.d1,
      d2 := x.d2
    } range_check_ptr₁ ∧
    κ₁ + 9 ≤ κ ∧
    ρ_range_check_ptr = range_check_ptr₁ ∧
    ρ_res = 1) ∨
   (ιχ__temp40 = 0 ∧
    ∃ (κ₁ : ℕ) (range_check_ptr₁ : F) (x_inv : BigInt3 F), spec_nondet_bigint3 mem κ₁ range_check_ptr range_check_ptr₁ x_inv ∧
    ∃ (κ₂ : ℕ) (x_x_inv : UnreducedBigInt3 F), spec_unreduced_mul mem κ₂ x x_inv x_x_inv ∧
    ∃ (κ₃ : ℕ) (range_check_ptr₂ : F), spec_verify_zero mem κ₃ range_check_ptr₁ {
      d0 := x_x_inv.d0 - 1,
      d1 := x_x_inv.d1,
      d2 := x_x_inv.d2
    } range_check_ptr₂ ∧
    κ₁ + κ₂ + κ₃ + 19 ≤ κ ∧
    ρ_range_check_ptr = range_check_ptr₂ ∧
    ρ_res = 0))

-- You may change anything in this definition except the name and arguments.
def spec_is_zero (mem : F → F) (κ : ℕ) (range_check_ptr : F) (x : BigInt3 F) (ρ_range_check_ptr ρ_res : F) : Prop :=
  ∀ ix : bigint3, x = ix.toBigInt3 → ix.bounded (2^107) →
    ((ix.val % SECP_PRIME = 0 ∧ ρ_res = 1) ∨
      (ix.val % SECP_PRIME ≠ 0 ∧ ρ_res = 0))

/- is_zero soundness theorem -/

def bigint3.one : bigint3 := ⟨1, 0, 0⟩

theorem bigint3.one_val : bigint3.one.val = 1 :=
by simp [bigint3.one, bigint3.val]

theorem SECP_PRIME_gt_one : 1 < (SECP_PRIME : ℤ) :=
by { rw [SECP_PRIME_eq, BASE, SECP_REM], simp_int_casts, norm_num1 }

-- Do not change the statement of this theorem. You may change the proof.
theorem sound_is_zero
    {mem : F → F}
    (κ : ℕ)
    (range_check_ptr : F) (x : BigInt3 F) (ρ_range_check_ptr ρ_res : F)
    (h_auto : auto_spec_is_zero mem κ range_check_ptr x ρ_range_check_ptr ρ_res) :
  spec_is_zero mem κ range_check_ptr x ρ_range_check_ptr ρ_res :=
begin
  intros ix xeq ixbdd,
  rcases h_auto with ⟨ιχ__temp40, ⟨ιχ__temp40_nez, case_neg⟩ | ⟨ιχ__temp32_ez, case_pos⟩⟩,
  { rcases case_neg with ⟨_, _, h_verify_zero, _, _, ρ_res_eq⟩,
    have ixbdd' : ix.bounded (2^250),
    { apply bigint3.bounded_of_bounded_of_le ixbdd,
      norm_num },
    have := h_verify_zero ix (by rw xeq; refl) ixbdd',
    left, use [this, ρ_res_eq] },
  rcases case_pos with ⟨_, _, x_inv, h_x_inv, _, x_x_inv, h_x_x_inv, _, _, h_verify_zero, _, _, ρ_res_eq⟩,
  rcases nondet_bigint3_corr h_x_inv with ⟨ix_inv, x_inv_eq, ix_inv_bdd⟩,
  have := h_x_x_inv ix ix_inv xeq x_inv_eq,
  have : ((ix.mul ix_inv).sub bigint3.one).toUnreducedBigInt3 =
    {d0 := x_x_inv.d0 - 1, d1 := x_x_inv.d1, d2 := x_x_inv.d2},
  { rw this, simp [bigint3.toUnreducedBigInt3, bigint3.sub, bigint3.one], },
  have := h_verify_zero _ this.symm,
  have := this _,
  rw [bigint3.sub_val, bigint3.one_val, ←int.modeq_iff_sub_mod_eq_zero] at this,
  have h := (bigint3.mul_val _ _).symm.trans this,
  right,
  refine ⟨_, ρ_res_eq⟩,
  { intro ixval_mod_eq_zero,
    have : ix.val * ix_inv.val ≡ 0 [ZMOD ↑SECP_PRIME],
    { apply int.modeq.trans,
      apply int.modeq.mul (int.modeq_zero_iff.mpr ixval_mod_eq_zero) int.modeq.rfl,
      rw zero_mul },
    rw [int.modeq, int.zero_mod] at this,
    rw [int.modeq, this, int.mod_eq_of_lt] at h,
    apply zero_ne_one h,
    norm_num,
    exact SECP_PRIME_gt_one },
  have : ((2^107 : ℤ)^2 * (8 * SECP_REM + 1)) + 1 ≤ 2^250,
  { simp, norm_num },
  apply bigint3.bounded_of_bounded_of_le _ this,
  apply bigint3.bounded_sub, swap,
  { simp [bigint3.bounded, bigint3.one] },
  apply bigint3.bounded_mul ixbdd,
  apply bigint3.bounded_of_bounded_of_le ix_inv_bdd,
  norm_num
end

/-
-- Function: reduce
-/

/- reduce autogenerated specification -/

-- Do not change this definition.
def auto_spec_reduce (mem : F → F) (κ : ℕ) (range_check_ptr : F) (x : UnreducedBigInt3 F) (ρ_range_check_ptr : F) (ρ_reduced_x : BigInt3 F) : Prop :=
  ∃ (κ₁ : ℕ) (range_check_ptr₁ : F) (reduced_x : BigInt3 F), spec_nondet_bigint3 mem κ₁ range_check_ptr range_check_ptr₁ reduced_x ∧
  ∃ (κ₂ : ℕ) (range_check_ptr₂ : F), spec_verify_zero mem κ₂ range_check_ptr₁ {
    d0 := x.d0 - reduced_x.d0,
    d1 := x.d1 - reduced_x.d1,
    d2 := x.d2 - reduced_x.d2
  } range_check_ptr₂ ∧
  κ₁ + κ₂ + 11 ≤ κ ∧
  ρ_range_check_ptr = range_check_ptr₂ ∧
  ρ_reduced_x = reduced_x

-- You may change anything in this definition except the name and arguments.
def spec_reduce (mem : F → F) (κ : ℕ) (range_check_ptr : F) (x : UnreducedBigInt3 F) (ρ_range_check_ptr : F) (ρ_reduced_x : BigInt3 F) : Prop :=
  ∀ ix : bigint3,
    -- We could get by with 2^250 - 3 * (BASE - 1).
    ix.bounded (2^249) →
    x = ix.toUnreducedBigInt3 →
    ∃ ix' : bigint3,
      ix'.bounded (3 * (BASE - 1)) ∧
      ix'.val ≡ ix.val [ZMOD SECP_PRIME] ∧
      ρ_reduced_x = ix'.toBigInt3

/- reduce soundness theorem -/

-- Do not change the statement of this theorem. You may change the proof.
theorem sound_reduce
    {mem : F → F}
    (κ : ℕ)
    (range_check_ptr : F) (x : UnreducedBigInt3 F) (ρ_range_check_ptr : F) (ρ_reduced_x : BigInt3 F)
    (h_auto : auto_spec_reduce mem κ range_check_ptr x ρ_range_check_ptr ρ_reduced_x) :
  spec_reduce mem κ range_check_ptr x ρ_range_check_ptr ρ_reduced_x :=
begin
  intros ix ixbdd xeq,
  rcases h_auto with ⟨_, _, reduced_x, h_reduced_x, _, _, h_verify_zero, _, _, ρ_reduced_x_eq⟩,
  rcases nondet_bigint3_corr h_reduced_x with ⟨ix', reduced_x_eq, ix'bdd⟩,
  have : (ix.sub ix').val % SECP_PRIME = 0,
  { apply h_verify_zero,
    { rw [bigint3.toUnreducedBigInt3_sub, ←xeq, UnreducedBigInt3.sub, reduced_x_eq,
          bigint3.toUnreducedBigInt3, bigint3.toBigInt3] },
      have : (2^249 : ℤ) + 3 * (↑BASE - 1) ≤ 2^250,
      { simp, norm_num },
      apply bigint3.bounded_of_bounded_of_le _ this,
      exact bigint3.bounded_sub ixbdd ix'bdd },
  use [ix', ix'bdd],
  rw [bigint3.sub_val, ←int.mod_eq_mod_iff_mod_sub_eq_zero] at this,
  use this.symm,
  exact ρ_reduced_x_eq.trans reduced_x_eq
end

/-
-- Function: validate_reduced_field_element
-/

/- validate_reduced_field_element autogenerated specification -/

-- Do not change this definition.
def auto_spec_validate_reduced_field_element (mem : F → F) (κ : ℕ) (range_check_ptr : F) (val : BigInt3 F) (ρ_range_check_ptr : F) : Prop :=
  ∃ (κ₁ : ℕ) (range_check_ptr₁ : F), spec_assert_nn_le mem κ₁ range_check_ptr val.d2 P2 range_check_ptr₁ ∧
  ∃ (κ₂ : ℕ) (range_check_ptr₂ : F), spec_assert_nn_le mem κ₂ range_check_ptr₁ val.d1 (BASE - 1) range_check_ptr₂ ∧
  ∃ (κ₃ : ℕ) (range_check_ptr₃ : F), spec_assert_nn_le mem κ₃ range_check_ptr₂ val.d0 (BASE - 1) range_check_ptr₃ ∧
  ((val.d2 = P2 ∧
    ((val.d1 = P1 ∧
      ∃ (κ₄ : ℕ) (range_check_ptr₄ : F), spec_assert_nn_le mem κ₄ range_check_ptr₃ val.d0 (P0 - 1) range_check_ptr₄ ∧
      κ₁ + κ₂ + κ₃ + κ₄ + 19 ≤ κ ∧
      ρ_range_check_ptr = range_check_ptr₄) ∨
     (val.d1 ≠ P1 ∧
      ∃ (κ₄ : ℕ) (range_check_ptr₄ : F), spec_assert_nn_le mem κ₄ range_check_ptr₃ val.d1 (P1 - 1) range_check_ptr₄ ∧
      κ₁ + κ₂ + κ₃ + κ₄ + 19 ≤ κ ∧
      ρ_range_check_ptr = range_check_ptr₄))) ∨
   (val.d2 ≠ P2 ∧
    κ₁ + κ₂ + κ₃ + 14 ≤ κ ∧
    ρ_range_check_ptr = range_check_ptr₃))

-- You may change anything in this definition except the name and arguments.
def spec_validate_reduced_field_element (mem : F → F) (κ : ℕ) (range_check_ptr : F) (val : BigInt3 F) (ρ_range_check_ptr : F) : Prop :=
  ∃ n0 n1 n2 : ℕ,
    n0 ≤ BASE - 1 ∧
    n1 ≤ BASE - 1 ∧
    n2 ≤ P2 ∧
    n2 * BASE^2 + n1 * BASE + n0 < SECP_PRIME ∧
    val = ⟨n0, n1, n2⟩

/- validate_reduced_field_element soundness theorem -/

theorem P0_pos : 0 < P0 :=
by { rw P0, norm_num }

theorem P1_pos : 0 < P1 :=
by { rw P1, norm_num }

theorem P2_pos : 0 < P2 :=
by { rw P2, norm_num }

theorem BASE_pos : 0 < BASE :=
by { rw BASE, norm_num }

theorem coe_P0m1_eq :
  ((↑P0 : F) - 1 = ↑(P0 - 1)) :=
begin
  rw [nat.cast_sub, nat.cast_one],
  exact P0_pos,
end

theorem coe_P1m1_eq :
  ((↑P1 : F) - 1 = ↑(P1 - 1)) :=
begin
  rw [nat.cast_sub, nat.cast_one],
  exact P1_pos,
end

theorem coe_BASEm1_eq :
  ((↑BASE : F) - 1 = ↑(BASE - 1)) :=
begin
  rw [nat.cast_sub, nat.cast_one],
  exact BASE_pos,
end

theorem aux0 {m p : ℕ} (hm : m < rc_bound F) (hp : p < PRIME) (h : (m : F) = p) : m = p :=
begin
  apply @PRIME.nat_coe_field_inj F _ _ _ _ _ hp h,
  apply lt_of_lt_of_le hm,
  apply le_trans (rc_bound_hyp F),
  rw PRIME, norm_num
end

theorem aux1 {m n p : ℕ} (hm : m < rc_bound F) (hn : n < rc_bound F)
(hp : p < PRIME) (h : (↑(m + n) : F) = p) : m ≤ p :=
begin
  have : m + n = p,
  { apply @PRIME.nat_coe_field_inj F _ _ _ _ _ hp h,
    apply lt_of_lt_of_le (add_lt_add hm hn),
    apply le_trans (add_le_add (rc_bound_hyp F) (rc_bound_hyp F)),
    rw PRIME, norm_num },
  rw ←this,
  apply nat.le_add_right
end

-- Do not change the statement of this theorem. You may change the proof.
theorem sound_validate_reduced_field_element
    {mem : F → F}
    (κ : ℕ)
    (range_check_ptr : F) (val : BigInt3 F) (ρ_range_check_ptr : F)
    (h_auto : auto_spec_validate_reduced_field_element mem κ range_check_ptr val ρ_range_check_ptr) :
  spec_validate_reduced_field_element mem κ range_check_ptr val ρ_range_check_ptr :=
begin
  rcases h_auto with ⟨
    _, _, ⟨m2, n2, m2lt, n2lt, vald2eq, P2eq⟩,
    _, _, ⟨m1, n1, m1lt, n1lt, vald1eq, m1n1eq⟩,
    _, _, ⟨m0, n0, m0lt, n0lt, vald0eq, m0n0eq⟩,
    hcases⟩,
  have m2leP2 : m2 ≤ P2,
  { apply aux1 m2lt n2lt _ P2eq.symm,
    rw PRIME, norm_num },
  have m1leBASEm1 : m1 ≤ BASE - 1,
  { rw coe_BASEm1_eq at m1n1eq,
    apply aux1 m1lt n1lt _ m1n1eq.symm,
    rw [PRIME, BASE], norm_num },
  have m0leBASEm1 : m0 ≤ BASE - 1,
  { rw coe_BASEm1_eq at m0n0eq,
    apply aux1 m0lt n0lt _ m0n0eq.symm,
    rw [PRIME, BASE], norm_num },
  rcases hcases with ⟨d2eq, hcases⟩ | ⟨d2ne, _, _⟩,
  { have m2eqP2: m2 = P2,
    { apply aux0 m2lt _ (vald2eq.symm.trans d2eq),
      rw [PRIME, P2], norm_num },
    rcases hcases with
      ⟨d1eq, _, _, ⟨m0', n0', m0'lt, n0'lt, vald0eq', P0m1eq⟩, _, _⟩ |
      ⟨d1ne, _, _, ⟨m1', n1', m1'lt, n1'lt, vald1eq', P1m1eq⟩, _, _⟩,
    { have m1eqP1: m1 = P1,
      { apply aux0 m1lt _ (vald1eq.symm.trans d1eq),
        rw [PRIME, P1], norm_num },
      have m0'leP0m1 : m0' ≤ P0 - 1,
      { rw coe_P0m1_eq at P0m1eq,
        apply aux1 m0'lt n0'lt _ P0m1eq.symm,
        rw [PRIME, P0], norm_num },
      use [m0', m1, m2], split,
      { apply le_trans m0'leP0m1,
        rw [BASE, P0], norm_num },
      split,
      {  exact m1leBASEm1 },
      split,
      { rw m2eqP2 },
      split,
      { apply lt_of_le_of_lt,
        apply add_le_add_left m0'leP0m1,
        rw [m2eqP2, P2, m1eqP1, P1, P0, SECP_PRIME, BASE, SECP_REM],
        norm_num },
      rw BigInt3.ext_iff,
      use [vald0eq', vald1eq, vald2eq] },
    have m1'leP1m1 : m1' ≤ P1 - 1,
    { rw coe_P1m1_eq at P1m1eq,
      apply aux1 m1'lt n1'lt _ P1m1eq.symm,
      rw [PRIME, P1], norm_num },
    use [m0, m1', m2], split,
    use [m0leBASEm1],
    split,
    { apply le_trans m1'leP1m1,
      rw [BASE, P1], norm_num },
    split,
    { exact m2leP2 },
    split,
    { apply lt_of_le_of_lt,
      apply add_le_add,
      apply add_le_add_left,
      apply nat.mul_le_mul_right _ m1'leP1m1,
      exact m0leBASEm1,
      rw [m2eqP2, SECP_PRIME, SECP_REM, BASE, P2, P1], norm_num },
    rw BigInt3.ext_iff,
    use [vald0eq, vald1eq', vald2eq] },
  have : m2 ≠ P2,
  { intro h,
    apply d2ne,
    rw [vald2eq, h] },
  have m2leP2m1 : m2 ≤ P2 - 1,
  { apply nat.le_pred_of_lt,
    apply lt_of_le_of_ne m2leP2 this },
  use [m0, m1, m2, m0leBASEm1, m1leBASEm1, m2leP2],
  split,
  { apply lt_of_le_of_lt,
    apply add_le_add,
    apply add_le_add,
    apply nat.mul_le_mul_right _ m2leP2m1,
    apply nat.mul_le_mul_right _ m1leBASEm1,
    apply m0leBASEm1,
    rw [SECP_PRIME, SECP_REM, BASE, P2], norm_num },
  rw BigInt3.ext_iff,
  use [vald0eq, vald1eq, vald2eq]
end

/-
def spec_validate_reduced_field_element' (mem : F → F) (κ : ℕ) (range_check_ptr : F) (val : BigInt3 F) (ρ_range_check_ptr : F) : Prop :=
  ∃ ival : bigint3,
    ival.bounded ↑(BASE - 1) ∧
    ival.val < SECP_PRIME ∧
    val = ival.toBigInt3

theorem sound_validate_reduced_field_element'
    {mem : F → F}
    (κ : ℕ)
    (range_check_ptr : F) (val : BigInt3 F) (ρ_range_check_ptr : F)
    (h_auto : auto_spec_validate_reduced_field_element mem κ range_check_ptr val ρ_range_check_ptr) :
  spec_validate_reduced_field_element mem κ range_check_ptr val ρ_range_check_ptr :=
begin
  rcases h_auto with ⟨
    _, _, ⟨m2, n2, m2lt, n2lt, vald2eq, P2eq⟩,
    _, _, ⟨m1, n1, m1lt, n1lt, vald1eq, m1n1eq⟩,
    _, _, ⟨m0, n0, m0lt, n0lt, vald0eq, m0n0eq⟩,
    hcases⟩,
  have m2leP2 : m2 ≤ P2,
  { apply aux1 m2lt n2lt _ P2eq.symm,
    rw PRIME, norm_num },
  have m1leBASEm1 : m1 ≤ BASE - 1,
  { rw coe_BASEm1_eq at m1n1eq,
    apply aux1 m1lt n1lt _ m1n1eq.symm,
    rw [PRIME, BASE], norm_num },
  have m0leBASEm1 : m0 ≤ BASE - 1,
  { rw coe_BASEm1_eq at m0n0eq,
    apply aux1 m0lt n0lt _ m0n0eq.symm,
    rw [PRIME, BASE], norm_num },
  rcases hcases with ⟨d2eq, hcases⟩ | ⟨d2ne, _, _⟩,
  { have m2eqP2: m2 = P2,
    { apply aux0 m2lt _ (vald2eq.symm.trans d2eq),
      rw [PRIME, P2], norm_num },
    rcases hcases with
      ⟨d1eq, _, _, ⟨m0', n0', m0'lt, n0'lt, vald0eq', P0m1eq⟩, _, _⟩ |
      ⟨d1ne, _, _, ⟨m1', n1', m1'lt, n1'lt, vald1eq', P1m1eq⟩, _, _⟩,
    { have m1eqP1: m1 = P1,
      { apply aux0 m1lt _ (vald1eq.symm.trans d1eq),
        rw [PRIME, P1], norm_num },
      have m0'leP0m1 : m0' ≤ P0 - 1,
      { rw coe_P0m1_eq at P0m1eq,
        apply aux1 m0'lt n0'lt _ P0m1eq.symm,
        rw [PRIME, P0], norm_num },
      use ⟨m0', m1, m2⟩, split,
      { simp only [bigint3.bounded, int.coe_nat_le, int.coe_nat_abs],
        split,
        { apply le_trans m0'leP0m1,
          rw [BASE, P0], norm_num },
        split,
        {  exact m1leBASEm1 },
        rw [m2eqP2, BASE, P2], norm_num },
      split,
      { have : (m0' : ℤ) ≤ ↑(P0 - 1),
        { rw [int.coe_nat_le], exact m0'leP0m1 },
        rw [bigint3.val, m2eqP2, m1eqP1], dsimp,
        refine lt_of_le_of_lt (add_le_add_left this _) _,
        rw [P2, P1, P0, SECP_PRIME_eq, BASE, SECP_REM],
        simp_int_casts, norm_num },
      rw [bigint3.toBigInt3, BigInt3.ext_iff],
      dsimp,
      rw [vald0eq', d2eq, d1eq, m1eqP1, m2eqP2],
      exact ⟨rfl, rfl, rfl⟩ },
    have m1'leP1m1 : m1' ≤ P1 - 1,
    { rw coe_P1m1_eq at P1m1eq,
      apply aux1 m1'lt n1'lt _ P1m1eq.symm,
      rw [PRIME, P1], norm_num },
    use ⟨m0, m1', m2⟩, split,
    { simp only [bigint3.bounded, int.coe_nat_le, int.coe_nat_abs],
      use [m0leBASEm1],
      split,
      { apply le_trans m1'leP1m1,
        rw [BASE, P1], norm_num },
      rw [m2eqP2, BASE, P2], norm_num },
    split,
    { have h1: (m1' : ℤ) * BASE ≤ ↑(P1 - 1) * BASE,
      { apply mul_le_mul_of_nonneg_right _ (int.coe_nat_nonneg _),
        rw [int.coe_nat_le],
        exact m1'leP1m1 },
      have h2 : (m0 : ℤ) ≤ ↑(BASE - 1),
      { rw [int.coe_nat_le],
        exact m0leBASEm1 },
      rw [bigint3.val, m2eqP2], dsimp,
      apply lt_of_le_of_lt,
      apply add_le_add (add_le_add_left h1 _) h2,
      rw [P2, P1, SECP_PRIME_eq, BASE, SECP_REM],
      simp_int_casts, norm_num },
    rw [bigint3.toBigInt3, BigInt3.ext_iff],
    dsimp,
    rw [vald0eq, vald1eq', d2eq, m2eqP2],
    exact ⟨rfl, rfl, rfl⟩ },
  have : m2 ≠ P2,
  { intro h,
    apply d2ne,
    rw [vald2eq, h] },
  have m2leP2m1 : m2 ≤ P2 - 1,
  { apply nat.le_pred_of_lt,
    apply lt_of_le_of_ne m2leP2 this },
  use ⟨m0, m1, m2⟩,
  split,
  { simp only [bigint3.bounded, int.coe_nat_le, int.coe_nat_abs],
    use [m0leBASEm1, m1leBASEm1],
    apply le_trans m2leP2m1,
    rw [BASE, P2], norm_num },
  split,
  { rw bigint3.val, dsimp,
    have h0: (m2 : ℤ) * BASE^2 ≤ ↑(P2 - 1) * BASE^2,
    { apply mul_le_mul_of_nonneg_right _ (int.coe_nat_nonneg _),
      rw [int.coe_nat_le],
      exact m2leP2m1 },
    have h1: (m1 : ℤ) * BASE ≤ ↑(BASE - 1) * BASE,
    { apply mul_le_mul_of_nonneg_right _ (int.coe_nat_nonneg _),
      rw [int.coe_nat_le],
      exact m1leBASEm1 },
    have h2 : (m0 : ℤ) ≤ ↑(BASE - 1),
    { rw [int.coe_nat_le],
      exact m0leBASEm1 },
    apply lt_of_le_of_lt,
    apply add_le_add  (add_le_add h0 h1) h2,
    rw [int.coe_nat_sub, int.coe_nat_sub],
    rw [P2, SECP_PRIME_eq, BASE, SECP_REM],
    simp_int_casts, repeat { norm_num [BASE] } },
  rw [bigint3.toBigInt3, BigInt3.ext_iff],
  dsimp,
  use [vald0eq, vald1eq, vald2eq]
end
-/

end starkware.cairo.common.cairo_secp.field


import ring_theory.noetherian
import ring_theory.algebra_operations

local attribute [instance] classical.prop_decidable

namespace submodule
open algebra
variables {R : Type*} {A : Type*} [comm_ring R] [comm_ring A] [algebra R A]

local attribute [instance] set.pointwise_mul_semiring
local attribute [instance] set.singleton.is_monoid_hom

set_option class.instance_max_depth 80

lemma smul_eq_smul_span_int (S : set R) (I : ideal R) :
  (↑(S • I) : set R) = (↑(S • (span ℤ (↑I : set R))) : set R) :=
begin
  conv_lhs {erw ← span_eq I},
  dsimp only [(•)],
  erw [span_mul_span, span_mul_span],
  apply set.subset.antisymm,
  all_goals {
    intros x hx,
    apply span_induction hx,
    { intros, apply subset_span, assumption },
    { apply submodule.zero_mem (span _ _) },
    { intros, apply submodule.add_mem (span _ _), assumption' },
  { intros a si hsi,
    apply span_induction hsi,
    { rintros _ ⟨s, hs, i, hi, rfl⟩,
      apply subset_span,
      refine ⟨s, hs, a * i, I.mul_mem_left hi, _⟩,
      rw [← mul_assoc, mul_comm s a, mul_assoc, smul_def''],
      refl },
    { rw smul_zero, apply submodule.zero_mem (span _ _) },
    { intros, rw smul_add, apply submodule.add_mem (span _ _), assumption' },
    { intros b si hsi,
      rw [show a • b • si = b • a • si, by {simp}],
      apply submodule.smul_mem (span _ _) b hsi } } }
end

section
variables {B : Type*} [comm_ring B] [algebra R B]
variables (S : subalgebra R B)

lemma span_mono' (X : set B) : (↑(span R X) : set B) ⊆ span S X :=
λ b hb, span_induction hb
  (λ x hx, subset_span hx)
  (span S X).zero_mem
  (λ x y hx hy, (span S X).add_mem hx hy)
  (λ r b hb, by { rw algebra.smul_def, exact (span S X).smul_mem (algebra_map S r) hb })

lemma span_span' (X : set B) : span S ↑(span R X) = span S X :=
le_antisymm (span_le.mpr $ span_mono' S X) (span_mono subset_span)

lemma span_span_int (S' : set B) [is_subring S'] (X : set B) : span S' ↑(span ℤ X) = span S' X :=
le_antisymm
begin
  rw span_le,
  intros x hx,
  refine span_induction hx (λ x hx, subset_span hx) (span S' X).zero_mem
    (λ x y hx hy, (span S' X).add_mem hx hy) _,
  intros n b hb,
  erw [smul_def'', ← gsmul_eq_mul],
  apply is_add_subgroup.gsmul_mem hb,
end
(span_mono subset_span)

end

instance mul_action_algebra : mul_action A (submodule R A) :=
{ smul := λ a M, ({a} : set A) • M,
  mul_smul := λ s t P, show ({s * t} : set A) • _ = _,
    by { rw [is_mul_hom.map_mul (singleton : A → set A)], apply mul_smul },
  one_smul := (submodule.semimodule_set R A).one_smul }

end submodule


/-
Copyright (c) 2017 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
import data.fintype.basic
import data.finset.pi

/-!
# fintype instances for pi types

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

variables {α : Type*}

open finset

namespace fintype

variables [decidable_eq α] [fintype α] {δ : α → Type*}

/-- Given for all `a : α` a finset `t a` of `δ a`, then one can define the
finset `fintype.pi_finset t` of all functions taking values in `t a` for all `a`. This is the
analogue of `finset.pi` where the base finset is `univ` (but formally they are not the same, as
there is an additional condition `i ∈ finset.univ` in the `finset.pi` definition). -/
def pi_finset (t : Π a, finset (δ a)) : finset (Π a, δ a) :=
(finset.univ.pi t).map ⟨λ f a, f a (mem_univ a), λ _ _, by simp [function.funext_iff]⟩

@[simp] lemma mem_pi_finset {t : Π a, finset (δ a)} {f : Π a, δ a} :
  f ∈ pi_finset t ↔ ∀ a, f a ∈ t a :=
begin
  split,
  { simp only [pi_finset, mem_map, and_imp, forall_prop_of_true, exists_prop, mem_univ,
               exists_imp_distrib, mem_pi],
    rintro g hg hgf a,
    rw ← hgf,
    exact hg a },
  { simp only [pi_finset, mem_map, forall_prop_of_true, exists_prop, mem_univ, mem_pi],
    exact λ hf, ⟨λ a ha, f a, hf, rfl⟩ }
end

@[simp] lemma coe_pi_finset (t : Π a, finset (δ a)) :
  (pi_finset t : set (Π a, δ a)) = set.pi set.univ (λ a, t a) :=
set.ext $ λ x, by { rw set.mem_univ_pi, exact fintype.mem_pi_finset }

lemma pi_finset_subset (t₁ t₂ : Π a, finset (δ a)) (h : ∀ a, t₁ a ⊆ t₂ a) :
  pi_finset t₁ ⊆ pi_finset t₂ :=
λ g hg, mem_pi_finset.2 $ λ a, h a $ mem_pi_finset.1 hg a

@[simp] lemma pi_finset_empty [nonempty α] : pi_finset (λ _, ∅ : Π i, finset (δ i)) = ∅ :=
eq_empty_of_forall_not_mem $ λ _, by simp

@[simp] lemma pi_finset_singleton (f : Π i, δ i) :
  pi_finset (λ i, {f i} : Π i, finset (δ i)) = {f} :=
ext $ λ _, by simp only [function.funext_iff, fintype.mem_pi_finset, mem_singleton]

lemma pi_finset_subsingleton {f : Π i, finset (δ i)}
  (hf : ∀ i, (f i : set (δ i)).subsingleton) :
  (fintype.pi_finset f : set (Π i, δ i)).subsingleton :=
λ a ha b hb, funext $ λ i, hf _ (mem_pi_finset.1 ha _) (mem_pi_finset.1 hb _)

lemma pi_finset_disjoint_of_disjoint
  (t₁ t₂ : Π a, finset (δ a)) {a : α} (h : disjoint (t₁ a) (t₂ a)) :
  disjoint (pi_finset t₁) (pi_finset t₂) :=
disjoint_iff_ne.2 $ λ f₁ hf₁ f₂ hf₂ eq₁₂,
disjoint_iff_ne.1 h (f₁ a) (mem_pi_finset.1 hf₁ a) (f₂ a) (mem_pi_finset.1 hf₂ a) (congr_fun eq₁₂ a)

end fintype

/-! ### pi -/

/-- A dependent product of fintypes, indexed by a fintype, is a fintype. -/
instance pi.fintype {α : Type*} {β : α → Type*}
  [decidable_eq α] [fintype α] [∀ a, fintype (β a)] : fintype (Π a, β a) :=
⟨fintype.pi_finset (λ _, univ), by simp⟩

@[simp] lemma fintype.pi_finset_univ {α : Type*} {β : α → Type*}
  [decidable_eq α] [fintype α] [∀ a, fintype (β a)] :
  fintype.pi_finset (λ a : α, (finset.univ : finset (β a))) = (finset.univ : finset (Π a, β a)) :=
rfl

instance function.embedding.fintype {α β} [fintype α] [fintype β] [decidable_eq α]
  [decidable_eq β] : fintype (α ↪ β) :=
fintype.of_equiv _ (equiv.subtype_injective_equiv_embedding α β)

@[simp] lemma finset.univ_pi_univ {α : Type*} {β : α → Type*}
  [decidable_eq α] [fintype α] [∀ a, fintype (β a)] :
  finset.univ.pi (λ a : α, (finset.univ : finset (β a))) = finset.univ :=
by { ext, simp }


/-
Copyright (c) 2019 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison

! This file was ported from Lean 3 source module category_theory.limits.shapes.finite_products
! leanprover-community/mathlib commit 69c6a5a12d8a2b159f20933e60115a4f2de62b58
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.CategoryTheory.Limits.Shapes.FiniteLimits
import Mathbin.CategoryTheory.Limits.Shapes.Products

/-!
# Categories with finite (co)products

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

Typeclasses representing categories with (co)products over finite indexing types.
-/

universe w v u

open CategoryTheory

open Classical

namespace CategoryTheory.Limits

variable (C : Type u) [Category.{v} C]

#print CategoryTheory.Limits.HasFiniteProducts /-
/- ./././Mathport/Syntax/Translate/Command.lean:388:30: infer kinds are unsupported in Lean 4: #[`out] [] -/
/-- A category has finite products if there is a chosen limit for every diagram
with shape `discrete J`, where we have `[finite J]`.

We require this condition only for `J = fin n` in the definition, then deduce a version for any
`J : Type*` as a corollary of this definition.
-/
class HasFiniteProducts : Prop where
  out (n : ℕ) : HasLimitsOfShape (Discrete (Fin n)) C
#align category_theory.limits.has_finite_products CategoryTheory.Limits.HasFiniteProducts
-/

#print CategoryTheory.Limits.hasFiniteProducts_of_hasFiniteLimits /-
/-- If `C` has finite limits then it has finite products. -/
instance (priority := 10) hasFiniteProducts_of_hasFiniteLimits [HasFiniteLimits C] :
    HasFiniteProducts C :=
  ⟨fun n => inferInstance⟩
#align category_theory.limits.has_finite_products_of_has_finite_limits CategoryTheory.Limits.hasFiniteProducts_of_hasFiniteLimits
-/

#print CategoryTheory.Limits.hasLimitsOfShape_discrete /-
instance hasLimitsOfShape_discrete [HasFiniteProducts C] (ι : Type w) [Finite ι] :
    HasLimitsOfShape (Discrete ι) C :=
  by
  rcases Finite.exists_equiv_fin ι with ⟨n, ⟨e⟩⟩
  haveI := has_finite_products.out C n
  exact has_limits_of_shape_of_equivalence (discrete.equivalence e.symm)
#align category_theory.limits.has_limits_of_shape_discrete CategoryTheory.Limits.hasLimitsOfShape_discrete
-/

/-- We can now write this for powers. -/
noncomputable example [HasFiniteProducts C] (X : C) : C :=
  ∏ fun i : Fin 5 => X

#print CategoryTheory.Limits.hasFiniteProducts_of_hasProducts /-
/-- If a category has all products then in particular it has finite products.
-/
theorem hasFiniteProducts_of_hasProducts [HasProducts.{w} C] : HasFiniteProducts C :=
  ⟨fun n => hasLimitsOfShape_of_equivalence (Discrete.equivalence Equiv.ulift.{w})⟩
#align category_theory.limits.has_finite_products_of_has_products CategoryTheory.Limits.hasFiniteProducts_of_hasProducts
-/

#print CategoryTheory.Limits.HasFiniteCoproducts /-
/- ./././Mathport/Syntax/Translate/Command.lean:388:30: infer kinds are unsupported in Lean 4: #[`out] [] -/
/-- A category has finite coproducts if there is a chosen colimit for every diagram
with shape `discrete J`, where we have `[fintype J]`.

We require this condition only for `J = fin n` in the definition, then deduce a version for any
`J : Type*` as a corollary of this definition.
-/
class HasFiniteCoproducts : Prop where
  out (n : ℕ) : HasColimitsOfShape (Discrete (Fin n)) C
#align category_theory.limits.has_finite_coproducts CategoryTheory.Limits.HasFiniteCoproducts
-/

attribute [class] has_finite_coproducts

#print CategoryTheory.Limits.hasColimitsOfShape_discrete /-
instance hasColimitsOfShape_discrete [HasFiniteCoproducts C] (ι : Type w) [Finite ι] :
    HasColimitsOfShape (Discrete ι) C :=
  by
  rcases Finite.exists_equiv_fin ι with ⟨n, ⟨e⟩⟩
  haveI := has_finite_coproducts.out C n
  exact has_colimits_of_shape_of_equivalence (discrete.equivalence e.symm)
#align category_theory.limits.has_colimits_of_shape_discrete CategoryTheory.Limits.hasColimitsOfShape_discrete
-/

#print CategoryTheory.Limits.hasFiniteCoproducts_of_hasFiniteColimits /-
/-- If `C` has finite colimits then it has finite coproducts. -/
instance (priority := 10) hasFiniteCoproducts_of_hasFiniteColimits [HasFiniteColimits C] :
    HasFiniteCoproducts C :=
  ⟨fun J => by infer_instance⟩
#align category_theory.limits.has_finite_coproducts_of_has_finite_colimits CategoryTheory.Limits.hasFiniteCoproducts_of_hasFiniteColimits
-/

#print CategoryTheory.Limits.hasFiniteCoproducts_of_hasCoproducts /-
/-- If a category has all coproducts then in particular it has finite coproducts.
-/
theorem hasFiniteCoproducts_of_hasCoproducts [HasCoproducts.{w} C] : HasFiniteCoproducts C :=
  ⟨fun J => hasColimitsOfShape_of_equivalence (Discrete.equivalence Equiv.ulift.{w})⟩
#align category_theory.limits.has_finite_coproducts_of_has_coproducts CategoryTheory.Limits.hasFiniteCoproducts_of_hasCoproducts
-/

end CategoryTheory.Limits