example (P Q : Type) (p : P) (h : P → Q) : Q :=
begin
exact h(p),
end


/-
Copyright (c) 2018 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Kenny Lau, Scott Morrison
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.list.chain
import Mathlib.data.list.nodup
import Mathlib.data.list.of_fn
import Mathlib.data.list.zip
import Mathlib.PostPort

universes u u_1

namespace Mathlib

namespace list

/- iota and range(') -/

@[simp] theorem length_range' (s : ℕ) (n : ℕ) : length (range' s n) = n := sorry

@[simp] theorem range'_eq_nil {s : ℕ} {n : ℕ} : range' s n = [] ↔ n = 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (range' s n = [] ↔ n = 0)) (Eq.symm (propext length_eq_zero))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (length (range' s n) = 0 ↔ n = 0)) (length_range' s n))) (iff.refl (n = 0)))

@[simp] theorem mem_range' {m : ℕ} {s : ℕ} {n : ℕ} : m ∈ range' s n ↔ s ≤ m ∧ m < s + n := sorry

theorem map_add_range' (a : ℕ) (s : ℕ) (n : ℕ) : map (Add.add a) (range' s n) = range' (a + s) n := sorry

theorem map_sub_range' (a : ℕ) (s : ℕ) (n : ℕ) (h : a ≤ s) : map (fun (x : ℕ) => x - a) (range' s n) = range' (s - a) n := sorry

theorem chain_succ_range' (s : ℕ) (n : ℕ) : chain (fun (a b : ℕ) => b = Nat.succ a) s (range' (s + 1) n) := sorry

theorem chain_lt_range' (s : ℕ) (n : ℕ) : chain Less s (range' (s + 1) n) :=
  chain.imp (fun (a b : ℕ) (e : b = Nat.succ a) => Eq.symm e ▸ nat.lt_succ_self a) (chain_succ_range' s n)

theorem pairwise_lt_range' (s : ℕ) (n : ℕ) : pairwise Less (range' s n) := sorry

theorem nodup_range' (s : ℕ) (n : ℕ) : nodup (range' s n) :=
  pairwise.imp (fun (a b : ℕ) => ne_of_lt) (pairwise_lt_range' s n)

@[simp] theorem range'_append (s : ℕ) (m : ℕ) (n : ℕ) : range' s m ++ range' (s + m) n = range' s (n + m) := sorry

theorem range'_sublist_right {s : ℕ} {m : ℕ} {n : ℕ} : range' s m <+ range' s n ↔ m ≤ n := sorry

theorem range'_subset_right {s : ℕ} {m : ℕ} {n : ℕ} : range' s m ⊆ range' s n ↔ m ≤ n := sorry

theorem nth_range' (s : ℕ) {m : ℕ} {n : ℕ} : m < n → nth (range' s n) m = some (s + m) := sorry

@[simp] theorem nth_le_range' {n : ℕ} {m : ℕ} (i : ℕ) (H : i < length (range' n m)) : nth_le (range' n m) i H = n + i := sorry

theorem range'_concat (s : ℕ) (n : ℕ) : range' s (n + 1) = range' s n ++ [s + n] :=
  eq.mpr (id (Eq._oldrec (Eq.refl (range' s (n + 1) = range' s n ++ [s + n])) (add_comm n 1)))
    (Eq.symm (range'_append s n 1))

theorem range_core_range' (s : ℕ) (n : ℕ) : range_core s (range' s n) = range' 0 (n + s) := sorry

theorem range_eq_range' (n : ℕ) : range n = range' 0 n :=
  Eq.trans (range_core_range' n 0)
    (eq.mpr (id (Eq._oldrec (Eq.refl (range' 0 (0 + n) = range' 0 n)) (zero_add n))) (Eq.refl (range' 0 n)))

theorem range_succ_eq_map (n : ℕ) : range (n + 1) = 0 :: map Nat.succ (range n) := sorry

theorem range'_eq_map_range (s : ℕ) (n : ℕ) : range' s n = map (Add.add s) (range n) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (range' s n = map (Add.add s) (range n))) (range_eq_range' n)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (range' s n = map (Add.add s) (range' 0 n))) (map_add_range' s 0 n)))
      (Eq.refl (range' s n)))

@[simp] theorem length_range (n : ℕ) : length (range n) = n := sorry

@[simp] theorem range_eq_nil {n : ℕ} : range n = [] ↔ n = 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (range n = [] ↔ n = 0)) (Eq.symm (propext length_eq_zero))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (length (range n) = 0 ↔ n = 0)) (length_range n))) (iff.refl (n = 0)))

theorem pairwise_lt_range (n : ℕ) : pairwise Less (range n) := sorry

theorem nodup_range (n : ℕ) : nodup (range n) := sorry

theorem range_sublist {m : ℕ} {n : ℕ} : range m <+ range n ↔ m ≤ n := sorry

theorem range_subset {m : ℕ} {n : ℕ} : range m ⊆ range n ↔ m ≤ n := sorry

@[simp] theorem mem_range {m : ℕ} {n : ℕ} : m ∈ range n ↔ m < n := sorry

@[simp] theorem not_mem_range_self {n : ℕ} : ¬n ∈ range n :=
  mt (iff.mp mem_range) (lt_irrefl n)

@[simp] theorem self_mem_range_succ (n : ℕ) : n ∈ range (n + 1) := sorry

theorem nth_range {m : ℕ} {n : ℕ} (h : m < n) : nth (range n) m = some m := sorry

theorem range_succ (n : ℕ) : range (Nat.succ n) = range n ++ [n] := sorry

@[simp] theorem range_zero : range 0 = [] :=
  rfl

theorem iota_eq_reverse_range' (n : ℕ) : iota n = reverse (range' 1 n) := sorry

@[simp] theorem length_iota (n : ℕ) : length (iota n) = n := sorry

theorem pairwise_gt_iota (n : ℕ) : pairwise gt (iota n) := sorry

theorem nodup_iota (n : ℕ) : nodup (iota n) := sorry

theorem mem_iota {m : ℕ} {n : ℕ} : m ∈ iota n ↔ 1 ≤ m ∧ m ≤ n := sorry

theorem reverse_range' (s : ℕ) (n : ℕ) : reverse (range' s n) = map (fun (i : ℕ) => s + n - 1 - i) (range n) := sorry

/-- All elements of `fin n`, from `0` to `n-1`. -/
def fin_range (n : ℕ) : List (fin n) :=
  pmap fin.mk (range n) sorry

@[simp] theorem fin_range_zero : fin_range 0 = [] :=
  rfl

@[simp] theorem mem_fin_range {n : ℕ} (a : fin n) : a ∈ fin_range n := sorry

theorem nodup_fin_range (n : ℕ) : nodup (fin_range n) :=
  nodup_pmap (fun (_x : ℕ) (_x_1 : _x < n) (_x_2 : ℕ) (_x_3 : _x_2 < n) => fin.veq_of_eq) (nodup_range n)

@[simp] theorem length_fin_range (n : ℕ) : length (fin_range n) = n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (length (fin_range n) = n)) (fin_range.equations._eqn_1 n)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (length (pmap fin.mk (range n) (fin_range._proof_1 n)) = n)) length_pmap))
      (eq.mpr (id (Eq._oldrec (Eq.refl (length (range n) = n)) (length_range n))) (Eq.refl n)))

@[simp] theorem fin_range_eq_nil {n : ℕ} : fin_range n = [] ↔ n = 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (fin_range n = [] ↔ n = 0)) (Eq.symm (propext length_eq_zero))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (length (fin_range n) = 0 ↔ n = 0)) (length_fin_range n))) (iff.refl (n = 0)))

theorem prod_range_succ {α : Type u} [monoid α] (f : ℕ → α) (n : ℕ) : prod (map f (range (Nat.succ n))) = prod (map f (range n)) * f n := sorry

/-- A variant of `prod_range_succ` which pulls off the first
  term in the product rather than the last.-/
theorem sum_range_succ' {α : Type u} [add_monoid α] (f : ℕ → α) (n : ℕ) : sum (map f (range (Nat.succ n))) = f 0 + sum (map (fun (i : ℕ) => f (Nat.succ i)) (range n)) := sorry

@[simp] theorem enum_from_map_fst {α : Type u} (n : ℕ) (l : List α) : map prod.fst (enum_from n l) = range' n (length l) := sorry

@[simp] theorem enum_map_fst {α : Type u} (l : List α) : map prod.fst (enum l) = range (length l) := sorry

theorem enum_eq_zip_range {α : Type u} (l : List α) : enum l = zip (range (length l)) l :=
  zip_of_prod (enum_map_fst l) (enum_map_snd l)

@[simp] theorem unzip_enum_eq_prod {α : Type u} (l : List α) : unzip (enum l) = (range (length l), l) := sorry

theorem enum_from_eq_zip_range' {α : Type u} (l : List α) {n : ℕ} : enum_from n l = zip (range' n (length l)) l :=
  zip_of_prod (enum_from_map_fst n l) (enum_from_map_snd n l)

@[simp] theorem unzip_enum_from_eq_prod {α : Type u} (l : List α) {n : ℕ} : unzip (enum_from n l) = (range' n (length l), l) := sorry

@[simp] theorem nth_le_range {n : ℕ} (i : ℕ) (H : i < length (range n)) : nth_le (range n) i H = i := sorry

@[simp] theorem nth_le_fin_range {n : ℕ} {i : ℕ} (h : i < length (fin_range n)) : nth_le (fin_range n) i h = { val := i, property := length_fin_range n ▸ h } := sorry

theorem of_fn_eq_pmap {α : Type u_1} {n : ℕ} {f : fin n → α} : of_fn f = pmap (fun (i : ℕ) (hi : i < n) => f { val := i, property := hi }) (range n) fun (_x : ℕ) => iff.mp mem_range := sorry

theorem of_fn_id (n : ℕ) : of_fn id = fin_range n :=
  of_fn_eq_pmap

theorem of_fn_eq_map {α : Type u_1} {n : ℕ} {f : fin n → α} : of_fn f = map f (fin_range n) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (of_fn f = map f (fin_range n))) (Eq.symm (of_fn_id n))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (of_fn f = map f (of_fn id))) (map_of_fn id f)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (of_fn f = of_fn (f ∘ id))) (function.right_id f))) (Eq.refl (of_fn f))))

theorem nodup_of_fn {α : Type u_1} {n : ℕ} {f : fin n → α} (hf : function.injective f) : nodup (of_fn f) := sorry


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

# Preorder homomorphisms

Bundled monotone functions, `x ≤ y → f x ≤ f y`.
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.logic.function.iterate
import Mathlib.order.basic
import Mathlib.order.bounded_lattice
import Mathlib.order.complete_lattice
import Mathlib.tactic.monotonicity.default
import Mathlib.PostPort

universes u_1 u_2 l u_3

namespace Mathlib

/-! # Category of preorders -/

/-- Bundled monotone (aka, increasing) function -/
structure preorder_hom (α : Type u_1) (β : Type u_2) [preorder α] [preorder β]
where
  to_fun : α → β
  monotone' : monotone to_fun

infixr:25 " →ₘ " => Mathlib.preorder_hom

namespace preorder_hom

protected instance has_coe_to_fun {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] : has_coe_to_fun (α →ₘ β) :=
  has_coe_to_fun.mk (fun (f : α →ₘ β) => α → β) to_fun

theorem monotone {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (f : α →ₘ β) : monotone ⇑f :=
  monotone' f

@[simp] theorem coe_fun_mk {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] {f : α → β} (hf : monotone f) (x : α) : coe_fn (mk f hf) x = f x :=
  rfl

theorem ext {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (f : α →ₘ β) (g : α →ₘ β) (h : ∀ (a : α), coe_fn f a = coe_fn g a) : f = g := sorry

theorem coe_inj {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (f : α →ₘ β) (g : α →ₘ β) (h : ⇑f = ⇑g) : f = g :=
  ext f g fun (a : α) => eq.mpr (id (Eq._oldrec (Eq.refl (coe_fn f a = coe_fn g a)) h)) (Eq.refl (coe_fn g a))

/-- The identity function as bundled monotone function. -/
def id {α : Type u_1} [preorder α] : α →ₘ α :=
  mk id monotone_id

protected instance inhabited {α : Type u_1} [preorder α] : Inhabited (α →ₘ α) :=
  { default := id }

@[simp] theorem coe_id {α : Type u_1} [preorder α] : ⇑id = ⇑id :=
  rfl

/-- The composition of two bundled monotone functions. -/
def comp {α : Type u_1} {β : Type u_2} {γ : Type u_3} [preorder α] [preorder β] [preorder γ] (g : β →ₘ γ) (f : α →ₘ β) : α →ₘ γ :=
  mk (⇑g ∘ ⇑f) sorry

@[simp] theorem comp_id {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (f : α →ₘ β) : comp f id = f :=
  ext (comp f id) f fun (a : α) => Eq.refl (coe_fn (comp f id) a)

@[simp] theorem id_comp {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (f : α →ₘ β) : comp id f = f :=
  ext (comp id f) f fun (a : α) => Eq.refl (coe_fn (comp id f) a)

/-- `subtype.val` as a bundled monotone function.  -/
def subtype.val {α : Type u_1} [preorder α] (p : α → Prop) : Subtype p →ₘ α :=
  mk subtype.val sorry

/-- The preorder structure of `α →ₘ β` is pointwise inequality: `f ≤ g ↔ ∀ a, f a ≤ g a`. -/
protected instance preorder {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] : preorder (α →ₘ β) :=
  preorder.lift to_fun

protected instance partial_order {α : Type u_1} [preorder α] {β : Type u_2} [partial_order β] : partial_order (α →ₘ β) :=
  partial_order.lift to_fun sorry

protected instance has_sup {α : Type u_1} [preorder α] {β : Type u_2} [semilattice_sup β] : has_sup (α →ₘ β) :=
  has_sup.mk fun (f g : α →ₘ β) => mk (fun (a : α) => coe_fn f a ⊔ coe_fn g a) sorry

protected instance semilattice_sup {α : Type u_1} [preorder α] {β : Type u_2} [semilattice_sup β] : semilattice_sup (α →ₘ β) :=
  semilattice_sup.mk has_sup.sup partial_order.le partial_order.lt sorry sorry sorry sorry sorry sorry

@[simp] theorem has_inf_inf_to_fun {α : Type u_1} [preorder α] {β : Type u_2} [semilattice_inf β] (f : α →ₘ β) (g : α →ₘ β) (a : α) : coe_fn (f ⊓ g) a = coe_fn f a ⊓ coe_fn g a :=
  Eq.refl (coe_fn (f ⊓ g) a)

protected instance semilattice_inf {α : Type u_1} [preorder α] {β : Type u_2} [semilattice_inf β] : semilattice_inf (α →ₘ β) :=
  semilattice_inf.mk has_inf.inf partial_order.le partial_order.lt sorry sorry sorry sorry sorry sorry

protected instance lattice {α : Type u_1} [preorder α] {β : Type u_2} [lattice β] : lattice (α →ₘ β) :=
  lattice.mk semilattice_sup.sup semilattice_sup.le semilattice_sup.lt sorry sorry sorry sorry sorry sorry
    semilattice_inf.inf sorry sorry sorry

protected instance has_bot {α : Type u_1} [preorder α] {β : Type u_2} [order_bot β] : has_bot (α →ₘ β) :=
  has_bot.mk (mk (fun (a : α) => ⊥) sorry)

protected instance order_bot {α : Type u_1} [preorder α] {β : Type u_2} [order_bot β] : order_bot (α →ₘ β) :=
  order_bot.mk ⊥ partial_order.le partial_order.lt sorry sorry sorry sorry

@[simp] theorem has_top_top_to_fun {α : Type u_1} [preorder α] {β : Type u_2} [order_top β] (a : α) : coe_fn ⊤ a = ⊤ :=
  Eq.refl (coe_fn ⊤ a)

protected instance order_top {α : Type u_1} [preorder α] {β : Type u_2} [order_top β] : order_top (α →ₘ β) :=
  order_top.mk ⊤ partial_order.le partial_order.lt sorry sorry sorry sorry

protected instance has_Inf {α : Type u_1} [preorder α] {β : Type u_2} [complete_lattice β] : has_Inf (α →ₘ β) :=
  has_Inf.mk fun (s : set (α →ₘ β)) => mk (fun (x : α) => Inf ((fun (f : α →ₘ β) => coe_fn f x) '' s)) sorry

@[simp] theorem has_Sup_Sup_to_fun {α : Type u_1} [preorder α] {β : Type u_2} [complete_lattice β] (s : set (α →ₘ β)) (x : α) : coe_fn (Sup s) x = Sup ((fun (f : α →ₘ β) => coe_fn f x) '' s) :=
  Eq.refl (coe_fn (Sup s) x)

protected instance complete_lattice {α : Type u_1} [preorder α] {β : Type u_2} [complete_lattice β] : complete_lattice (α →ₘ β) :=
  complete_lattice.mk lattice.sup lattice.le lattice.lt sorry sorry sorry sorry sorry sorry lattice.inf sorry sorry sorry
    order_top.top sorry order_bot.bot sorry Sup Inf sorry sorry sorry sorry

theorem iterate_sup_le_sup_iff {α : Type u_1} [semilattice_sup α] (f : α →ₘ α) : (∀ (n₁ n₂ : ℕ) (a₁ a₂ : α), nat.iterate (⇑f) (n₁ + n₂) (a₁ ⊔ a₂) ≤ nat.iterate (⇑f) n₁ a₁ ⊔ nat.iterate (⇑f) n₂ a₂) ↔
  ∀ (a₁ a₂ : α), coe_fn f (a₁ ⊔ a₂) ≤ coe_fn f a₁ ⊔ a₂ := sorry


/-
Copyright (c) 2019 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin, Floris van Doorn
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebra.module.basic
import Mathlib.data.set.finite
import Mathlib.group_theory.submonoid.basic
import Mathlib.PostPort

universes u_1 u_2 u_3 u_4

namespace Mathlib

/-!
# Pointwise addition, multiplication, and scalar multiplication of sets.

This file defines pointwise algebraic operations on sets.
* For a type `α` with multiplication, multiplication is defined on `set α` by taking
  `s * t` to be the set of all `x * y` where `x ∈ s` and `y ∈ t`. Similarly for addition.
* For `α` a semigroup, `set α` is a semigroup.
* If `α` is a (commutative) monoid, we define an alias `set_semiring α` for `set α`, which then
  becomes a (commutative) semiring with union as addition and pointwise multiplication as
  multiplication.
* For a type `β` with scalar multiplication by another type `α`, this
  file defines a scalar multiplication of `set β` by `set α` and a separate scalar
  multiplication of `set β` by `α`.
* We also define pointwise multiplication on `finset`.

Appropriate definitions and results are also transported to the additive theory via `to_additive`.

## Implementation notes
* The following expressions are considered in simp-normal form in a group:
  `(λ h, h * g) ⁻¹' s`, `(λ h, g * h) ⁻¹' s`, `(λ h, h * g⁻¹) ⁻¹' s`, `(λ h, g⁻¹ * h) ⁻¹' s`,
  `s * t`, `s⁻¹`, `(1 : set _)` (and similarly for additive variants).
  Expressions equal to one of these will be simplified.

## Tags

set multiplication, set addition, pointwise addition, pointwise multiplication

-/

namespace set

/-! ### Properties about 1 -/

protected instance has_one {α : Type u_1} [HasOne α] : HasOne (set α) := { one := singleton 1 }

theorem singleton_one {α : Type u_1} [HasOne α] : singleton 1 = 1 := rfl

@[simp] theorem mem_zero {α : Type u_1} {a : α} [HasZero α] : a ∈ 0 ↔ a = 0 := iff.rfl

theorem one_mem_one {α : Type u_1} [HasOne α] : 1 ∈ 1 := Eq.refl 1

@[simp] theorem zero_subset {α : Type u_1} {s : set α} [HasZero α] : 0 ⊆ s ↔ 0 ∈ s :=
  singleton_subset_iff

theorem zero_nonempty {α : Type u_1} [HasZero α] : set.nonempty 0 := Exists.intro 0 rfl

@[simp] theorem image_zero {α : Type u_1} {β : Type u_2} [HasZero α] {f : α → β} :
    f '' 0 = singleton (f 0) :=
  image_singleton

/-! ### Properties about multiplication -/

protected instance has_add {α : Type u_1} [Add α] : Add (set α) := { add := image2 Add.add }

@[simp] theorem image2_mul {α : Type u_1} {s : set α} {t : set α} [Mul α] :
    image2 Mul.mul s t = s * t :=
  rfl

theorem mem_add {α : Type u_1} {s : set α} {t : set α} {a : α} [Add α] :
    a ∈ s + t ↔ ∃ (x : α), ∃ (y : α), x ∈ s ∧ y ∈ t ∧ x + y = a :=
  iff.rfl

theorem mul_mem_mul {α : Type u_1} {s : set α} {t : set α} {a : α} {b : α} [Mul α] (ha : a ∈ s)
    (hb : b ∈ t) : a * b ∈ s * t :=
  mem_image2_of_mem ha hb

theorem add_image_prod {α : Type u_1} {s : set α} {t : set α} [Add α] :
    (fun (x : α × α) => prod.fst x + prod.snd x) '' set.prod s t = s + t :=
  image_prod Add.add

@[simp] theorem image_mul_left {α : Type u_1} {t : set α} {a : α} [group α] :
    (fun (b : α) => a * b) '' t = (fun (b : α) => a⁻¹ * b) ⁻¹' t :=
  sorry

@[simp] theorem image_add_right {α : Type u_1} {t : set α} {b : α} [add_group α] :
    (fun (a : α) => a + b) '' t = (fun (a : α) => a + -b) ⁻¹' t :=
  sorry

theorem image_add_left' {α : Type u_1} {t : set α} {a : α} [add_group α] :
    (fun (b : α) => -a + b) '' t = (fun (b : α) => a + b) ⁻¹' t :=
  sorry

theorem image_mul_right' {α : Type u_1} {t : set α} {b : α} [group α] :
    (fun (a : α) => a * (b⁻¹)) '' t = (fun (a : α) => a * b) ⁻¹' t :=
  sorry

@[simp] theorem preimage_add_left_singleton {α : Type u_1} {a : α} {b : α} [add_group α] :
    Add.add a ⁻¹' singleton b = singleton (-a + b) :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (Add.add a ⁻¹' singleton b = singleton (-a + b)))
        (Eq.symm image_add_left')))
    (eq.mpr
      (id
        (Eq._oldrec (Eq.refl ((fun (b : α) => -a + b) '' singleton b = singleton (-a + b)))
          image_singleton))
      (Eq.refl (singleton (-a + b))))

@[simp] theorem preimage_mul_right_singleton {α : Type u_1} {a : α} {b : α} [group α] :
    (fun (_x : α) => _x * a) ⁻¹' singleton b = singleton (b * (a⁻¹)) :=
  sorry

@[simp] theorem preimage_add_left_zero {α : Type u_1} {a : α} [add_group α] :
    (fun (b : α) => a + b) ⁻¹' 0 = singleton (-a) :=
  sorry

@[simp] theorem preimage_mul_right_one {α : Type u_1} {b : α} [group α] :
    (fun (a : α) => a * b) ⁻¹' 1 = singleton (b⁻¹) :=
  sorry

theorem preimage_add_left_zero' {α : Type u_1} {a : α} [add_group α] :
    (fun (b : α) => -a + b) ⁻¹' 0 = singleton a :=
  sorry

theorem preimage_add_right_zero' {α : Type u_1} {b : α} [add_group α] :
    (fun (a : α) => a + -b) ⁻¹' 0 = singleton b :=
  sorry

@[simp] theorem mul_singleton {α : Type u_1} {s : set α} {b : α} [Mul α] :
    s * singleton b = (fun (a : α) => a * b) '' s :=
  image2_singleton_right

@[simp] theorem singleton_add {α : Type u_1} {t : set α} {a : α} [Add α] :
    singleton a + t = (fun (b : α) => a + b) '' t :=
  image2_singleton_left

@[simp] theorem singleton_add_singleton {α : Type u_1} {a : α} {b : α} [Add α] :
    singleton a + singleton b = singleton (a + b) :=
  image2_singleton

protected instance semigroup {α : Type u_1} [semigroup α] : semigroup (set α) :=
  semigroup.mk Mul.mul sorry

protected instance monoid {α : Type u_1} [monoid α] : monoid (set α) :=
  monoid.mk semigroup.mul sorry 1 sorry sorry

protected theorem mul_comm {α : Type u_1} {s : set α} {t : set α} [comm_semigroup α] :
    s * t = t * s :=
  sorry

protected instance add_comm_monoid {α : Type u_1} [add_comm_monoid α] : add_comm_monoid (set α) :=
  add_comm_monoid.mk add_monoid.add sorry add_monoid.zero sorry sorry sorry

theorem singleton.is_mul_hom {α : Type u_1} [Mul α] : is_mul_hom singleton :=
  is_mul_hom.mk fun (a b : α) => Eq.symm singleton_mul_singleton

@[simp] theorem empty_add {α : Type u_1} {s : set α} [Add α] : ∅ + s = ∅ := image2_empty_left

@[simp] theorem mul_empty {α : Type u_1} {s : set α} [Mul α] : s * ∅ = ∅ := image2_empty_right

theorem add_subset_add {α : Type u_1} {s₁ : set α} {s₂ : set α} {t₁ : set α} {t₂ : set α} [Add α]
    (h₁ : s₁ ⊆ t₁) (h₂ : s₂ ⊆ t₂) : s₁ + s₂ ⊆ t₁ + t₂ :=
  image2_subset h₁ h₂

theorem union_add {α : Type u_1} {s : set α} {t : set α} {u : set α} [Add α] :
    s ∪ t + u = s + u ∪ (t + u) :=
  image2_union_left

theorem mul_union {α : Type u_1} {s : set α} {t : set α} {u : set α} [Mul α] :
    s * (t ∪ u) = s * t ∪ s * u :=
  image2_union_right

theorem Union_mul_left_image {α : Type u_1} {s : set α} {t : set α} [Mul α] :
    (Union fun (a : α) => Union fun (H : a ∈ s) => (fun (x : α) => a * x) '' t) = s * t :=
  Union_image_left fun (a x : α) => a * x

theorem Union_mul_right_image {α : Type u_1} {s : set α} {t : set α} [Mul α] :
    (Union fun (a : α) => Union fun (H : a ∈ t) => (fun (x : α) => x * a) '' s) = s * t :=
  Union_image_right fun (x a : α) => x * a

@[simp] theorem univ_mul_univ {α : Type u_1} [monoid α] : univ * univ = univ := sorry

/-- `singleton` is a monoid hom. -/
def singleton_add_hom {α : Type u_1} [add_monoid α] : α →+ set α :=
  add_monoid_hom.mk singleton sorry sorry

theorem nonempty.add {α : Type u_1} {s : set α} {t : set α} [Add α] :
    set.nonempty s → set.nonempty t → set.nonempty (s + t) :=
  nonempty.image2

theorem finite.mul {α : Type u_1} {s : set α} {t : set α} [Mul α] (hs : finite s) (ht : finite t) :
    finite (s * t) :=
  finite.image2 (fun (a b : α) => a * b) hs ht

/-- multiplication preserves finiteness -/
def fintype_mul {α : Type u_1} [Mul α] [DecidableEq α] (s : set α) (t : set α) [hs : fintype ↥s]
    [ht : fintype ↥t] : fintype ↥(s * t) :=
  set.fintype_image2 (fun (a b : α) => a * b) s t

theorem bdd_above_add {α : Type u_1} [ordered_add_comm_monoid α] {A : set α} {B : set α} :
    bdd_above A → bdd_above B → bdd_above (A + B) :=
  sorry

/-! ### Properties about inversion -/

protected instance has_inv {α : Type u_1} [has_inv α] : has_inv (set α) :=
  has_inv.mk (preimage has_inv.inv)

@[simp] theorem mem_inv {α : Type u_1} {s : set α} {a : α} [has_inv α] : a ∈ (s⁻¹) ↔ a⁻¹ ∈ s :=
  iff.rfl

theorem inv_mem_inv {α : Type u_1} {s : set α} {a : α} [group α] : a⁻¹ ∈ (s⁻¹) ↔ a ∈ s := sorry

@[simp] theorem inv_preimage {α : Type u_1} {s : set α} [has_inv α] : has_inv.inv ⁻¹' s = (s⁻¹) :=
  rfl

@[simp] theorem image_inv {α : Type u_1} {s : set α} [group α] : has_inv.inv '' s = (s⁻¹) := sorry

@[simp] theorem inter_neg {α : Type u_1} {s : set α} {t : set α} [Neg α] : -(s ∩ t) = -s ∩ -t :=
  preimage_inter

@[simp] theorem union_neg {α : Type u_1} {s : set α} {t : set α} [Neg α] : -(s ∪ t) = -s ∪ -t :=
  preimage_union

@[simp] theorem compl_inv {α : Type u_1} {s : set α} [has_inv α] : sᶜ⁻¹ = (s⁻¹ᶜ) := preimage_compl

@[simp] protected theorem inv_inv {α : Type u_1} {s : set α} [group α] : s⁻¹⁻¹ = s := sorry

@[simp] protected theorem univ_inv {α : Type u_1} [group α] : univ⁻¹ = univ := preimage_univ

@[simp] theorem neg_subset_neg {α : Type u_1} [add_group α] {s : set α} {t : set α} :
    -s ⊆ -t ↔ s ⊆ t :=
  function.surjective.preimage_subset_preimage_iff (equiv.surjective (equiv.neg α))

theorem neg_subset {α : Type u_1} [add_group α] {s : set α} {t : set α} : -s ⊆ t ↔ s ⊆ -t :=
  eq.mpr (id (Eq._oldrec (Eq.refl (-s ⊆ t ↔ s ⊆ -t)) (Eq.symm (propext neg_subset_neg))))
    (eq.mpr (id (Eq._oldrec (Eq.refl ( --s ⊆ -t ↔ s ⊆ -t)) set.neg_neg)) (iff.refl (s ⊆ -t)))

/-! ### Properties about scalar multiplication -/

/-- Scaling a set: multiplying every element by a scalar. -/
protected instance has_scalar_set {α : Type u_1} {β : Type u_2} [has_scalar α β] :
    has_scalar α (set β) :=
  has_scalar.mk fun (a : α) => image (has_scalar.smul a)

@[simp] theorem image_smul {α : Type u_1} {β : Type u_2} {a : α} [has_scalar α β] {t : set β} :
    (fun (x : β) => a • x) '' t = a • t :=
  rfl

theorem mem_smul_set {α : Type u_1} {β : Type u_2} {a : α} {x : β} [has_scalar α β] {t : set β} :
    x ∈ a • t ↔ ∃ (y : β), y ∈ t ∧ a • y = x :=
  iff.rfl

theorem smul_mem_smul_set {α : Type u_1} {β : Type u_2} {a : α} {y : β} [has_scalar α β] {t : set β}
    (hy : y ∈ t) : a • y ∈ a • t :=
  Exists.intro y { left := hy, right := rfl }

theorem smul_set_union {α : Type u_1} {β : Type u_2} {a : α} [has_scalar α β] {s : set β}
    {t : set β} : a • (s ∪ t) = a • s ∪ a • t :=
  sorry

@[simp] theorem smul_set_empty {α : Type u_1} {β : Type u_2} [has_scalar α β] (a : α) : a • ∅ = ∅ :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a • ∅ = ∅)) (Eq.symm image_smul)))
    (eq.mpr
      (id
        (Eq._oldrec (Eq.refl ((fun (x : β) => a • x) '' ∅ = ∅)) (image_empty fun (x : β) => a • x)))
      (Eq.refl ∅))

theorem smul_set_mono {α : Type u_1} {β : Type u_2} {a : α} [has_scalar α β] {s : set β} {t : set β}
    (h : s ⊆ t) : a • s ⊆ a • t :=
  sorry

/-- Pointwise scalar multiplication by a set of scalars. -/
protected instance has_scalar {α : Type u_1} {β : Type u_2} [has_scalar α β] :
    has_scalar (set α) (set β) :=
  has_scalar.mk (image2 has_scalar.smul)

@[simp] theorem image2_smul {α : Type u_1} {β : Type u_2} {s : set α} [has_scalar α β] {t : set β} :
    image2 has_scalar.smul s t = s • t :=
  rfl

theorem mem_smul {α : Type u_1} {β : Type u_2} {s : set α} {x : β} [has_scalar α β] {t : set β} :
    x ∈ s • t ↔ ∃ (a : α), ∃ (y : β), a ∈ s ∧ y ∈ t ∧ a • y = x :=
  iff.rfl

theorem image_smul_prod {α : Type u_1} {β : Type u_2} {s : set α} [has_scalar α β] {t : set β} :
    (fun (x : α × β) => prod.fst x • prod.snd x) '' set.prod s t = s • t :=
  image_prod has_scalar.smul

theorem range_smul_range {α : Type u_1} {β : Type u_2} [has_scalar α β] {ι : Type u_3}
    {κ : Type u_4} (b : ι → α) (c : κ → β) :
    range b • range c = range fun (p : ι × κ) => b (prod.fst p) • c (prod.snd p) :=
  sorry

theorem singleton_smul {α : Type u_1} {β : Type u_2} {a : α} [has_scalar α β] {t : set β} :
    singleton a • t = a • t :=
  image2_singleton_left

/-! ### `set α` as a `(∪,*)`-semiring -/

/-- An alias for `set α`, which has a semiring structure given by `∪` as "addition" and pointwise
  multiplication `*` as "multiplication". -/
def set_semiring (α : Type u_1) := set α

/-- The identitiy function `set α → set_semiring α`. -/
/-- The identitiy function `set_semiring α → set α`. -/
protected def up {α : Type u_1} (s : set α) : set_semiring α := s

protected def set_semiring.down {α : Type u_1} (s : set_semiring α) : set α := s

@[simp] protected theorem down_up {α : Type u_1} {s : set α} : set_semiring.down (set.up s) = s :=
  rfl

@[simp] protected theorem up_down {α : Type u_1} {s : set_semiring α} :
    set.up (set_semiring.down s) = s :=
  rfl

protected instance set_semiring.semiring {α : Type u_1} [monoid α] : semiring (set_semiring α) :=
  semiring.mk (fun (s t : set_semiring α) => s ∪ t) union_assoc ∅ empty_union union_empty union_comm
    monoid.mul sorry monoid.one sorry sorry sorry sorry sorry sorry

protected instance set_semiring.comm_semiring {α : Type u_1} [comm_monoid α] :
    comm_semiring (set_semiring α) :=
  comm_semiring.mk semiring.add sorry semiring.zero sorry sorry sorry comm_monoid.mul sorry
    comm_monoid.one sorry sorry sorry sorry sorry sorry sorry

/-- A multiplicative action of a monoid on a type β gives also a
 multiplicative action on the subsets of β. -/
protected instance mul_action_set {α : Type u_1} {β : Type u_2} [monoid α] [mul_action α β] :
    mul_action α (set β) :=
  mul_action.mk sorry sorry

theorem image_add {α : Type u_1} {β : Type u_2} {s : set α} {t : set α} [Add α] [Add β] (m : α → β)
    [is_add_hom m] : m '' (s + t) = m '' s + m '' t :=
  sorry

theorem preimage_mul_preimage_subset {α : Type u_1} {β : Type u_2} [Mul α] [Mul β] (m : α → β)
    [is_mul_hom m] {s : set β} {t : set β} : m ⁻¹' s * m ⁻¹' t ⊆ m ⁻¹' (s * t) :=
  sorry

/-- The image of a set under function is a ring homomorphism
with respect to the pointwise operations on sets. -/
def image_hom {α : Type u_1} {β : Type u_2} [monoid α] [monoid β] (f : α →* β) :
    set_semiring α →+* set_semiring β :=
  ring_hom.mk (image ⇑f) sorry sorry sorry sorry

end set

/-- A nonempty set in a semimodule is scaled by zero to the singleton
containing 0 in the semimodule. -/
theorem zero_smul_set {α : Type u_1} {β : Type u_2} [semiring α] [add_comm_monoid β]
    [semimodule α β] {s : set β} (h : set.nonempty s) : 0 • s = 0 :=
  sorry

theorem mem_inv_smul_set_iff {α : Type u_1} {β : Type u_2} [field α] [mul_action α β] {a : α}
    (ha : a ≠ 0) (A : set β) (x : β) : x ∈ a⁻¹ • A ↔ a • x ∈ A :=
  sorry

theorem mem_smul_set_iff_inv_smul_mem {α : Type u_1} {β : Type u_2} [field α] [mul_action α β]
    {a : α} (ha : a ≠ 0) (A : set β) (x : β) : x ∈ a • A ↔ a⁻¹ • x ∈ A :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (x ∈ a • A ↔ a⁻¹ • x ∈ A))
        (Eq.symm (propext (mem_inv_smul_set_iff (inv_ne_zero ha) A x)))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (x ∈ a • A ↔ x ∈ a⁻¹⁻¹ • A)) (inv_inv' a)))
      (iff.refl (x ∈ a • A)))

namespace finset

/-- The pointwise product of two finite sets `s` and `t`:
  `st = s ⬝ t = s * t = { x * y | x ∈ s, y ∈ t }`. -/
protected instance has_add {α : Type u_1} [DecidableEq α] [Add α] : Add (finset α) :=
  { add :=
      fun (s t : finset α) =>
        image (fun (p : α × α) => prod.fst p + prod.snd p) (finset.product s t) }

theorem mul_def {α : Type u_1} [DecidableEq α] [Mul α] {s : finset α} {t : finset α} :
    s * t = image (fun (p : α × α) => prod.fst p * prod.snd p) (finset.product s t) :=
  rfl

theorem mem_add {α : Type u_1} [DecidableEq α] [Add α] {s : finset α} {t : finset α} {x : α} :
    x ∈ s + t ↔ ∃ (y : α), ∃ (z : α), y ∈ s ∧ z ∈ t ∧ y + z = x :=
  sorry

@[simp] theorem coe_add {α : Type u_1} [DecidableEq α] [Add α] {s : finset α} {t : finset α} :
    ↑(s + t) = ↑s + ↑t :=
  sorry

theorem mul_mem_mul {α : Type u_1} [DecidableEq α] [Mul α] {s : finset α} {t : finset α} {x : α}
    {y : α} (hx : x ∈ s) (hy : y ∈ t) : x * y ∈ s * t :=
  eq.mpr (id (propext mem_mul))
    (Exists.intro x (Exists.intro y { left := hx, right := { left := hy, right := rfl } }))

theorem add_card_le {α : Type u_1} [DecidableEq α] [Add α] {s : finset α} {t : finset α} :
    card (s + t) ≤ card s * card t :=
  sorry

theorem mul_card_le {α : Type u_1} [DecidableEq α] [Mul α] {s : finset α} {t : finset α} :
    card (s * t) ≤ card s * card t :=
  sorry

/-- A finite set `U` contained in the product of two sets `S * S'` is also contained in the product
of two finite sets `T * T' ⊆ S * S'`. -/
theorem subset_add {M : Type u_1} [add_monoid M] {S : set M} {S' : set M} {U : finset M}
    (f : ↑U ⊆ S + S') : ∃ (T : finset M), ∃ (T' : finset M), ↑T ⊆ S ∧ ↑T' ⊆ S' ∧ U ⊆ T + T' :=
  sorry

end finset

/-! Some lemmas about pointwise multiplication and submonoids. Ideally we put these in
  `group_theory.submonoid.basic`, but currently we cannot because that file is imported by this. -/

namespace submonoid

theorem mul_subset {M : Type u_1} [monoid M] {s : set M} {t : set M} {S : submonoid M} (hs : s ⊆ ↑S)
    (ht : t ⊆ ↑S) : s * t ⊆ ↑S :=
  sorry

theorem mul_subset_closure {M : Type u_1} [monoid M] {s : set M} {t : set M} {u : set M}
    (hs : s ⊆ u) (ht : t ⊆ u) : s * t ⊆ ↑(closure u) :=
  mul_subset (set.subset.trans hs subset_closure) (set.subset.trans ht subset_closure)

theorem Mathlib.add_submonoid.coe_add_self_eq {M : Type u_1} [add_monoid M] (s : add_submonoid M) :
    ↑s + ↑s = ↑s :=
  sorry

end Mathlib


/-
Copyright (c) 2017 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.logic.embedding
import Mathlib.order.rel_classes
import Mathlib.data.set.intervals.basic
import Mathlib.PostPort

universes u_4 u_5 l u_1 u_2 u_3

namespace Mathlib

/-- A relation homomorphism with respect to a given pair of relations `r` and `s`
is a function `f : α → β` such that `r a b → s (f a) (f b)`. -/
structure rel_hom {α : Type u_4} {β : Type u_5} (r : α → α → Prop) (s : β → β → Prop)
where
  to_fun : α → β
  map_rel' : ∀ {a b : α}, r a b → s (to_fun a) (to_fun b)

infixl:25 " →r " => Mathlib.rel_hom

namespace rel_hom

protected instance has_coe_to_fun {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} : has_coe_to_fun (r →r s) :=
  has_coe_to_fun.mk (fun (_x : r →r s) => α → β) fun (o : r →r s) => to_fun o

theorem map_rel {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r →r s) {a : α} {b : α} : r a b → s (coe_fn f a) (coe_fn f b) :=
  map_rel' f

@[simp] theorem coe_fn_mk {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : α → β) (o : ∀ {a b : α}, r a b → s (f a) (f b)) : ⇑(mk f o) = f :=
  rfl

@[simp] theorem coe_fn_to_fun {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r →r s) : to_fun f = ⇑f :=
  rfl

/-- The map `coe_fn : (r →r s) → (α → β)` is injective. We can't use `function.injective`
here but mimic its signature by using `⦃e₁ e₂⦄`. -/
theorem coe_fn_inj {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} {e₁ : r →r s} {e₂ : r →r s} : ⇑e₁ = ⇑e₂ → e₁ = e₂ := sorry

theorem ext {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} {f : r →r s} {g : r →r s} (h : ∀ (x : α), coe_fn f x = coe_fn g x) : f = g :=
  coe_fn_inj (funext h)

theorem ext_iff {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} {f : r →r s} {g : r →r s} : f = g ↔ ∀ (x : α), coe_fn f x = coe_fn g x :=
  { mp := fun (h : f = g) (x : α) => h ▸ rfl, mpr := fun (h : ∀ (x : α), coe_fn f x = coe_fn g x) => ext h }

/-- Identity map is a relation homomorphism. -/
protected def id {α : Type u_1} (r : α → α → Prop) : r →r r :=
  mk id sorry

/-- Composition of two relation homomorphisms is a relation homomorphism. -/
protected def comp {α : Type u_1} {β : Type u_2} {γ : Type u_3} {r : α → α → Prop} {s : β → β → Prop} {t : γ → γ → Prop} (g : s →r t) (f : r →r s) : r →r t :=
  mk (to_fun g ∘ to_fun f) sorry

@[simp] theorem id_apply {α : Type u_1} {r : α → α → Prop} (x : α) : coe_fn (rel_hom.id r) x = x :=
  rfl

@[simp] theorem comp_apply {α : Type u_1} {β : Type u_2} {γ : Type u_3} {r : α → α → Prop} {s : β → β → Prop} {t : γ → γ → Prop} (g : s →r t) (f : r →r s) (a : α) : coe_fn (rel_hom.comp g f) a = coe_fn g (coe_fn f a) :=
  rfl

/-- A relation homomorphism is also a relation homomorphism between dual relations. -/
protected def swap {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r →r s) : function.swap r →r function.swap s :=
  mk ⇑f sorry

/-- A function is a relation homomorphism from the preimage relation of `s` to `s`. -/
def preimage {α : Type u_1} {β : Type u_2} (f : α → β) (s : β → β → Prop) : f ⁻¹'o s →r s :=
  mk f sorry

protected theorem is_irrefl {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r →r s) [is_irrefl β s] : is_irrefl α r := sorry

protected theorem is_asymm {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r →r s) [is_asymm β s] : is_asymm α r := sorry

protected theorem acc {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r →r s) (a : α) : acc s (coe_fn f a) → acc r a := sorry

protected theorem well_founded {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r →r s) (h : well_founded s) : well_founded r :=
  well_founded.dcases_on h
    fun (h : ∀ (a : β), acc s a) =>
      idRhs (well_founded r) (well_founded.intro fun (a : α) => rel_hom.acc f a (h (coe_fn f a)))

theorem map_inf {α : Type u_1} {β : Type u_2} [semilattice_inf α] [linear_order β] (a : Less →r Less) (m : β) (n : β) : coe_fn a (m ⊓ n) = coe_fn a m ⊓ coe_fn a n := sorry

theorem map_sup {α : Type u_1} {β : Type u_2} [semilattice_sup α] [linear_order β] (a : gt →r gt) (m : β) (n : β) : coe_fn a (m ⊔ n) = coe_fn a m ⊔ coe_fn a n := sorry

end rel_hom

/-- An increasing function is injective -/
theorem injective_of_increasing {α : Type u_1} {β : Type u_2} (r : α → α → Prop) (s : β → β → Prop) [is_trichotomous α r] [is_irrefl β s] (f : α → β) (hf : ∀ {x y : α}, r x y → s (f x) (f y)) : function.injective f := sorry

/-- An increasing function is injective -/
theorem rel_hom.injective_of_increasing {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} [is_trichotomous α r] [is_irrefl β s] (f : r →r s) : function.injective ⇑f :=
  injective_of_increasing r s ⇑f fun (x y : α) => rel_hom.map_rel f

theorem surjective.well_founded_iff {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} {f : α → β} (hf : function.surjective f) (o : ∀ {a b : α}, r a b ↔ s (f a) (f b)) : well_founded r ↔ well_founded s := sorry

/-- A relation embedding with respect to a given pair of relations `r` and `s`
is an embedding `f : α ↪ β` such that `r a b ↔ s (f a) (f b)`. -/
structure rel_embedding {α : Type u_4} {β : Type u_5} (r : α → α → Prop) (s : β → β → Prop)
extends α ↪ β
where
  map_rel_iff' : ∀ {a b : α}, s (coe_fn _to_embedding a) (coe_fn _to_embedding b) ↔ r a b

infixl:25 " ↪r " => Mathlib.rel_embedding

/-- An order embedding is an embedding `f : α ↪ β` such that `a ≤ b ↔ (f a) ≤ (f b)`.
This definition is an abbreviation of `rel_embedding (≤) (≤)`. -/
def order_embedding (α : Type u_1) (β : Type u_2) [HasLessEq α] [HasLessEq β] :=
  LessEq ↪r LessEq

infixl:25 " ↪o " => Mathlib.order_embedding

/-- The induced relation on a subtype is an embedding under the natural inclusion. -/
def subtype.rel_embedding {X : Type u_1} (r : X → X → Prop) (p : X → Prop) : subtype.val ⁻¹'o r ↪r r :=
  rel_embedding.mk (function.embedding.subtype p) sorry

theorem preimage_equivalence {α : Sort u_1} {β : Sort u_2} (f : α → β) {s : β → β → Prop} (hs : equivalence s) : equivalence (f ⁻¹'o s) := sorry

namespace rel_embedding

/-- A relation embedding is also a relation homomorphism -/
def to_rel_hom {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ↪r s) : r →r s :=
  rel_hom.mk (function.embedding.to_fun (to_embedding f)) sorry

-- see Note [function coercion]

protected instance rel_hom.has_coe {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} : has_coe (r ↪r s) (r →r s) :=
  has_coe.mk to_rel_hom

protected instance has_coe_to_fun {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} : has_coe_to_fun (r ↪r s) :=
  has_coe_to_fun.mk (fun (_x : r ↪r s) => α → β) fun (o : r ↪r s) => ⇑(to_embedding o)

@[simp] theorem to_rel_hom_eq_coe {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ↪r s) : to_rel_hom f = ↑f :=
  rfl

@[simp] theorem coe_coe_fn {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ↪r s) : ⇑↑f = ⇑f :=
  rfl

theorem injective {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ↪r s) : function.injective ⇑f :=
  function.embedding.inj' (to_embedding f)

theorem map_rel_iff {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ↪r s) {a : α} {b : α} : s (coe_fn f a) (coe_fn f b) ↔ r a b :=
  map_rel_iff' f

@[simp] theorem coe_fn_mk {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : α ↪ β) (o : ∀ {a b : α}, s (coe_fn f a) (coe_fn f b) ↔ r a b) : ⇑(mk f o) = ⇑f :=
  rfl

@[simp] theorem coe_fn_to_embedding {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ↪r s) : ⇑(to_embedding f) = ⇑f :=
  rfl

/-- The map `coe_fn : (r ↪r s) → (α → β)` is injective. We can't use `function.injective`
here but mimic its signature by using `⦃e₁ e₂⦄`. -/
theorem coe_fn_inj {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} {e₁ : r ↪r s} {e₂ : r ↪r s} : ⇑e₁ = ⇑e₂ → e₁ = e₂ := sorry

theorem ext {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} {f : r ↪r s} {g : r ↪r s} (h : ∀ (x : α), coe_fn f x = coe_fn g x) : f = g :=
  coe_fn_inj (funext h)

theorem ext_iff {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} {f : r ↪r s} {g : r ↪r s} : f = g ↔ ∀ (x : α), coe_fn f x = coe_fn g x :=
  { mp := fun (h : f = g) (x : α) => h ▸ rfl, mpr := fun (h : ∀ (x : α), coe_fn f x = coe_fn g x) => ext h }

/-- Identity map is a relation embedding. -/
protected def refl {α : Type u_1} (r : α → α → Prop) : r ↪r r :=
  mk (function.embedding.refl α) sorry

/-- Composition of two relation embeddings is a relation embedding. -/
protected def trans {α : Type u_1} {β : Type u_2} {γ : Type u_3} {r : α → α → Prop} {s : β → β → Prop} {t : γ → γ → Prop} (f : r ↪r s) (g : s ↪r t) : r ↪r t :=
  mk (function.embedding.trans (to_embedding f) (to_embedding g)) sorry

protected instance inhabited {α : Type u_1} (r : α → α → Prop) : Inhabited (r ↪r r) :=
  { default := rel_embedding.refl r }

@[simp] theorem refl_apply {α : Type u_1} {r : α → α → Prop} (x : α) : coe_fn (rel_embedding.refl r) x = x :=
  rfl

theorem trans_apply {α : Type u_1} {β : Type u_2} {γ : Type u_3} {r : α → α → Prop} {s : β → β → Prop} {t : γ → γ → Prop} (f : r ↪r s) (g : s ↪r t) (a : α) : coe_fn (rel_embedding.trans f g) a = coe_fn g (coe_fn f a) :=
  rfl

@[simp] theorem coe_trans {α : Type u_1} {β : Type u_2} {γ : Type u_3} {r : α → α → Prop} {s : β → β → Prop} {t : γ → γ → Prop} (f : r ↪r s) (g : s ↪r t) : ⇑(rel_embedding.trans f g) = ⇑g ∘ ⇑f :=
  rfl

/-- A relation embedding is also a relation embedding between dual relations. -/
protected def swap {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ↪r s) : function.swap r ↪r function.swap s :=
  mk (to_embedding f) sorry

/-- If `f` is injective, then it is a relation embedding from the
  preimage relation of `s` to `s`. -/
def preimage {α : Type u_1} {β : Type u_2} (f : α ↪ β) (s : β → β → Prop) : ⇑f ⁻¹'o s ↪r s :=
  mk f sorry

theorem eq_preimage {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ↪r s) : r = ⇑f ⁻¹'o s :=
  funext fun (a : α) => funext fun (b : α) => propext (iff.symm (map_rel_iff f))

protected theorem is_irrefl {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ↪r s) [is_irrefl β s] : is_irrefl α r :=
  is_irrefl.mk fun (a : α) => mt (iff.mpr (map_rel_iff f)) (irrefl (coe_fn f a))

protected theorem is_refl {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ↪r s) [is_refl β s] : is_refl α r :=
  is_refl.mk fun (a : α) => iff.mp (map_rel_iff f) (refl (coe_fn f a))

protected theorem is_symm {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ↪r s) [is_symm β s] : is_symm α r :=
  is_symm.mk fun (a b : α) => imp_imp_imp (iff.mpr (map_rel_iff f)) (iff.mp (map_rel_iff f)) symm

protected theorem is_asymm {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ↪r s) [is_asymm β s] : is_asymm α r :=
  is_asymm.mk fun (a b : α) (h₁ : r a b) (h₂ : r b a) => asymm (iff.mpr (map_rel_iff f) h₁) (iff.mpr (map_rel_iff f) h₂)

protected theorem is_antisymm {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ↪r s) [is_antisymm β s] : is_antisymm α r := sorry

protected theorem is_trans {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ↪r s) [is_trans β s] : is_trans α r := sorry

protected theorem is_total {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ↪r s) [is_total β s] : is_total α r := sorry

protected theorem is_preorder {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ↪r s) [is_preorder β s] : is_preorder α r :=
  idRhs (is_preorder α r) is_preorder.mk

protected theorem is_partial_order {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ↪r s) [is_partial_order β s] : is_partial_order α r :=
  idRhs (is_partial_order α r) is_partial_order.mk

protected theorem is_linear_order {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ↪r s) [is_linear_order β s] : is_linear_order α r :=
  idRhs (is_linear_order α r) is_linear_order.mk

protected theorem is_strict_order {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ↪r s) [is_strict_order β s] : is_strict_order α r :=
  idRhs (is_strict_order α r) is_strict_order.mk

protected theorem is_trichotomous {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ↪r s) [is_trichotomous β s] : is_trichotomous α r := sorry

protected theorem is_strict_total_order' {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ↪r s) [is_strict_total_order' β s] : is_strict_total_order' α r :=
  idRhs (is_strict_total_order' α r) is_strict_total_order'.mk

protected theorem acc {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ↪r s) (a : α) : acc s (coe_fn f a) → acc r a := sorry

protected theorem well_founded {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ↪r s) (h : well_founded s) : well_founded r :=
  well_founded.dcases_on h
    fun (h : ∀ (a : β), acc s a) =>
      idRhs (well_founded r) (well_founded.intro fun (a : α) => rel_embedding.acc f a (h (coe_fn f a)))

protected theorem is_well_order {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ↪r s) [is_well_order β s] : is_well_order α r :=
  idRhs (is_well_order α r) (is_well_order.mk (rel_embedding.well_founded f is_well_order.wf))

/-- It suffices to prove `f` is monotone between strict relations
  to show it is a relation embedding. -/
def of_monotone {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} [is_trichotomous α r] [is_asymm β s] (f : α → β) (H : ∀ (a b : α), r a b → s (f a) (f b)) : r ↪r s :=
  mk (function.embedding.mk f sorry) sorry

@[simp] theorem of_monotone_coe {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} [is_trichotomous α r] [is_asymm β s] (f : α → β) (H : ∀ (a b : α), r a b → s (f a) (f b)) : ⇑(of_monotone f H) = f :=
  rfl

/-- Embeddings of partial orders that preserve `<` also preserve `≤`  -/
def order_embedding_of_lt_embedding {α : Type u_1} {β : Type u_2} [partial_order α] [partial_order β] (f : Less ↪r Less) : α ↪o β :=
  mk (to_embedding f) sorry

end rel_embedding

namespace order_embedding

/-- lt is preserved by order embeddings of preorders -/
def lt_embedding {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (f : α ↪o β) : Less ↪r Less :=
  rel_embedding.mk (rel_embedding.to_embedding f) sorry

@[simp] theorem lt_embedding_apply {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (f : α ↪o β) (x : α) : coe_fn (lt_embedding f) x = coe_fn f x :=
  rfl

@[simp] theorem le_iff_le {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (f : α ↪o β) {a : α} {b : α} : coe_fn f a ≤ coe_fn f b ↔ a ≤ b :=
  rel_embedding.map_rel_iff f

@[simp] theorem lt_iff_lt {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (f : α ↪o β) {a : α} {b : α} : coe_fn f a < coe_fn f b ↔ a < b :=
  rel_embedding.map_rel_iff (lt_embedding f)

@[simp] theorem eq_iff_eq {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (f : α ↪o β) {a : α} {b : α} : coe_fn f a = coe_fn f b ↔ a = b :=
  function.injective.eq_iff (rel_embedding.injective f)

protected theorem monotone {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (f : α ↪o β) : monotone ⇑f :=
  fun (x y : α) => iff.mpr (le_iff_le f)

protected theorem strict_mono {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (f : α ↪o β) : strict_mono ⇑f :=
  fun (x y : α) => iff.mpr (lt_iff_lt f)

protected theorem acc {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (f : α ↪o β) (a : α) : acc Less (coe_fn f a) → acc Less a :=
  rel_embedding.acc (lt_embedding f) a

protected theorem well_founded {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (f : α ↪o β) : well_founded Less → well_founded Less :=
  rel_embedding.well_founded (lt_embedding f)

protected theorem is_well_order {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (f : α ↪o β) [is_well_order β Less] : is_well_order α Less :=
  rel_embedding.is_well_order (lt_embedding f)

/-- An order embedding is also an order embedding between dual orders. -/
protected def dual {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (f : α ↪o β) : order_dual α ↪o order_dual β :=
  rel_embedding.mk (rel_embedding.to_embedding f) sorry

/-- A sctrictly monotone map from a linear order is an order embedding. --/
def of_strict_mono {α : Type u_1} {β : Type u_2} [linear_order α] [preorder β] (f : α → β) (h : strict_mono f) : α ↪o β :=
  rel_embedding.mk (function.embedding.mk f (strict_mono.injective h)) sorry

@[simp] theorem coe_of_strict_mono {α : Type u_1} {β : Type u_2} [linear_order α] [preorder β] {f : α → β} (h : strict_mono f) : ⇑(of_strict_mono f h) = f :=
  rfl

/-- Embedding of a subtype into the ambient type as an `order_embedding`. -/
def subtype {α : Type u_1} [preorder α] (p : α → Prop) : Subtype p ↪o α :=
  rel_embedding.mk (function.embedding.subtype p) sorry

@[simp] theorem coe_subtype {α : Type u_1} [preorder α] (p : α → Prop) : ⇑(subtype p) = coe :=
  rfl

end order_embedding

/-- A relation isomorphism is an equivalence that is also a relation embedding. -/
structure rel_iso {α : Type u_4} {β : Type u_5} (r : α → α → Prop) (s : β → β → Prop)
extends α ≃ β
where
  map_rel_iff' : ∀ {a b : α}, s (coe_fn _to_equiv a) (coe_fn _to_equiv b) ↔ r a b

infixl:25 " ≃r " => Mathlib.rel_iso

/-- An order isomorphism is an equivalence such that `a ≤ b ↔ (f a) ≤ (f b)`.
This definition is an abbreviation of `rel_iso (≤) (≤)`. -/
def order_iso (α : Type u_1) (β : Type u_2) [HasLessEq α] [HasLessEq β] :=
  LessEq ≃r LessEq

infixl:25 " ≃o " => Mathlib.order_iso

namespace rel_iso

/-- Convert an `rel_iso` to an `rel_embedding`. This function is also available as a coercion
but often it is easier to write `f.to_rel_embedding` than to write explicitly `r` and `s`
in the target type. -/
def to_rel_embedding {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ≃r s) : r ↪r s :=
  rel_embedding.mk (equiv.to_embedding (to_equiv f)) (map_rel_iff' f)

-- see Note [function coercion]

protected instance rel_embedding.has_coe {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} : has_coe (r ≃r s) (r ↪r s) :=
  has_coe.mk to_rel_embedding

protected instance has_coe_to_fun {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} : has_coe_to_fun (r ≃r s) :=
  has_coe_to_fun.mk (fun (_x : r ≃r s) => α → β) fun (f : r ≃r s) => ⇑f

@[simp] theorem to_rel_embedding_eq_coe {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ≃r s) : to_rel_embedding f = ↑f :=
  rfl

@[simp] theorem coe_coe_fn {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ≃r s) : ⇑↑f = ⇑f :=
  rfl

theorem map_rel_iff {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ≃r s) {a : α} {b : α} : s (coe_fn f a) (coe_fn f b) ↔ r a b :=
  map_rel_iff' f

@[simp] theorem coe_fn_mk {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : α ≃ β) (o : ∀ {a b : α}, s (coe_fn f a) (coe_fn f b) ↔ r a b) : ⇑(mk f o) = ⇑f :=
  rfl

@[simp] theorem coe_fn_to_equiv {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ≃r s) : ⇑(to_equiv f) = ⇑f :=
  rfl

theorem injective_to_equiv {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} : function.injective to_equiv := sorry

/-- The map `coe_fn : (r ≃r s) → (α → β)` is injective. Lean fails to parse
`function.injective (λ e : r ≃r s, (e : α → β))`, so we use a trick to say the same. -/
theorem injective_coe_fn {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} : function.injective fun (e : r ≃r s) (x : α) => coe_fn e x :=
  function.injective.comp equiv.injective_coe_fn injective_to_equiv

theorem ext {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} {f : r ≃r s} {g : r ≃r s} (h : ∀ (x : α), coe_fn f x = coe_fn g x) : f = g :=
  injective_coe_fn (funext h)

theorem ext_iff {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} {f : r ≃r s} {g : r ≃r s} : f = g ↔ ∀ (x : α), coe_fn f x = coe_fn g x :=
  { mp := fun (h : f = g) (x : α) => h ▸ rfl, mpr := fun (h : ∀ (x : α), coe_fn f x = coe_fn g x) => ext h }

/-- Identity map is a relation isomorphism. -/
protected def refl {α : Type u_1} (r : α → α → Prop) : r ≃r r :=
  mk (equiv.refl α) sorry

/-- Inverse map of a relation isomorphism is a relation isomorphism. -/
protected def symm {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ≃r s) : s ≃r r :=
  mk (equiv.symm (to_equiv f)) sorry

/-- Composition of two relation isomorphisms is a relation isomorphism. -/
protected def trans {α : Type u_1} {β : Type u_2} {γ : Type u_3} {r : α → α → Prop} {s : β → β → Prop} {t : γ → γ → Prop} (f₁ : r ≃r s) (f₂ : s ≃r t) : r ≃r t :=
  mk (equiv.trans (to_equiv f₁) (to_equiv f₂)) sorry

protected instance inhabited {α : Type u_1} (r : α → α → Prop) : Inhabited (r ≃r r) :=
  { default := rel_iso.refl r }

@[simp] theorem default_def {α : Type u_1} (r : α → α → Prop) : Inhabited.default = rel_iso.refl r :=
  rfl

/-- a relation isomorphism is also a relation isomorphism between dual relations. -/
protected def swap {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ≃r s) : function.swap r ≃r function.swap s :=
  mk (to_equiv f) sorry

@[simp] theorem coe_fn_symm_mk {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : α ≃ β) (o : ∀ {a b : α}, s (coe_fn f a) (coe_fn f b) ↔ r a b) : ⇑(rel_iso.symm (mk f o)) = ⇑(equiv.symm f) :=
  rfl

@[simp] theorem refl_apply {α : Type u_1} {r : α → α → Prop} (x : α) : coe_fn (rel_iso.refl r) x = x :=
  rfl

@[simp] theorem trans_apply {α : Type u_1} {β : Type u_2} {γ : Type u_3} {r : α → α → Prop} {s : β → β → Prop} {t : γ → γ → Prop} (f : r ≃r s) (g : s ≃r t) (a : α) : coe_fn (rel_iso.trans f g) a = coe_fn g (coe_fn f a) :=
  rfl

@[simp] theorem apply_symm_apply {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (e : r ≃r s) (x : β) : coe_fn e (coe_fn (rel_iso.symm e) x) = x :=
  equiv.apply_symm_apply (to_equiv e) x

@[simp] theorem symm_apply_apply {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (e : r ≃r s) (x : α) : coe_fn (rel_iso.symm e) (coe_fn e x) = x :=
  equiv.symm_apply_apply (to_equiv e) x

theorem rel_symm_apply {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (e : r ≃r s) {x : α} {y : β} : r x (coe_fn (rel_iso.symm e) y) ↔ s (coe_fn e x) y := sorry

theorem symm_apply_rel {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (e : r ≃r s) {x : β} {y : α} : r (coe_fn (rel_iso.symm e) x) y ↔ s x (coe_fn e y) := sorry

protected theorem bijective {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (e : r ≃r s) : function.bijective ⇑e :=
  equiv.bijective (to_equiv e)

protected theorem injective {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (e : r ≃r s) : function.injective ⇑e :=
  equiv.injective (to_equiv e)

protected theorem surjective {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (e : r ≃r s) : function.surjective ⇑e :=
  equiv.surjective (to_equiv e)

@[simp] theorem range_eq {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (e : r ≃r s) : set.range ⇑e = set.univ :=
  function.surjective.range_eq (rel_iso.surjective e)

@[simp] theorem eq_iff_eq {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ≃r s) {a : α} {b : α} : coe_fn f a = coe_fn f b ↔ a = b :=
  function.injective.eq_iff (rel_iso.injective f)

/-- Any equivalence lifts to a relation isomorphism between `s` and its preimage. -/
protected def preimage {α : Type u_1} {β : Type u_2} (f : α ≃ β) (s : β → β → Prop) : ⇑f ⁻¹'o s ≃r s :=
  mk f sorry

/-- A surjective relation embedding is a relation isomorphism. -/
def of_surjective {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ↪r s) (H : function.surjective ⇑f) : r ≃r s :=
  mk (equiv.of_bijective ⇑f sorry) sorry

@[simp] theorem of_surjective_coe {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (f : r ↪r s) (H : function.surjective ⇑f) : ⇑(of_surjective f H) = ⇑f :=
  rfl

/--
Given relation isomorphisms `r₁ ≃r r₂` and `s₁ ≃r s₂`, construct a relation isomorphism for the
lexicographic orders on the sum.
-/
def sum_lex_congr {α₁ : Type u_1} {α₂ : Type u_2} {β₁ : Type u_3} {β₂ : Type u_4} {r₁ : α₁ → α₁ → Prop} {r₂ : α₂ → α₂ → Prop} {s₁ : β₁ → β₁ → Prop} {s₂ : β₂ → β₂ → Prop} (e₁ : r₁ ≃r r₂) (e₂ : s₁ ≃r s₂) : sum.lex r₁ s₁ ≃r sum.lex r₂ s₂ :=
  mk (equiv.sum_congr (to_equiv e₁) (to_equiv e₂)) sorry

/--
Given relation isomorphisms `r₁ ≃r r₂` and `s₁ ≃r s₂`, construct a relation isomorphism for the
lexicographic orders on the product.
-/
def prod_lex_congr {α₁ : Type u_1} {α₂ : Type u_2} {β₁ : Type u_3} {β₂ : Type u_4} {r₁ : α₁ → α₁ → Prop} {r₂ : α₂ → α₂ → Prop} {s₁ : β₁ → β₁ → Prop} {s₂ : β₂ → β₂ → Prop} (e₁ : r₁ ≃r r₂) (e₂ : s₁ ≃r s₂) : prod.lex r₁ s₁ ≃r prod.lex r₂ s₂ :=
  mk (equiv.prod_congr (to_equiv e₁) (to_equiv e₂)) sorry

protected instance group {α : Type u_1} {r : α → α → Prop} : group (r ≃r r) :=
  group.mk (fun (f₁ f₂ : r ≃r r) => rel_iso.trans f₂ f₁) sorry (rel_iso.refl r) sorry sorry rel_iso.symm
    (div_inv_monoid.div._default (fun (f₁ f₂ : r ≃r r) => rel_iso.trans f₂ f₁) sorry (rel_iso.refl r) sorry sorry
      rel_iso.symm)
    sorry

@[simp] theorem coe_one {α : Type u_1} {r : α → α → Prop} : ⇑1 = id :=
  rfl

@[simp] theorem coe_mul {α : Type u_1} {r : α → α → Prop} (e₁ : r ≃r r) (e₂ : r ≃r r) : ⇑(e₁ * e₂) = ⇑e₁ ∘ ⇑e₂ :=
  rfl

theorem mul_apply {α : Type u_1} {r : α → α → Prop} (e₁ : r ≃r r) (e₂ : r ≃r r) (x : α) : coe_fn (e₁ * e₂) x = coe_fn e₁ (coe_fn e₂ x) :=
  rfl

@[simp] theorem inv_apply_self {α : Type u_1} {r : α → α → Prop} (e : r ≃r r) (x : α) : coe_fn (e⁻¹) (coe_fn e x) = x :=
  symm_apply_apply e x

@[simp] theorem apply_inv_self {α : Type u_1} {r : α → α → Prop} (e : r ≃r r) (x : α) : coe_fn e (coe_fn (e⁻¹) x) = x :=
  apply_symm_apply e x

end rel_iso

namespace order_iso

/-- Reinterpret an order isomorphism as an order embedding. -/
def to_order_embedding {α : Type u_1} {β : Type u_2} [HasLessEq α] [HasLessEq β] (e : α ≃o β) : α ↪o β :=
  rel_iso.to_rel_embedding e

@[simp] theorem coe_to_order_embedding {α : Type u_1} {β : Type u_2} [HasLessEq α] [HasLessEq β] (e : α ≃o β) : ⇑(to_order_embedding e) = ⇑e :=
  rfl

protected theorem bijective {α : Type u_1} {β : Type u_2} [HasLessEq α] [HasLessEq β] (e : α ≃o β) : function.bijective ⇑e :=
  equiv.bijective (rel_iso.to_equiv e)

protected theorem injective {α : Type u_1} {β : Type u_2} [HasLessEq α] [HasLessEq β] (e : α ≃o β) : function.injective ⇑e :=
  equiv.injective (rel_iso.to_equiv e)

protected theorem surjective {α : Type u_1} {β : Type u_2} [HasLessEq α] [HasLessEq β] (e : α ≃o β) : function.surjective ⇑e :=
  equiv.surjective (rel_iso.to_equiv e)

@[simp] theorem range_eq {α : Type u_1} {β : Type u_2} [HasLessEq α] [HasLessEq β] (e : α ≃o β) : set.range ⇑e = set.univ :=
  function.surjective.range_eq (order_iso.surjective e)

@[simp] theorem apply_eq_iff_eq {α : Type u_1} {β : Type u_2} [HasLessEq α] [HasLessEq β] (e : α ≃o β) {x : α} {y : α} : coe_fn e x = coe_fn e y ↔ x = y :=
  equiv.apply_eq_iff_eq (rel_iso.to_equiv e)

/-- Inverse of an order isomorphism. -/
def symm {α : Type u_1} {β : Type u_2} [HasLessEq α] [HasLessEq β] (e : α ≃o β) : β ≃o α :=
  rel_iso.symm e

@[simp] theorem apply_symm_apply {α : Type u_1} {β : Type u_2} [HasLessEq α] [HasLessEq β] (e : α ≃o β) (x : β) : coe_fn e (coe_fn (symm e) x) = x :=
  equiv.apply_symm_apply (rel_iso.to_equiv e) x

@[simp] theorem symm_apply_apply {α : Type u_1} {β : Type u_2} [HasLessEq α] [HasLessEq β] (e : α ≃o β) (x : α) : coe_fn (symm e) (coe_fn e x) = x :=
  equiv.symm_apply_apply (rel_iso.to_equiv e) x

theorem symm_apply_eq {α : Type u_1} {β : Type u_2} [HasLessEq α] [HasLessEq β] (e : α ≃o β) {x : α} {y : β} : coe_fn (symm e) y = x ↔ y = coe_fn e x :=
  equiv.symm_apply_eq (rel_iso.to_equiv e)

@[simp] theorem symm_symm {α : Type u_1} {β : Type u_2} [HasLessEq α] [HasLessEq β] (e : α ≃o β) : symm (symm e) = e :=
  rel_iso.ext fun (x : α) => Eq.refl (coe_fn (symm (symm e)) x)

theorem symm_injective {α : Type u_1} {β : Type u_2} [HasLessEq α] [HasLessEq β] : function.injective symm := sorry

@[simp] theorem to_equiv_symm {α : Type u_1} {β : Type u_2} [HasLessEq α] [HasLessEq β] (e : α ≃o β) : equiv.symm (rel_iso.to_equiv e) = rel_iso.to_equiv (symm e) :=
  rfl

/-- Composition of two order isomorphisms is an order isomorphism. -/
def trans {α : Type u_1} {β : Type u_2} {γ : Type u_3} [HasLessEq α] [HasLessEq β] [HasLessEq γ] (e : α ≃o β) (e' : β ≃o γ) : α ≃o γ :=
  rel_iso.trans e e'

@[simp] theorem coe_trans {α : Type u_1} {β : Type u_2} {γ : Type u_3} [HasLessEq α] [HasLessEq β] [HasLessEq γ] (e : α ≃o β) (e' : β ≃o γ) : ⇑(trans e e') = ⇑e' ∘ ⇑e :=
  rfl

theorem trans_apply {α : Type u_1} {β : Type u_2} {γ : Type u_3} [HasLessEq α] [HasLessEq β] [HasLessEq γ] (e : α ≃o β) (e' : β ≃o γ) (x : α) : coe_fn (trans e e') x = coe_fn e' (coe_fn e x) :=
  rfl

protected theorem monotone {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (e : α ≃o β) : monotone ⇑e :=
  order_embedding.monotone (to_order_embedding e)

protected theorem strict_mono {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (e : α ≃o β) : strict_mono ⇑e :=
  order_embedding.strict_mono (to_order_embedding e)

@[simp] theorem le_iff_le {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (e : α ≃o β) {x : α} {y : α} : coe_fn e x ≤ coe_fn e y ↔ x ≤ y :=
  rel_iso.map_rel_iff e

@[simp] theorem lt_iff_lt {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (e : α ≃o β) {x : α} {y : α} : coe_fn e x < coe_fn e y ↔ x < y :=
  order_embedding.lt_iff_lt (to_order_embedding e)

@[simp] theorem preimage_Iic {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (e : α ≃o β) (b : β) : ⇑e ⁻¹' set.Iic b = set.Iic (coe_fn (symm e) b) := sorry

@[simp] theorem preimage_Ici {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (e : α ≃o β) (b : β) : ⇑e ⁻¹' set.Ici b = set.Ici (coe_fn (symm e) b) := sorry

@[simp] theorem preimage_Iio {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (e : α ≃o β) (b : β) : ⇑e ⁻¹' set.Iio b = set.Iio (coe_fn (symm e) b) := sorry

@[simp] theorem preimage_Ioi {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (e : α ≃o β) (b : β) : ⇑e ⁻¹' set.Ioi b = set.Ioi (coe_fn (symm e) b) := sorry

@[simp] theorem preimage_Icc {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (e : α ≃o β) (a : β) (b : β) : ⇑e ⁻¹' set.Icc a b = set.Icc (coe_fn (symm e) a) (coe_fn (symm e) b) := sorry

@[simp] theorem preimage_Ico {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (e : α ≃o β) (a : β) (b : β) : ⇑e ⁻¹' set.Ico a b = set.Ico (coe_fn (symm e) a) (coe_fn (symm e) b) := sorry

@[simp] theorem preimage_Ioc {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (e : α ≃o β) (a : β) (b : β) : ⇑e ⁻¹' set.Ioc a b = set.Ioc (coe_fn (symm e) a) (coe_fn (symm e) b) := sorry

@[simp] theorem preimage_Ioo {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (e : α ≃o β) (a : β) (b : β) : ⇑e ⁻¹' set.Ioo a b = set.Ioo (coe_fn (symm e) a) (coe_fn (symm e) b) := sorry

/-- To show that `f : α → β`, `g : β → α` make up an order isomorphism of linear orders,
    it suffices to prove `cmp a (g b) = cmp (f a) b`. --/
def of_cmp_eq_cmp {α : Type u_1} {β : Type u_2} [linear_order α] [linear_order β] (f : α → β) (g : β → α) (h : ∀ (a : α) (b : β), cmp a (g b) = cmp (f a) b) : α ≃o β :=
  (fun (gf : ∀ (a : α), a = g (f a)) => rel_iso.mk (equiv.mk f g sorry sorry) sorry) sorry

/-- Order isomorphism between two equal sets. -/
def set_congr {α : Type u_1} [preorder α] (s : set α) (t : set α) (h : s = t) : ↥s ≃o ↥t :=
  rel_iso.mk (equiv.set_congr h) sorry

/-- Order isomorphism between `univ : set α` and `α`. -/
def set.univ {α : Type u_1} [preorder α] : ↥set.univ ≃o α :=
  rel_iso.mk (equiv.set.univ α) sorry

end order_iso

/-- If a function `f` is strictly monotone on a set `s`, then it defines an order isomorphism
between `s` and its image. -/
protected def strict_mono_incr_on.order_iso {α : Type u_1} {β : Type u_2} [linear_order α] [preorder β] (f : α → β) (s : set α) (hf : strict_mono_incr_on f s) : ↥s ≃o ↥(f '' s) :=
  rel_iso.mk (set.bij_on.equiv f sorry) sorry

/-- A strictly monotone function from a linear order is an order isomorphism between its domain and
its range. -/
protected def strict_mono.order_iso {α : Type u_1} {β : Type u_2} [linear_order α] [preorder β] (f : α → β) (h_mono : strict_mono f) : α ≃o ↥(set.range f) :=
  rel_iso.mk (equiv.set.range f (strict_mono.injective h_mono)) sorry

/-- A strictly monotone surjective function from a linear order is an order isomorphism. -/
def strict_mono.order_iso_of_surjective {α : Type u_1} {β : Type u_2} [linear_order α] [preorder β] (f : α → β) (h_mono : strict_mono f) (h_surj : function.surjective f) : α ≃o β :=
  order_iso.trans (strict_mono.order_iso f h_mono)
    (order_iso.trans (order_iso.set_congr (set.range f) set.univ (function.surjective.range_eq h_surj))
      order_iso.set.univ)

/-- `subrel r p` is the inherited relation on a subset. -/
def subrel {α : Type u_1} (r : α → α → Prop) (p : set α) : ↥p → ↥p → Prop :=
  coe ⁻¹'o r

@[simp] theorem subrel_val {α : Type u_1} (r : α → α → Prop) (p : set α) {a : ↥p} {b : ↥p} : subrel r p a b ↔ r (subtype.val a) (subtype.val b) :=
  iff.rfl

namespace subrel

/-- The relation embedding from the inherited relation on a subset. -/
protected def rel_embedding {α : Type u_1} (r : α → α → Prop) (p : set α) : subrel r p ↪r r :=
  rel_embedding.mk (function.embedding.subtype fun (x : α) => x ∈ p) sorry

@[simp] theorem rel_embedding_apply {α : Type u_1} (r : α → α → Prop) (p : set α) (a : ↥p) : coe_fn (subrel.rel_embedding r p) a = subtype.val a :=
  rfl

protected instance is_well_order {α : Type u_1} (r : α → α → Prop) [is_well_order α r] (p : set α) : is_well_order (↥p) (subrel r p) :=
  rel_embedding.is_well_order (subrel.rel_embedding r p)

end subrel

/-- Restrict the codomain of a relation embedding. -/
def rel_embedding.cod_restrict {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (p : set β) (f : r ↪r s) (H : ∀ (a : α), coe_fn f a ∈ p) : r ↪r subrel s p :=
  rel_embedding.mk (function.embedding.cod_restrict p (rel_embedding.to_embedding f) H) (rel_embedding.map_rel_iff' f)

@[simp] theorem rel_embedding.cod_restrict_apply {α : Type u_1} {β : Type u_2} {r : α → α → Prop} {s : β → β → Prop} (p : set β) (f : r ↪r s) (H : ∀ (a : α), coe_fn f a ∈ p) (a : α) : coe_fn (rel_embedding.cod_restrict p f H) a = { val := coe_fn f a, property := H a } :=
  rfl

protected def order_iso.dual {α : Type u_1} {β : Type u_2} [preorder α] [preorder β] (f : α ≃o β) : order_dual α ≃o order_dual β :=
  rel_iso.mk (rel_iso.to_equiv f) sorry

theorem order_iso.map_bot' {α : Type u_1} {β : Type u_2} [partial_order α] [partial_order β] (f : α ≃o β) {x : α} {y : β} (hx : ∀ (x' : α), x ≤ x') (hy : ∀ (y' : β), y ≤ y') : coe_fn f x = y := sorry

theorem order_iso.map_bot {α : Type u_1} {β : Type u_2} [order_bot α] [order_bot β] (f : α ≃o β) : coe_fn f ⊥ = ⊥ :=
  order_iso.map_bot' f (fun (_x : α) => bot_le) fun (_x : β) => bot_le

theorem order_iso.map_top' {α : Type u_1} {β : Type u_2} [partial_order α] [partial_order β] (f : α ≃o β) {x : α} {y : β} (hx : ∀ (x' : α), x' ≤ x) (hy : ∀ (y' : β), y' ≤ y) : coe_fn f x = y :=
  order_iso.map_bot' (order_iso.dual f) hx hy

theorem order_iso.map_top {α : Type u_1} {β : Type u_2} [order_top α] [order_top β] (f : α ≃o β) : coe_fn f ⊤ = ⊤ :=
  order_iso.map_bot (order_iso.dual f)

theorem order_embedding.map_inf_le {α : Type u_1} {β : Type u_2} [semilattice_inf α] [semilattice_inf β] (f : α ↪o β) (x : α) (y : α) : coe_fn f (x ⊓ y) ≤ coe_fn f x ⊓ coe_fn f y :=
  monotone.map_inf_le (order_embedding.monotone f) x y

theorem order_iso.map_inf {α : Type u_1} {β : Type u_2} [semilattice_inf α] [semilattice_inf β] (f : α ≃o β) (x : α) (y : α) : coe_fn f (x ⊓ y) = coe_fn f x ⊓ coe_fn f y := sorry

theorem order_embedding.le_map_sup {α : Type u_1} {β : Type u_2} [semilattice_sup α] [semilattice_sup β] (f : α ↪o β) (x : α) (y : α) : coe_fn f x ⊔ coe_fn f y ≤ coe_fn f (x ⊔ y) :=
  monotone.le_map_sup (order_embedding.monotone f) x y

theorem order_iso.map_sup {α : Type u_1} {β : Type u_2} [semilattice_sup α] [semilattice_sup β] (f : α ≃o β) (x : α) (y : α) : coe_fn f (x ⊔ y) = coe_fn f x ⊔ coe_fn f y :=
  order_iso.map_inf (order_iso.dual f) x y


/-
Copyright (c) 2022 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Floris van Doorn
-/

import geometry.manifold.vector_bundle.pullback
import topology.vector_bundle.hom
import to_mathlib.geometry.manifold.misc_manifold

/-!
# Various operations on and properties of smooth vector bundles
-/

noncomputable theory

open bundle set topological_space local_homeomorph
open_locale classical manifold bundle

namespace fiber_bundle

variables {𝕜 B B' F M : Type*} {E : B → Type*}
variables [topological_space F] [topological_space (total_space E)] [∀ x, topological_space (E x)]
  {HB : Type*} [topological_space HB]
  [topological_space B] [charted_space HB B] [fiber_bundle F E]

lemma charted_space_chart_at_fst' (x y : total_space E) :
  (chart_at (model_prod HB F) x y).1 =
  chart_at HB x.proj (trivialization_at F E x.proj y).1 :=
by { rw [charted_space_chart_at], refl }

lemma charted_space_chart_at_fst {x y : total_space E}
  (hy : y.proj ∈ (trivialization_at F E x.proj).base_set) :
  (chart_at (model_prod HB F) x y).1 = chart_at HB x.proj y.proj :=
by rw [charted_space_chart_at_fst', (trivialization_at F E x.proj).coe_fst' hy]

lemma charted_space_chart_at_snd (x y : total_space E) :
  (chart_at (model_prod HB F) x y).2 = (trivialization_at F E x.proj y).2 :=
by { rw [charted_space_chart_at], refl }

end fiber_bundle

section vector_bundle

variables {𝕜 B F F₁ F₂ : Type*}
  {E : B → Type*} {E₁ : B → Type*} {E₂ : B → Type*}
  [nontrivially_normed_field 𝕜]
  [∀ x, add_comm_monoid (E x)] [∀ x, module 𝕜 (E x)]
  [normed_add_comm_group F] [normed_space 𝕜 F]
  [topological_space (total_space E)] [∀ x, topological_space (E x)]
  [∀ x, add_comm_monoid (E₁ x)] [∀ x, module 𝕜 (E₁ x)]
  [normed_add_comm_group F₁] [normed_space 𝕜 F₁]
  [topological_space (total_space E₁)] [∀ x, topological_space (E₁ x)]
  [∀ x, add_comm_monoid (E₂ x)] [∀ x, module 𝕜 (E₂ x)]
  [normed_add_comm_group F₂] [normed_space 𝕜 F₂]
  [topological_space (total_space E₂)] [∀ x, topological_space (E₂ x)]
  [topological_space B]
  {n : ℕ∞}
  [fiber_bundle F₁ E₁] [vector_bundle 𝕜 F₁ E₁]
  [fiber_bundle F₂ E₂] [vector_bundle 𝕜 F₂ E₂]
  {e₁ e₁' : trivialization F₁ (π E₁)} {e₂ e₂' : trivialization F₂ (π E₂)}

end vector_bundle

namespace vector_bundle_core

variables {R 𝕜 B F ι : Type*}
  [nontrivially_normed_field R]
  [normed_add_comm_group F] [normed_space R F] [topological_space B]
  (Z : vector_bundle_core R B F ι)

/-- `Z.coord_change j i` is a partial inverse of `Z.coord_change i j`. -/
lemma coord_change_comp_eq_self {i j : ι} {x : B} (hx : x ∈ Z.base_set i ∩ Z.base_set j) (v : F) :
  Z.coord_change j i x (Z.coord_change i j x v) = v :=
by rw [Z.coord_change_comp i j i x ⟨hx, hx.1⟩, Z.coord_change_self i x hx.1]

end vector_bundle_core

namespace bundle.trivial
open _root_.trivialization

variables {𝕜 B F : Type*}
variables [nontrivially_normed_field 𝕜] [normed_add_comm_group F] [normed_space 𝕜 F]
  [topological_space B]

@[simp, mfld_simps]
protected lemma trivialization_at (x : B) :
  trivialization_at F (trivial B F) x = trivial.trivialization B F :=
rfl

@[simp, mfld_simps]
lemma trivialization_continuous_linear_map_at (x : B) :
  (trivial.trivialization B F).continuous_linear_map_at 𝕜 x = continuous_linear_map.id 𝕜 F :=
begin
  ext v,
  simp_rw [continuous_linear_map_at_apply, coe_linear_map_at],
  rw [if_pos],
  exacts [rfl, mem_univ _]
end

end bundle.trivial

section hom
variables {𝕜₁ : Type*} [nontrivially_normed_field 𝕜₁] {𝕜₂ : Type*} [nontrivially_normed_field 𝕜₂]
  (σ : 𝕜₁ →+* 𝕜₂) [iσ : ring_hom_isometric σ]

variables {B : Type*} [topological_space B]

variables (F₁ : Type*) [normed_add_comm_group F₁] [normed_space 𝕜₁ F₁]
  (E₁ : B → Type*) [Π x, add_comm_monoid (E₁ x)] [Π x, module 𝕜₁ (E₁ x)]
  [topological_space (total_space E₁)]
variables (F₂ : Type*) [normed_add_comm_group F₂][normed_space 𝕜₂ F₂]
  (E₂ : B → Type*) [Π x, add_comm_monoid (E₂ x)] [Π x, module 𝕜₂ (E₂ x)]
  [topological_space (total_space E₂)]
variables (F₁ E₁ F₂ E₂) [ring_hom_isometric σ]
variables [Π x : B, topological_space (E₁ x)] [fiber_bundle F₁ E₁] [vector_bundle 𝕜₁ F₁ E₁]
variables [Π x : B, topological_space (E₂ x)] [fiber_bundle F₂ E₂] [vector_bundle 𝕜₂ F₂ E₂]
variables [Π x, has_continuous_add (E₂ x)] [Π x, has_continuous_smul 𝕜₂ (E₂ x)]

@[simp, mfld_simps]
lemma continuous_linear_map_trivialization_at (x : B) :
  trivialization_at (F₁ →SL[σ] F₂) (bundle.continuous_linear_map σ F₁ E₁ F₂ E₂) x =
  (trivialization_at F₁ E₁ x).continuous_linear_map σ (trivialization_at F₂ E₂ x) :=
rfl

end hom

section pullback

/-- We need some instances like this to work with negation on pullbacks -/
instance {B B'} {E : B → Type*} {f : B' → B} {x : B'} [∀ x', add_comm_group (E x')] :
  add_comm_group ((f *ᵖ E) x) :=
by delta_instance bundle.pullback

instance {B B'} {E : B → Type*} {f : B' → B} {x : B'} [∀ x', has_zero (E x')] :
  has_zero ((f *ᵖ E) x) :=
by delta_instance bundle.pullback

variables {B F B' K : Type*} {E : B → Type*} {f : K}
  [topological_space B'] [topological_space (total_space E)]
  [topological_space F] [topological_space B]
  [∀ b, has_zero (E b)] [continuous_map_class K B' B]

namespace trivialization

-- attribute [simps base_set] trivialization.pullback

lemma pullback_symm (e : trivialization F (π E)) (x : B') : (e.pullback f).symm x = e.symm (f x) :=
begin
  ext y,
  simp_rw [trivialization.symm, pretrivialization.symm],
  congr', ext (hx : f x ∈ e.to_pretrivialization.base_set),
  change cast _ (e.symm (f x) y) = cast _ ((e.to_local_homeomorph.symm (f x, y)).2),
  simp_rw [trivialization.symm, pretrivialization.symm, dif_pos hx, cast_cast],
  refl,
end

end trivialization

variables [∀ x, topological_space (E x)] [fiber_bundle F E]

lemma pullback_trivialization_at {x : B'} : trivialization_at F (f *ᵖ E) x =
  (trivialization_at F E (f x)).pullback f :=
rfl

end pullback

section pullback_vb

variables {R 𝕜 B F B' : Type*} {E : B → Type*}

variables [topological_space B'] [topological_space (total_space E)]
  [nontrivially_normed_field 𝕜] [normed_add_comm_group F] [normed_space 𝕜 F] [topological_space B]
  [∀ x, add_comm_monoid (E x)] [∀ x, module 𝕜 (E x)] [∀ x, topological_space (E x)]
  [fiber_bundle F E]
  {K : Type*} [continuous_map_class K B' B] (f : K)

namespace trivialization
lemma pullback_symmL (e : trivialization F (π E)) [e.is_linear 𝕜] (x : B') :
  (e.pullback f).symmL 𝕜 x = e.symmL 𝕜 (f x) :=
by { ext y, simp_rw [symmL_apply, pullback_symm] }

end trivialization

end pullback_vb

namespace vector_prebundle

attribute [reducible] vector_prebundle.to_fiber_bundle

/-!
### `vector_prebundle.is_smooth`

Todo: maybe redefine `vector_prebundle` as a mixin `fiber_prebundle.is_vector_prebundle`.
The reason is that if you define a `fiber_prebundle` operation, and then
(under certain circumstances)
upgrade it to a `vector_prebundle`, this will result in `fiber_bundle` instances that are probably
not easily seen as definitionally equal by type-class inference.
-/

variables {𝕜 B F F₁ F₂ M M₁ M₂ : Type*}
  {E : B → Type*} {E₁ : B → Type*} {E₂ : B → Type*}
  [nontrivially_normed_field 𝕜]
  [∀ x, add_comm_monoid (E x)] [∀ x, module 𝕜 (E x)]
  [normed_add_comm_group F] [normed_space 𝕜 F]
  [∀ x, add_comm_monoid (E₁ x)] [∀ x, module 𝕜 (E₁ x)]
  [normed_add_comm_group F₁] [normed_space 𝕜 F₁]
  [∀ x, add_comm_monoid (E₂ x)] [∀ x, module 𝕜 (E₂ x)]
  [normed_add_comm_group F₂] [normed_space 𝕜 F₂]
  {EB : Type*} [normed_add_comm_group EB] [normed_space 𝕜 EB]
  {HB : Type*} [topological_space HB] (IB : model_with_corners 𝕜 EB HB)
  [topological_space B] [charted_space HB B] [smooth_manifold_with_corners IB B]
  {EM : Type*} [normed_add_comm_group EM] [normed_space 𝕜 EM]
  {HM : Type*} [topological_space HM] {IM : model_with_corners 𝕜 EM HM}
  [topological_space M] [charted_space HM M]
  {n : ℕ∞}

variables (IB)

/-- Mixin for a `vector_prebundle` stating smoothness of coordinate changes. -/
class is_smooth (a : vector_prebundle 𝕜 F E) : Prop :=
(exists_smooth_coord_change : ∀ (e e' ∈ a.pretrivialization_atlas), ∃ f : B → F →L[𝕜] F,
  smooth_on IB 𝓘(𝕜, F →L[𝕜] F) f (e.base_set ∩ e'.base_set) ∧
  ∀ (b : B) (hb : b ∈ e.base_set ∩ e'.base_set) (v : F),
    f b v = (e' (total_space_mk b (e.symm b v))).2)

variables (a : vector_prebundle 𝕜 F E) [ha : a.is_smooth IB] {e e' : pretrivialization F (π E)}
include ha

/-- A randomly chosen coordinate change on a `smooth_vector_prebundle`, given by
  the field `exists_coord_change`. -/
def smooth_coord_change (he : e ∈ a.pretrivialization_atlas) (he' : e' ∈ a.pretrivialization_atlas)
  (b : B) : F →L[𝕜] F :=
classical.some (ha.exists_smooth_coord_change e he e' he') b

variables {IB}
lemma smooth_on_smooth_coord_change (he : e ∈ a.pretrivialization_atlas)
  (he' : e' ∈ a.pretrivialization_atlas) :
  smooth_on IB 𝓘(𝕜, F →L[𝕜] F) (a.smooth_coord_change IB he he') (e.base_set ∩ e'.base_set) :=
(classical.some_spec (ha.exists_smooth_coord_change e he e' he')).1

lemma smooth_coord_change_apply (he : e ∈ a.pretrivialization_atlas)
  (he' : e' ∈ a.pretrivialization_atlas) {b : B} (hb : b ∈ e.base_set ∩ e'.base_set) (v : F) :
  a.smooth_coord_change IB he he' b v = (e' (total_space_mk b (e.symm b v))).2 :=
(classical.some_spec (ha.exists_smooth_coord_change e he e' he')).2 b hb v

lemma mk_smooth_coord_change (he : e ∈ a.pretrivialization_atlas)
  (he' : e' ∈ a.pretrivialization_atlas) {b : B} (hb : b ∈ e.base_set ∩ e'.base_set) (v : F) :
  (b, (a.smooth_coord_change IB he he' b v)) = e' (total_space_mk b (e.symm b v)) :=
begin
  ext,
  { rw [e.mk_symm hb.1 v, e'.coe_fst', e.proj_symm_apply' hb.1],
    rw [e.proj_symm_apply' hb.1], exact hb.2 },
  { exact a.smooth_coord_change_apply he he' hb v }
end

variables (IB)
/-- Make a `smooth_vector_bundle` from a `smooth_vector_prebundle`.  -/
lemma to_smooth_vector_bundle :
  @smooth_vector_bundle _ _ F E _ _ _ _ _ a.total_space_topology a.fiber_topology _ _ _ _ _ IB
  _ _ _ a.to_fiber_bundle a.to_vector_bundle :=
{ smooth_on_coord_change := begin
    rintros _ _ ⟨e, he, rfl⟩ ⟨e', he', rfl⟩,
    refine (a.smooth_on_smooth_coord_change he he').congr _,
    intros b hb,
    ext v,
    rw [a.smooth_coord_change_apply he he' hb v, continuous_linear_equiv.coe_coe,
      trivialization.coord_changeL_apply],
    exacts [rfl, hb]
  end }

end vector_prebundle

variables {𝕜 B F F₁ F₂ M M₁ M₂ : Type*}
  {E : B → Type*} {E₁ : B → Type*} {E₂ : B → Type*}
  [nontrivially_normed_field 𝕜]
  [∀ x, add_comm_monoid (E x)] [∀ x, module 𝕜 (E x)]
  [normed_add_comm_group F] [normed_space 𝕜 F]
  [topological_space (total_space E)] [∀ x, topological_space (E x)]
  [∀ x, add_comm_monoid (E₁ x)] [∀ x, module 𝕜 (E₁ x)]
  [normed_add_comm_group F₁] [normed_space 𝕜 F₁]
  [topological_space (total_space E₁)] [∀ x, topological_space (E₁ x)]
  [∀ x, add_comm_monoid (E₂ x)] [∀ x, module 𝕜 (E₂ x)]
  [normed_add_comm_group F₂] [normed_space 𝕜 F₂]
  [topological_space (total_space E₂)] [∀ x, topological_space (E₂ x)]

  {EB : Type*} [normed_add_comm_group EB] [normed_space 𝕜 EB]
  {HB : Type*} [topological_space HB] (IB : model_with_corners 𝕜 EB HB)
  [topological_space B] [charted_space HB B]
  {EM : Type*} [normed_add_comm_group EM] [normed_space 𝕜 EM]
  {HM : Type*} [topological_space HM] {IM : model_with_corners 𝕜 EM HM}
  [topological_space M] [charted_space HM M] [Is : smooth_manifold_with_corners IM M]
  {n : ℕ∞}
  [fiber_bundle F₁ E₁] [vector_bundle 𝕜 F₁ E₁]
  [fiber_bundle F₂ E₂] [vector_bundle 𝕜 F₂ E₂]
  {e₁ e₁' : trivialization F₁ (π E₁)} {e₂ e₂' : trivialization F₂ (π E₂)}

/-!
### Homs of smooth vector bundles over the same base space
-/
-- protect `bundle.continuous_linear_map`

section hom
open continuous_linear_map pretrivialization

local notation `σ` := ring_hom.id 𝕜

section general
-- what is better notation for this?
local notation `FE₁E₂` := bundle.continuous_linear_map σ F₁ E₁ F₂ E₂
local notation `LE₁E₂` := total_space FE₁E₂
local notation `PLE₁E₂` := bundle.continuous_linear_map.vector_prebundle σ F₁ E₁ F₂ E₂

/- This proof is slow, especially the `simp only` and the elaboration of `h₂`. -/
lemma smooth_on_continuous_linear_map_coord_change
  [smooth_manifold_with_corners IB B]
  [smooth_vector_bundle F₁ E₁ IB] [smooth_vector_bundle F₂ E₂ IB]
  [mem_trivialization_atlas e₁] [mem_trivialization_atlas e₁']
  [mem_trivialization_atlas e₂] [mem_trivialization_atlas e₂'] :
  smooth_on IB 𝓘(𝕜, ((F₁ →L[𝕜] F₂) →L[𝕜] (F₁ →L[𝕜] F₂)))
    (continuous_linear_map_coord_change σ e₁ e₁' e₂ e₂')
    ((e₁.base_set ∩ e₂.base_set) ∩ (e₁'.base_set ∩ e₂'.base_set)) :=
begin
  let L₁ := compSL F₁ F₂ F₂ σ σ,
  have h₁ : smooth _ _ _ := L₁.cont_mdiff,
  have h₂ : smooth _ _ _ := (continuous_linear_map.flip (compSL F₁ F₁ F₂ σ σ)).cont_mdiff,
  have h₃ : smooth_on IB _ _ _ := smooth_on_coord_change e₁' e₁,
  have h₄ : smooth_on IB _ _ _ := smooth_on_coord_change e₂ e₂',
  refine ((h₁.comp_smooth_on (h₄.mono _)).clm_comp (h₂.comp_smooth_on (h₃.mono _))).congr _,
  { mfld_set_tac },
  { mfld_set_tac },
  { intros b hb, ext L v,
    simp only [continuous_linear_map_coord_change, continuous_linear_equiv.coe_coe,
      continuous_linear_equiv.arrow_congrSL_apply, comp_apply, function.comp, compSL_apply,
      flip_apply, continuous_linear_equiv.symm_symm] },
end

variables [∀ x, has_continuous_add (E₂ x)] [∀ x, has_continuous_smul 𝕜 (E₂ x)]

@[reducible]
def topological_space.continuous_linear_map' (x) : topological_space (FE₁E₂ x) :=
by apply_instance
local attribute [instance, priority 1] topological_space.continuous_linear_map'
-- ^ probably needed because of the type-class pi bug
-- https://leanprover.zulipchat.com/#narrow/stream/116395-maths/topic/vector.20bundles.20--.20typeclass.20inference.20issue

lemma hom_trivialization_at_apply (x₀ : B) (x : LE₁E₂) :
  trivialization_at (F₁ →L[𝕜] F₂) (bundle.continuous_linear_map σ F₁ E₁ F₂ E₂) x₀ x =
  ⟨x.1, in_coordinates' F₁ F₂ E₁ E₂ x₀ x.1 x₀ x.1 x.2⟩ :=
rfl

@[simp, mfld_simps]
lemma hom_trivialization_at_source (x₀ : B) :
  (trivialization_at (F₁ →L[𝕜] F₂) (bundle.continuous_linear_map σ F₁ E₁ F₂ E₂) x₀).source =
  π FE₁E₂ ⁻¹' ((trivialization_at F₁ E₁ x₀).base_set ∩ (trivialization_at F₂ E₂ x₀).base_set) :=
rfl

@[simp, mfld_simps]
lemma hom_trivialization_at_target (x₀ : B) :
  (trivialization_at (F₁ →L[𝕜] F₂) (bundle.continuous_linear_map σ F₁ E₁ F₂ E₂) x₀).target =
  ((trivialization_at F₁ E₁ x₀).base_set ∩ (trivialization_at F₂ E₂ x₀).base_set) ×ˢ set.univ :=
rfl

lemma hom_chart (x₀ x : LE₁E₂) :
  chart_at (model_prod HB (F₁ →L[𝕜] F₂)) x₀ x =
  (chart_at HB x₀.1 x.1, in_coordinates' F₁ F₂ E₁ E₂ x₀.1 x.1 x₀.1 x.1 x.2) :=
by simp_rw [fiber_bundle.charted_space_chart_at, trans_apply, local_homeomorph.prod_apply,
  trivialization.coe_coe, local_homeomorph.refl_apply, function.id_def, hom_trivialization_at_apply]

lemma smooth_at_hom_bundle {f : M → LE₁E₂} {x₀ : M} :
  smooth_at IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂)) f x₀ ↔
  smooth_at IM IB (λ x, (f x).1) x₀ ∧
  smooth_at IM 𝓘(𝕜, F₁ →L[𝕜] F₂)
  (λ x, in_coordinates' F₁ F₂ E₁ E₂ (f x₀).1 (f x).1 (f x₀).1 (f x).1 (f x).2) x₀ :=
by { simp_rw [smooth_at, cont_mdiff_at_total_space], refl }

variables [smooth_manifold_with_corners IB B]
  [smooth_vector_bundle F₁ E₁ IB] [smooth_vector_bundle F₂ E₂ IB]

variables [∀ x, has_continuous_add (E₂ x)] [∀ x, has_continuous_smul 𝕜 (E₂ x)]

instance bundle.continuous_linear_map.vector_prebundle.is_smooth : PLE₁E₂ .is_smooth IB :=
{ exists_smooth_coord_change := by {
    rintro _ ⟨e₁, e₂, he₁, he₂, rfl⟩ _ ⟨e₁', e₂', he₁', he₂', rfl⟩,
    resetI,
    refine ⟨continuous_linear_map_coord_change σ e₁ e₁' e₂ e₂',
    smooth_on_continuous_linear_map_coord_change IB,
    continuous_linear_map_coord_change_apply σ e₁ e₁' e₂ e₂'⟩ } }

instance smooth_vector_bundle.continuous_linear_map :
  smooth_vector_bundle (F₁ →L[𝕜] F₂) FE₁E₂ IB :=
PLE₁E₂ .to_smooth_vector_bundle IB

end general

namespace vector_bundle_core

variables {ι₁ ι₂ : Type*} (Z₁ : vector_bundle_core 𝕜 B F₁ ι₁) (Z₂ : vector_bundle_core 𝕜 B F₂ ι₂)

local notation `FZ₁Z₂` := bundle.continuous_linear_map σ F₁ Z₁.fiber F₂ Z₂.fiber
local notation `LZ₁Z₂` := bundle.total_space FZ₁Z₂

def foo1 (b : B) :
  topological_space (continuous_linear_map σ F₁ Z₁.fiber F₂ Z₂.fiber b) :=
by apply_instance
local attribute [instance] foo1 -- probably needed because of the type-class pi bug

lemma hom_trivialization_at (x₀ x : LZ₁Z₂)
  (h1x : x.proj ∈ Z₁.base_set (Z₁.index_at x₀.proj))
  (h2x : x.proj ∈ Z₂.base_set (Z₂.index_at x₀.proj)) :
  (trivialization_at (F₁ →L[𝕜] F₂) FZ₁Z₂ x₀.proj x).2 =
  in_coordinates_core' Z₁ Z₂ x₀.proj x.proj x₀.proj x.proj x.2 :=
begin
  rw [continuous_linear_map_trivialization_at, trivialization.continuous_linear_map_apply,
    ← in_coordinates_core'_eq],
  exacts [rfl, h1x, h2x]
end

-- todo: refactor
lemma smooth_at_hom_bundle_core {f : M → LZ₁Z₂} {x₀ : M} :
  smooth_at IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂)) f x₀ ↔
  smooth_at IM IB (λ x, (f x).1) x₀ ∧
  smooth_at IM 𝓘(𝕜, F₁ →L[𝕜] F₂)
  (λ x, in_coordinates_core' Z₁ Z₂ (f x₀).1 (f x).1 (f x₀).1 (f x).1 (f x).2) x₀  :=
begin
  simp_rw [smooth_at, cont_mdiff_at_total_space, and.congr_right_iff],
  intro hf,
  refine filter.eventually_eq.cont_mdiff_at_iff _,
  have h1 := hf.continuous_at.preimage_mem_nhds ((trivialization.open_base_set _).mem_nhds
    (mem_base_set_trivialization_at F₁ Z₁.fiber (f x₀).proj)),
  have h2 := hf.continuous_at.preimage_mem_nhds ((trivialization.open_base_set _).mem_nhds
    (mem_base_set_trivialization_at F₂ Z₂.fiber (f x₀).proj)),
  filter_upwards [h1, h2],
  intros x h1x h2x,
  exact hom_trivialization_at Z₁ Z₂ (f x₀) (f x) h1x h2x
end

end vector_bundle_core

end hom