/-
Copyright (c) 2020 Joseph Myers. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joseph Myers, Yury Kudryashov
-/
import analysis.normed_space.basic
import linear_algebra.affine_space.midpoint
import topology.instances.real_vector_space

/-!
# Torsors of additive normed group actions.

This file defines torsors of additive normed group actions, with a
metric space structure.  The motivating case is Euclidean affine
spaces.

-/

noncomputable theory
open_locale nnreal topological_space
open filter

/-- A `semi_normed_add_torsor V P` is a torsor of an additive seminormed group
action by a `semi_normed_group V` on points `P`. We bundle the pseudometric space
structure and require the distance to be the same as results from the
norm (which in fact implies the distance yields a pseudometric space, but
bundling just the distance and using an instance for the pseudometric space
results in type class problems). -/
class semi_normed_add_torsor (V : out_param $ Type*) (P : Type*)
  [out_param $ semi_normed_group V] [pseudo_metric_space P]
  extends add_torsor V P :=
(dist_eq_norm' : ∀ (x y : P), dist x y = ∥(x -ᵥ y : V)∥)

/-- A `normed_add_torsor V P` is a torsor of an additive normed group
action by a `normed_group V` on points `P`. We bundle the metric space
structure and require the distance to be the same as results from the
norm (which in fact implies the distance yields a metric space, but
bundling just the distance and using an instance for the metric space
results in type class problems). -/
class normed_add_torsor (V : out_param $ Type*) (P : Type*)
  [out_param $ normed_group V] [metric_space P]
  extends add_torsor V P :=
(dist_eq_norm' : ∀ (x y : P), dist x y = ∥(x -ᵥ y : V)∥)

/-- A `normed_add_torsor` is a `semi_normed_add_torsor`. -/
@[priority 100]
instance normed_add_torsor.to_semi_normed_add_torsor {V P : Type*} [normed_group V] [metric_space P]
  [β : normed_add_torsor V P] : semi_normed_add_torsor V P := { ..β }

variables {α V P : Type*} [semi_normed_group V] [pseudo_metric_space P] [semi_normed_add_torsor V P]
variables {W Q : Type*} [normed_group W] [metric_space Q] [normed_add_torsor W Q]

/-- A `semi_normed_group` is a `semi_normed_add_torsor` over itself. -/
@[priority 100]
instance semi_normed_group.normed_add_torsor : semi_normed_add_torsor V V :=
{ dist_eq_norm' := dist_eq_norm }

/-- A `normed_group` is a `normed_add_torsor` over itself. -/
@[priority 100]
instance normed_group.normed_add_torsor : normed_add_torsor W W :=
{ dist_eq_norm' := dist_eq_norm }

include V

section

variables (V W)

/-- The distance equals the norm of subtracting two points. In this
lemma, it is necessary to have `V` as an explicit argument; otherwise
`rw dist_eq_norm_vsub` sometimes doesn't work. -/
lemma dist_eq_norm_vsub (x y : P) :
  dist x y = ∥(x -ᵥ y)∥ :=
semi_normed_add_torsor.dist_eq_norm' x y

end

@[simp] lemma dist_vadd_cancel_left (v : V) (x y : P) :
  dist (v +ᵥ x) (v +ᵥ y) = dist x y :=
by rw [dist_eq_norm_vsub V, dist_eq_norm_vsub V, vadd_vsub_vadd_cancel_left]

@[simp] lemma dist_vadd_cancel_right (v₁ v₂ : V) (x : P) :
  dist (v₁ +ᵥ x) (v₂ +ᵥ x) = dist v₁ v₂ :=
by rw [dist_eq_norm_vsub V, dist_eq_norm, vadd_vsub_vadd_cancel_right]

@[simp] lemma dist_vadd_left (v : V) (x : P) : dist (v +ᵥ x) x = ∥v∥ :=
by simp [dist_eq_norm_vsub V _ x]

@[simp] lemma dist_vadd_right (v : V) (x : P) : dist x (v +ᵥ x) = ∥v∥ :=
by rw [dist_comm, dist_vadd_left]

@[simp] lemma dist_vsub_cancel_left (x y z : P) : dist (x -ᵥ y) (x -ᵥ z) = dist y z :=
by rw [dist_eq_norm, vsub_sub_vsub_cancel_left, dist_comm, dist_eq_norm_vsub V]

@[simp] lemma dist_vsub_cancel_right (x y z : P) : dist (x -ᵥ z) (y -ᵥ z) = dist x y :=
by rw [dist_eq_norm, vsub_sub_vsub_cancel_right, dist_eq_norm_vsub V]

lemma dist_vadd_vadd_le (v v' : V) (p p' : P) :
  dist (v +ᵥ p) (v' +ᵥ p') ≤ dist v v' + dist p p' :=
by simpa using dist_triangle (v +ᵥ p) (v' +ᵥ p) (v' +ᵥ p')

lemma dist_vsub_vsub_le (p₁ p₂ p₃ p₄ : P) :
  dist (p₁ -ᵥ p₂) (p₃ -ᵥ p₄) ≤ dist p₁ p₃ + dist p₂ p₄ :=
by { rw [dist_eq_norm, vsub_sub_vsub_comm, dist_eq_norm_vsub V, dist_eq_norm_vsub V],
 exact norm_sub_le _ _ }

lemma nndist_vadd_vadd_le (v v' : V) (p p' : P) :
  nndist (v +ᵥ p) (v' +ᵥ p') ≤ nndist v v' + nndist p p' :=
by simp only [← nnreal.coe_le_coe, nnreal.coe_add, ← dist_nndist, dist_vadd_vadd_le]

lemma nndist_vsub_vsub_le (p₁ p₂ p₃ p₄ : P) :
  nndist (p₁ -ᵥ p₂) (p₃ -ᵥ p₄) ≤ nndist p₁ p₃ + nndist p₂ p₄ :=
by simp only [← nnreal.coe_le_coe, nnreal.coe_add, ← dist_nndist, dist_vsub_vsub_le]

lemma edist_vadd_vadd_le (v v' : V) (p p' : P) :
  edist (v +ᵥ p) (v' +ᵥ p') ≤ edist v v' + edist p p' :=
by { simp only [edist_nndist], apply_mod_cast nndist_vadd_vadd_le }

lemma edist_vsub_vsub_le (p₁ p₂ p₃ p₄ : P) :
  edist (p₁ -ᵥ p₂) (p₃ -ᵥ p₄) ≤ edist p₁ p₃ + edist p₂ p₄ :=
by { simp only [edist_nndist], apply_mod_cast nndist_vsub_vsub_le }

omit V

/-- The pseudodistance defines a pseudometric space structure on the torsor. This
is not an instance because it depends on `V` to define a `metric_space
P`. -/
def pseudo_metric_space_of_normed_group_of_add_torsor (V P : Type*) [semi_normed_group V]
  [add_torsor V P] : pseudo_metric_space P :=
{ dist := λ x y, ∥(x -ᵥ y : V)∥,
  dist_self := λ x, by simp,
  dist_comm := λ x y, by simp only [←neg_vsub_eq_vsub_rev y x, norm_neg],
  dist_triangle := begin
    intros x y z,
    change ∥x -ᵥ z∥ ≤ ∥x -ᵥ y∥ + ∥y -ᵥ z∥,
    rw ←vsub_add_vsub_cancel,
    apply norm_add_le
  end }

/-- The distance defines a metric space structure on the torsor. This
is not an instance because it depends on `V` to define a `metric_space
P`. -/
def metric_space_of_normed_group_of_add_torsor (V P : Type*) [normed_group V] [add_torsor V P] :
  metric_space P :=
{ dist := λ x y, ∥(x -ᵥ y : V)∥,
  dist_self := λ x, by simp,
  eq_of_dist_eq_zero := λ x y h, by simpa using h,
  dist_comm := λ x y, by simp only [←neg_vsub_eq_vsub_rev y x, norm_neg],
  dist_triangle := begin
    intros x y z,
    change ∥x -ᵥ z∥ ≤ ∥x -ᵥ y∥ + ∥y -ᵥ z∥,
    rw ←vsub_add_vsub_cancel,
    apply norm_add_le
  end }

include V

lemma lipschitz_with.vadd [pseudo_emetric_space α] {f : α → V} {g : α → P} {Kf Kg : ℝ≥0}
  (hf : lipschitz_with Kf f) (hg : lipschitz_with Kg g) :
  lipschitz_with (Kf + Kg) (f +ᵥ g) :=
λ x y,
calc edist (f x +ᵥ g x) (f y +ᵥ g y) ≤ edist (f x) (f y) + edist (g x) (g y) :
  edist_vadd_vadd_le _ _ _ _
... ≤ Kf * edist x y + Kg * edist x y :
  add_le_add (hf x y) (hg x y)
... = (Kf + Kg) * edist x y :
  (add_mul _ _ _).symm

lemma lipschitz_with.vsub [pseudo_emetric_space α] {f g : α → P} {Kf Kg : ℝ≥0}
  (hf : lipschitz_with Kf f) (hg : lipschitz_with Kg g) :
  lipschitz_with (Kf + Kg) (f -ᵥ g) :=
λ x y,
calc edist (f x -ᵥ g x) (f y -ᵥ g y) ≤ edist (f x) (f y) + edist (g x) (g y) :
  edist_vsub_vsub_le _ _ _ _
... ≤ Kf * edist x y + Kg * edist x y :
  add_le_add (hf x y) (hg x y)
... = (Kf + Kg) * edist x y :
  (add_mul _ _ _).symm

lemma uniform_continuous_vadd : uniform_continuous (λ x : V × P, x.1 +ᵥ x.2) :=
(lipschitz_with.prod_fst.vadd lipschitz_with.prod_snd).uniform_continuous

lemma uniform_continuous_vsub : uniform_continuous (λ x : P × P, x.1 -ᵥ x.2) :=
(lipschitz_with.prod_fst.vsub lipschitz_with.prod_snd).uniform_continuous

@[priority 100] instance semi_normed_add_torsor.has_continuous_vadd :
  has_continuous_vadd V P :=
{ continuous_vadd := uniform_continuous_vadd.continuous }

lemma continuous_vsub : continuous (λ x : P × P, x.1 -ᵥ x.2) :=
uniform_continuous_vsub.continuous

lemma filter.tendsto.vsub {l : filter α} {f g : α → P} {x y : P}
  (hf : tendsto f l (𝓝 x)) (hg : tendsto g l (𝓝 y)) :
  tendsto (f -ᵥ g) l (𝓝 (x -ᵥ y)) :=
(continuous_vsub.tendsto (x, y)).comp (hf.prod_mk_nhds hg)

section

variables [topological_space α]

lemma continuous.vsub {f g : α → P} (hf : continuous f) (hg : continuous g) :
  continuous (f -ᵥ g) :=
continuous_vsub.comp (hf.prod_mk hg : _)

lemma continuous_at.vsub {f g : α → P}  {x : α} (hf : continuous_at f x) (hg : continuous_at g x) :
  continuous_at (f -ᵥ g) x :=
hf.vsub hg

lemma continuous_within_at.vsub {f g : α → P} {x : α} {s : set α}
  (hf : continuous_within_at f s x) (hg : continuous_within_at g s x) :
  continuous_within_at (f -ᵥ g) s x :=
hf.vsub hg

end

section

variables {R : Type*} [ring R] [topological_space R] [module R V] [has_continuous_smul R V]

lemma filter.tendsto.line_map {l : filter α} {f₁ f₂ : α → P} {g : α → R} {p₁ p₂ : P} {c : R}
  (h₁ : tendsto f₁ l (𝓝 p₁)) (h₂ : tendsto f₂ l (𝓝 p₂)) (hg : tendsto g l (𝓝 c)) :
  tendsto (λ x, affine_map.line_map (f₁ x) (f₂ x) (g x)) l (𝓝 $ affine_map.line_map p₁ p₂ c) :=
(hg.smul (h₂.vsub h₁)).vadd h₁

lemma filter.tendsto.midpoint [invertible (2:R)] {l : filter α} {f₁ f₂ : α → P} {p₁ p₂ : P}
  (h₁ : tendsto f₁ l (𝓝 p₁)) (h₂ : tendsto f₂ l (𝓝 p₂)) :
  tendsto (λ x, midpoint R (f₁ x) (f₂ x)) l (𝓝 $ midpoint R p₁ p₂) :=
h₁.line_map h₂ tendsto_const_nhds

end

section normed_space

variables {𝕜 : Type*} [normed_field 𝕜] [semi_normed_space 𝕜 V]

open affine_map

@[simp] lemma dist_center_homothety (p₁ p₂ : P) (c : 𝕜) :
  dist p₁ (homothety p₁ c p₂) = ∥c∥ * dist p₁ p₂ :=
by simp [homothety_def, norm_smul, ← dist_eq_norm_vsub, dist_comm]

@[simp] lemma dist_homothety_center (p₁ p₂ : P) (c : 𝕜) :
  dist (homothety p₁ c p₂) p₁ = ∥c∥ * dist p₁ p₂ :=
by rw [dist_comm, dist_center_homothety]

@[simp] lemma dist_homothety_self (p₁ p₂ : P) (c : 𝕜) :
  dist (homothety p₁ c p₂) p₂ = ∥1 - c∥ * dist p₁ p₂ :=
by rw [homothety_eq_line_map, ← line_map_apply_one_sub, ← homothety_eq_line_map,
  dist_homothety_center, dist_comm]

@[simp] lemma dist_self_homothety (p₁ p₂ : P) (c : 𝕜) :
  dist p₂ (homothety p₁ c p₂) = ∥1 - c∥ * dist p₁ p₂ :=
by rw [dist_comm, dist_homothety_self]

variables [invertible (2:𝕜)]

@[simp] lemma dist_left_midpoint (p₁ p₂ : P) :
  dist p₁ (midpoint 𝕜 p₁ p₂) = ∥(2:𝕜)∥⁻¹ * dist p₁ p₂ :=
by rw [midpoint, ← homothety_eq_line_map, dist_center_homothety, inv_of_eq_inv,
  ← normed_field.norm_inv]

@[simp] lemma dist_midpoint_left (p₁ p₂ : P) :
  dist (midpoint 𝕜 p₁ p₂) p₁ = ∥(2:𝕜)∥⁻¹ * dist p₁ p₂ :=
by rw [dist_comm, dist_left_midpoint]

@[simp] lemma dist_midpoint_right (p₁ p₂ : P) :
  dist (midpoint 𝕜 p₁ p₂) p₂ = ∥(2:𝕜)∥⁻¹ * dist p₁ p₂ :=
by rw [midpoint_comm, dist_midpoint_left, dist_comm]

@[simp] lemma dist_right_midpoint (p₁ p₂ : P) :
  dist p₂ (midpoint 𝕜 p₁ p₂) = ∥(2:𝕜)∥⁻¹ * dist p₁ p₂ :=
by rw [dist_comm, dist_midpoint_right]

lemma dist_midpoint_midpoint_le' (p₁ p₂ p₃ p₄ : P) :
  dist (midpoint 𝕜 p₁ p₂) (midpoint 𝕜 p₃ p₄) ≤ (dist p₁ p₃ + dist p₂ p₄) / ∥(2 : 𝕜)∥ :=
begin
  rw [dist_eq_norm_vsub V, dist_eq_norm_vsub V, dist_eq_norm_vsub V, midpoint_vsub_midpoint];
    try { apply_instance },
  rw [midpoint_eq_smul_add, norm_smul, inv_of_eq_inv, normed_field.norm_inv, ← div_eq_inv_mul],
  exact div_le_div_of_le_of_nonneg (norm_add_le _ _) (norm_nonneg _),
end

end normed_space

variables [semi_normed_space ℝ V] [normed_space ℝ W]

lemma dist_midpoint_midpoint_le (p₁ p₂ p₃ p₄ : V) :
  dist (midpoint ℝ p₁ p₂) (midpoint ℝ p₃ p₄) ≤ (dist p₁ p₃ + dist p₂ p₄) / 2 :=
by simpa using dist_midpoint_midpoint_le' p₁ p₂ p₃ p₄

include W

/-- A continuous map between two normed affine spaces is an affine map provided that
it sends midpoints to midpoints. -/
def affine_map.of_map_midpoint (f : P → Q)
  (h : ∀ x y, f (midpoint ℝ x y) = midpoint ℝ (f x) (f y))
  (hfc : continuous f) :
  P →ᵃ[ℝ] Q :=
affine_map.mk' f
  ↑((add_monoid_hom.of_map_midpoint ℝ ℝ
    ((affine_equiv.vadd_const ℝ (f $ classical.arbitrary P)).symm ∘ f ∘
      (affine_equiv.vadd_const ℝ (classical.arbitrary P))) (by simp)
      (λ x y, by simp [h])).to_real_linear_map $ by apply_rules [continuous.vadd, continuous.vsub,
        continuous_const, hfc.comp, continuous_id])
  (classical.arbitrary P)
  (λ p, by simp)


/-
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 measure_theory.measurable_space

import measure_theory.measure_space
import formal_ml.set
import formal_ml.finset
import formal_ml.classical

lemma set_Prop_le_def {α:Type*}
  (M M2:set α → Prop):
  M ≤ M2 ↔
  (∀ X:set α, M X → M2 X)
   :=
begin
  refl,
end

lemma finset_union_measurable {α:Type*} {T:finset α} {β:Type*} [measurable_space β] {U:α → set β}:
  (∀ t∈ T, measurable_set (U t)) →
  measurable_set (⋃ x ∈ T, U x) :=
begin
  intros a,
  have A1:(set.sUnion (set.image U ({a|a∈ T}:set α))) = (⋃ x ∈ T, U x),
  {
    simp,
  },
  rw ← A1,
  apply measurable_set.sUnion,
  {
    apply set.countable.image,
    apply set.finite.countable,
    apply finite_finset,
  },
  {
    intros,
    simp at H,
    cases H with x H,
    cases H with A2 A3,
    subst t,
    apply a,
    exact A2,
  }
end

lemma finset_inter_measurable {α:Type*} {T:finset α} {β:Type*} [measurable_space β] {U:α → set β}:
  (∀ t∈ T, measurable_set (U t)) →
  measurable_set (⋂ x ∈ T, U x) :=
begin
  intros a,
  have A1:(set.sInter (set.image U ({a|a∈ T}:set α))) = (⋂ x ∈ T, U x),
  {
    simp,
  },
  rw ← A1,
  apply measurable_set.sInter,
  {
    apply set.countable.image,
    apply set.finite.countable,
    apply finite_finset,
  },
  {
    intros,
    simp at H,
    cases H with x H,
    cases H with A2 A3,
    subst t,
    apply a,
    exact A2,
  }
end

lemma measurable_space_le_def {α:Type*}
  (M:measurable_space α) (M2:measurable_space α):
  M.measurable_set' ≤  M2.measurable_set'
  ↔  M ≤ M2 :=
begin
  refl,
end

lemma measurable_space_le_def2 {α:Type*}
  (M:measurable_space α) (M2:measurable_space α):
  (∀ X:set α, M.measurable_set' X → M2.measurable_set' X) ↔
   M ≤ M2 :=
begin
  intros,
  apply iff.trans,
  {
    apply set_Prop_le_def,
  },
  {
    apply measurable_space_le_def,
  }
end

-- Delete?
lemma measurable_space_le_intro {α:Type*}
  (M:measurable_space α) (M2:measurable_space α):
  (∀ X:set α, M.measurable_set' X → M2.measurable_set' X) →
   M ≤ M2 :=
begin
  intros a,
  have A1:M.measurable_set' ≤  M2.measurable_set'
  ↔  M ≤ M2,
  {
    apply measurable_space_le_def,
  },
  apply A1.mp,
  have A2:M.measurable_set' ≤ M2.measurable_set' ↔
  (∀ X:set α, M.measurable_set' X → M2.measurable_set' X),
  {
    apply set_Prop_le_def,
  },
  apply A2.mpr,
  apply a,
end

lemma measurable_def {α β:Type*}
  [M1:measurable_space α] [M2:measurable_space β] (f:α → β):
  (∀ B:(set β), (measurable_set B) → measurable_set (f ⁻¹' B))
  ↔ (measurable f) :=
begin
  unfold measurable,
end

lemma measurable_intro {α β:Type*}
  [measurable_space α] [measurable_space β] (f:α → β):
  (∀ B:(set β), measurable_set B → measurable_set (f ⁻¹' B))
  → (measurable f) :=
begin
  apply (measurable_def _).mp,
end

lemma measurable_elim {α β:Type*}
  [measurable_space α] [measurable_space β] (f:α → β) (B:set β):
  (measurable f)→ (measurable_set B) → (measurable_set (f ⁻¹' B)) :=
begin
  intros a a_1,
  apply (measurable_def _).mpr,
  apply a,
  apply a_1,
end

lemma measurable_fun_product_measurableh {α β:Type*}
  [M1:measurable_space α] [M2:measurable_space β]:
  (@prod.measurable_space α β M1 M2) = M1.comap prod.fst ⊔ M2.comap prod.snd :=
begin
  refl
end

lemma comap_elim {α β:Type*} [M2:measurable_space β] (f:α → β) (B:set β):
  (measurable_set B) →
  (M2.comap f).measurable_set'  (set.preimage f B) :=
begin
  intros a,
  unfold measurable_space.comap,
  simp,
  apply exists.intro B,
  split,
  apply a,
  refl
end

lemma measurable_comap {α β:Type*} [M1:measurable_space α] [M2:measurable_space β] (f:α → β):
  (M2.comap f) ≤ M1 → measurable f :=
begin
  intros a,
  apply measurable_intro,
  intros B a_1,
  have A1:(M2.comap f).measurable_set'  (set.preimage f B),
  {
    apply comap_elim,
    apply a_1,
  },
  rw ← measurable_space_le_def2 at a,
  apply a,
  apply A1,
end

lemma fst_measurable {α β:Type*}
  [M1:measurable_space α] [M2:measurable_space β]:measurable (λ x:(α × β), x.fst) :=
begin
  apply measurable_comap,
  have A1:M1.comap prod.fst ≤ (@prod.measurable_space α β M1 M2),
  {
    rw measurable_fun_product_measurableh,
    apply complete_lattice.le_sup_left (M1.comap prod.fst) (M2.comap prod.snd),
  },
  apply A1,
end

lemma snd_measurable {α β:Type*}
  [M1:measurable_space α] [M2:measurable_space β]:measurable (λ x:(α × β), x.snd) :=
begin
  apply measurable_comap,
  have A1:M2.comap prod.snd ≤ (@prod.measurable_space α β M1 M2),
  {
    rw measurable_fun_product_measurableh,
    apply complete_lattice.le_sup_right (M1.comap prod.fst) (M2.comap prod.snd),
  },
  apply A1,
end

lemma comap_def {α β:Type*} {B:set (set β)}
  (f:α → β):
  @measurable_space.comap α β f (measurable_space.generate_from B)
  = (measurable_space.generate_from (set.image (set.preimage f) B)) :=
begin
  apply measurable_space.comap_generate_from,
end

lemma comap_fst_def {α β:Type*} {Bα:set (set α)}:
  (measurable_space.generate_from Bα).comap (@prod.fst α β) =
  measurable_space.generate_from {U:set (α × β)|∃ A∈ Bα, U = set.prod A set.univ} :=
begin
  rw measurable_space.comap_generate_from,
  rw set.preimage_fst_def,
end

lemma comap_snd_def {α β:Type*} {Bβ:set (set β)}:
  (measurable_space.generate_from Bβ).comap (@prod.snd α β) =
  measurable_space.generate_from {U:set (α × β)|∃ B∈ Bβ, U = set.prod set.univ B} :=
begin
  rw measurable_space.comap_generate_from,
  rw set.preimage_snd_def,
end

lemma measurable_space_sup_def {α:Type*} {B C:set (set α)}:
  (measurable_space.generate_from B) ⊔ (measurable_space.generate_from C) =
  (measurable_space.generate_from (B ∪ C)) :=
begin
  apply measurable_space.generate_from_sup_generate_from,
end

lemma prod_measurable_space_def {α β:Type*} {Bα:set (set α)}
  {Bβ:set (set β)}:
  (@prod.measurable_space α β (measurable_space.generate_from Bα)
  (measurable_space.generate_from Bβ)) =
  @measurable_space.generate_from (α × β) (
    {U:set (α × β)|∃ A∈ Bα, U = set.prod A set.univ} ∪
    {U:set (α × β)|∃ B∈ Bβ, U = set.prod set.univ B})
   :=
begin
  rw measurable_fun_product_measurableh,
  rw comap_fst_def,
  rw comap_snd_def,
  rw measurable_space_sup_def,
end

lemma set.sUnion_eq_univ_elim {α:Type*} {S:set (set α)} (a:α):
  (set.sUnion S = set.univ) → (∃ T∈S, a∈ T) :=
begin
  intro A1,
  have A2:a∈ set.univ := set.mem_univ a,
  rw ← A1 at A2,
  simp at A2,
  cases A2 with T A2,
  apply exists.intro T,
  apply exists.intro A2.left,
  apply A2.right,
end

lemma prod_measurable_space_le {α β:Type*} {Bα:set (set α)}
  {Bβ:set (set β)}:
  @measurable_space.generate_from (α × β)
    {U:set (α × β)|∃ A∈ Bα, ∃ B∈Bβ,  U = set.prod A B} ≤
  (@prod.measurable_space α β (measurable_space.generate_from Bα)
  (measurable_space.generate_from Bβ))
   :=
begin
  rw prod_measurable_space_def,
  apply measurable_space.generate_from_le, intros X A5,
  simp at A5,
  cases A5 with A A5,
  cases A5 with A5 A6,
  cases A6 with B A6,
  cases A6 with A6 A7,
  have A8:(set.prod A (@set.univ β)) ∩
          (set.prod (@set.univ α) B) = set.prod A B,
  {
    ext p,split;intros A3A;{
      simp at A3A,
      simp,
       --cases p,
      apply A3A,
    },
  },
  rw ← A8 at A7,
  rw A7,
  apply measurable_set.inter,
  {
    apply measurable_space.measurable_set_generate_from,
    apply set.mem_union_left,
    simp,
    apply exists.intro A,
    split,
    apply A5,
    refl,
  },
  {
    apply measurable_space.measurable_set_generate_from,
    apply set.mem_union_right,
    simp,
    apply exists.intro B,
    split,
    apply A6,
    refl,
  },
end

lemma prod_measurable_space_def2 {α β:Type*} {Bα:set (set α)}
  {Bβ:set (set β)} {Cα:set (set α)} {Cβ:set (set β)}:
  (set.countable Cα) →
  (set.countable Cβ) →
  (Cα ⊆ Bα) →
  (Cβ ⊆ Bβ) →
  (set.sUnion Cα = set.univ) →
  (set.sUnion Cβ = set.univ) →
  (@prod.measurable_space α β (measurable_space.generate_from Bα)
  (measurable_space.generate_from Bβ)) =
  @measurable_space.generate_from (α × β)
    {U:set (α × β)|∃ A∈ Bα, ∃ B∈Bβ,  U = set.prod A B}
   :=
begin
  intros A1 A2 A3 A4 AX1 AX2,
  --rw prod_measurable_space_def,
  apply le_antisymm,
  {
    rw prod_measurable_space_def,
    apply measurable_space.generate_from_le,
    intros X A5,
    simp at A5,
    cases A5,
    {
       cases A5 with A A5,
       cases A5 with A5 A6,
       have A7:X = set.sUnion (set.image (set.prod A) Cβ),
       {
         rw A6,
         ext a,split;intro A7A;simp;simp at A7A,
         {
           have A7B := set.sUnion_eq_univ_elim a.snd AX2,
           cases A7B with i A7B,
           cases A7B with A7B A7C,
           apply exists.intro i,
           apply and.intro A7B (and.intro A7A A7C),
         },
         {
           cases A7A with i A7A,
           apply A7A.right.left,
         },
       },
       rw A7,
       --apply measurable_space.measurable_set_generate_from,
       apply measurable_set.sUnion,
       apply set.countable.image,
       apply A2,
       intro U,
       intro A8,
       simp at A8,
       cases A8 with B A8,
       cases A8 with A8 A9,
       subst U,
       apply measurable_space.measurable_set_generate_from,
       simp,
       apply exists.intro A,
       split,
       apply A5,
       apply exists.intro B,
       split,
       rw set.subset_def at A4,
       apply A4,
       apply A8,
       refl,
    },
    {
       cases A5 with B A5,
       cases A5 with A5 A6,
       have A7:X = set.sUnion (set.image (λ x, set.prod x B)  Cα),
       {
         rw A6,
         ext a,split;intro A7A;simp;simp at A7A,
         {
           have A7B := set.sUnion_eq_univ_elim a.fst AX1,
           cases A7B with i A7B,
           cases A7B with A7B A7C,
           apply exists.intro i,
           apply and.intro A7B (and.intro A7C A7A),
         },
         {
           cases A7A with i A7A,
           apply A7A.right.right,
         },
       },
       rw A7,
       --apply measurable_space.measurable_set_generate_from,
       apply measurable_set.sUnion,
       apply set.countable.image,
       apply A1,
       intro U,
       intro A8,
       simp at A8,
       cases A8 with A A8,
       cases A8 with A8 A9,
       subst U,
       apply measurable_space.measurable_set_generate_from,
       simp,
       apply exists.intro A,
       split,
       rw set.subset_def at A3,
       apply A3,
       apply A8,
       apply exists.intro B,
       split,
       apply A5,
       refl,
    },
  },
  {
    apply prod_measurable_space_le,
  }
end

lemma preimage_compl {α β:Type*} (f:α → β) (S:set β):
  (f ⁻¹' Sᶜ) = ((f ⁻¹' S)ᶜ) :=
begin
  ext,
  split;intros a,
  {
    intro a_1,
    unfold set.preimage at a,
    simp at a,
    apply a,
    apply a_1,
  },
  {
    unfold set.preimage,
    simp,
    intro a_1,
    apply a,
    apply a_1,
  }
end

lemma preimage_Union {α β:Type*} (f:α → β) (g:ℕ → set β):
   (f ⁻¹' ⋃ (i : ℕ), g i)=(⋃ (i : ℕ), f ⁻¹' (g i)) :=
begin
  ext,
  split;intros a,
  {
    cases a with B a,
    cases a with H a,
    cases H with y H,
    split,
    simp,
    split,
    apply exists.intro y,
    {
      simp at H,
    },
    {
      simp at H,
      subst B,
      apply a,
    }
  },
  {
    cases a with A a,
    cases a with A1 A2,
    cases A1 with i A3,
    simp at A3,
    subst A,
    split,
    simp,
    split,
    {
      apply exists.intro i,
      refl,
    },
    {
      apply A2,
    },
  }
end

lemma generate_from_measurable {α β:Type*} [M:measurable_space α] [M2:measurable_space β]
   (X:set (set β)) (f:α → β):
   (measurable_space.generate_from X = M2)→
   (∀ B∈ X, measurable_set (set.preimage f B))→
   (measurable f) :=
begin
  intros a a_1,
  apply measurable_intro,
  intros B a_2,
  have A1:@measurable_set β (measurable_space.generate_from X) B,
  {
    rw a,
    apply a_2,
  },
  clear a_2, -- Important for induction later.
  have A2:measurable_space.generate_measurable X B,
  {
    apply A1,
  },
  induction A2,
  {
    apply a_1,
    apply A2_H,
  },
  {
    simp,
  },
  { -- ⊢ measurable_set (f ⁻¹' -A2_s)
    rw preimage_compl,
    apply measurable_space.measurable_set_compl,
    apply A2_ih,
    {
      apply (measurable_set.compl_iff).mp,
      apply A1,
    },
  },
  {
    rw preimage_Union,
    apply measurable_space.measurable_set_Union,
    intros i,
    apply A2_ih,
    {
      apply A2_ᾰ,
    }
  }
end

lemma generate_from_self {α:Type*}
  (M:measurable_space α):
  M = measurable_space.generate_from {s : set α|measurable_space.measurable_set' M s} :=
begin
  ext,
  split;intros a,
  {
    apply measurable_space.generate_measurable.basic,
    apply a,
  },
  {
    induction a,
    {
      apply a_H,
    },
    {
      apply measurable_space.measurable_set_empty,
    },
    {
      apply measurable_space.measurable_set_compl,
      apply a_ih,
    },
    {
      apply measurable_space.measurable_set_Union,
      apply a_ih,
    },
  }
end

lemma measurable_fun_comap_def {α β:Type*}
  [M2:measurable_space β]  (f:α → β):
  measurable_space.comap f M2 = measurable_space.generate_from
  {s : set α|∃ (s' : set β), measurable_space.measurable_set' M2 s' ∧ f ⁻¹' s' = s} :=
begin
  unfold measurable_space.comap,
  apply generate_from_self,
end

lemma measurable_fun_product_measurable {α β γ:Type*}
  [M1:measurable_space α] [M2:measurable_space β] [M3:measurable_space γ]
  (X: α →  β) (Y: α → γ):
  measurable X →
  measurable Y →
  measurable (λ a:α, prod.mk (X a) (Y a)) :=
begin
  intros B1 B2,
  have A1:@measurable _ _ _ (@prod.measurable_space β γ M2 M3) (λ a:α, prod.mk (X a) (Y a)),
  {
    have A1A:(@prod.measurable_space β  γ  M2 M3)=measurable_space.generate_from (
      {s : set (β × γ) | ∃ (s' : set β), measurable_space.measurable_set' M2 s' ∧ prod.fst ⁻¹' s' = s} ∪
      {s : set (β  × γ) | ∃ (s' : set γ), measurable_space.measurable_set' M3 s' ∧ prod.snd ⁻¹' s' = s}),
    {
      rw measurable_fun_product_measurableh,
      rw measurable_fun_comap_def,
      rw measurable_fun_comap_def,
      rw measurable_space.generate_from_sup_generate_from,
    },
    rw A1A,
    apply generate_from_measurable,
    {
      refl,
    },
    {
      intro BC,
      intros H,
      cases H,
      {
        cases H with B H,
        cases H,
        subst BC,
        have A1B:(λ (a : α), (X a, Y a)) ⁻¹' (prod.fst ⁻¹' B) = (X ⁻¹' B),
        {
          ext,split;intros a,
          {
            simp at a,
            apply a,
          },
          {
            simp,
            apply a,
          }
        },
        rw A1B,
        apply B1,
        apply H_left,
      },
      {
        cases H with C H,
        cases H,
        subst BC,
        have A1C:(λ (a : α), (X a, Y a)) ⁻¹' (prod.snd ⁻¹' C) = (Y ⁻¹' C),
        {
          ext,split;intros a,
          {
            simp at a,
            apply a,
          },
          {
            simp,
            apply a,
          }
        },
        rw A1C,
        apply B2,
        apply H_left,
      }
    }
  },
  apply A1,
end

lemma compose_measurable_fun_measurable {α β γ:Type*}
  [measurable_space α] [measurable_space β] [measurable_space γ]
  (X:β → γ) (Y: α→  β):
  measurable X →
  measurable Y →
  measurable (X ∘ Y) :=
begin
  intros B1 B2,
  apply measurable_intro,
  intros B a,
  have A1:(X ∘ Y ⁻¹' B)=(Y ⁻¹' (X ⁻¹' B)),
  {
    refl,
  },
  rw A1,
  apply measurable_elim Y _ B2,
  apply measurable_elim X _ B1,
  apply a
end

-- Constant functions are measurable.
-- Different than measurable_set.const
lemma const_measurable {Ω:Type*} [measurable_space Ω] {β:Type*} [measurable_space β] (c:β):
  (measurable (λ ω:Ω, c)) :=
begin
  apply measurable_const,
end

lemma measurable_set_of_le_of_measurable_set
{α : Type*} {M1 : measurable_space α} {M2 : measurable_space α}
  {X:set α}:
  M1 ≤ M2 →
measurable_space.measurable_set' M1 X →
   measurable_space.measurable_set' M2 X :=
begin
  intros A2 A1,
  rw ← measurable_space_le_def2 at A2,
  apply A2,
  apply A1,
end

-- cf. measurable_set_prod
lemma measurable_set_prod' {β : Type*} {γ : Type*}
  {Mβ : measurable_space β} {Mγ : measurable_space γ}
  {X:set β} {Y:set γ}:measurable_set X →
   measurable_set Y →
   measurable_set (set.prod X Y) :=
begin
  --apply measurable_set_of_le_of_measurable_set,
  intros A1 A2,
  rw generate_from_self Mβ,
  rw generate_from_self Mγ,
  apply measurable_set_of_le_of_measurable_set,
  apply prod_measurable_space_le,
  apply measurable_space.measurable_set_generate_from,
  simp,
  apply exists.intro X,
  split,
  apply A1,
  apply exists.intro Y,
  split,
  apply A2,
  refl,
end

lemma measurable.preimage {α β:Type*} [measurable_space α] [measurable_space β] {f:α → β}
   {S:set β}:measurable f → measurable_set S → measurable_set (set.preimage f S) :=
begin
  intros A1 A2,
  apply A1,
  apply A2,
end

lemma measurable.if {α β:Type*}
  {Mα:measurable_space α} {Mβ:measurable_space β}
  {E:set α} {D:decidable_pred E}
  {X Y:α → β}:measurable_set E →
  measurable X →
  measurable Y →
  measurable (λ a:α, if (E a) then (X a) else (Y a)) :=
begin
  intros A1 A2 A3,
  intros S B1,
  rw preimage_if,
  apply measurable_set.union,
  {
    apply measurable_set.inter,
    apply A1,
    apply A2,
    apply B1,
  },
  {
    apply measurable_set.inter,
    apply measurable_set.compl,
    apply A1,
    apply A3,
    apply B1,
  },
end

lemma measurable_set.pi' {α:Type*} [F:fintype α] {β:α → Type*} [M:∀ a, measurable_space (β a)]
  {P:Π a, set (β a)} (T:set α):(∀ a, measurable_set (P a)) →
  measurable_set (set.pi T P) := begin
  classical,
  intros A0,
  have A1:(set.pi T P) = ⋂ (a ∈ T), ((λ (p:Π a, β a), p a) ⁻¹' (P a)),
  { ext x, simp,  },
  rw A1,
  have A3:trunc (encodable α) := encodable.trunc_encodable_of_fintype α,
  trunc_cases A3,
  haveI:encodable α := A3,
  apply measurable_set.Inter,
  intros a',
  cases classical.em (a' ∈ T) with A4 A4,
  { have A5:(⋂ (H : a' ∈ T), (λ (p : Π (a : α), β a), p a') ⁻¹' P a') =
            (λ (p : Π (a : α), β a), p a') ⁻¹' P a',
    { ext, simp; split; intros A5_1,
      apply A5_1 A4,
      intros A5_2, apply A5_1 },
      rw A5,
    have A2:measurable_space.comap (λ (p:Π a, β a), p a') (M a') ≤ measurable_space.pi,
    { simp [measurable_space.pi], apply @le_supr (measurable_space (Π a, β a)) _ _ _ (a') },
    apply A2,
    simp [measurable_space.comap],
    apply exists.intro (P a'),
    simp,
    apply A0 a' },
  { have A6:(⋂ (H : a' ∈ T), (λ (p : Π (a : α), β a), p a') ⁻¹' P a') = set.univ,
    { ext, simp; intros A6_1,
      apply absurd A4,
      simp,apply A6_1 },
    rw A6,
    simp, },
end

--Unused.
lemma measurable_space.generate_measurable_monotone {α:Type*} {s t:set (set α)}:
  (s ⊆ t) →
  (∀ u, (measurable_space.generate_measurable s u →
          measurable_space.generate_measurable t u)) :=
begin
  intros h1 u h2,
  induction h2 with u' h_u' u' h_u' h_ind f h_f h_ind,
  { apply measurable_space.generate_measurable.basic,
    apply h1,
    apply h_u' },
  { apply measurable_space.generate_measurable.empty },
  { apply measurable_space.generate_measurable.compl,
    apply h_ind },
  { apply measurable_space.generate_measurable.union,
    apply h_ind },
end


/-
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, Patrick Massot

! This file was ported from Lean 3 source module data.set.intervals.proj_Icc
! leanprover-community/mathlib commit c3291da49cfa65f0d43b094750541c0731edc932
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Set.Function
import Mathbin.Data.Set.Intervals.Basic

/-!
# Projection of a line onto a closed interval

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

Given a linearly ordered type `α`, in this file we define

* `set.proj_Icc (a b : α) (h : a ≤ b)` to be the map `α → [a, b]` sending `(-∞, a]` to `a`, `[b, ∞)`
  to `b`, and each point `x ∈ [a, b]` to itself;
* `set.Icc_extend {a b : α} (h : a ≤ b) (f : Icc a b → β)` to be the extension of `f` to `α` defined
  as `f ∘ proj_Icc a b h`.

We also prove some trivial properties of these maps.
-/

variable {α β : Type _} [LinearOrder α]

open Function

namespace Set

#print Set.projIcc /-
/-- Projection of `α` to the closed interval `[a, b]`. -/
def projIcc (a b : α) (h : a ≤ b) (x : α) : Icc a b :=
  ⟨max a (min b x), le_max_left _ _, max_le h (min_le_left _ _)⟩
#align set.proj_Icc Set.projIcc
-/

variable {a b : α} (h : a ≤ b) {x : α}

#print Set.projIcc_of_le_left /-
theorem projIcc_of_le_left (hx : x ≤ a) : projIcc a b h x = ⟨a, left_mem_Icc.2 h⟩ := by
  simp [proj_Icc, hx, hx.trans h]
#align set.proj_Icc_of_le_left Set.projIcc_of_le_left
-/

#print Set.projIcc_left /-
@[simp]
theorem projIcc_left : projIcc a b h a = ⟨a, left_mem_Icc.2 h⟩ :=
  projIcc_of_le_left h le_rfl
#align set.proj_Icc_left Set.projIcc_left
-/

#print Set.projIcc_of_right_le /-
theorem projIcc_of_right_le (hx : b ≤ x) : projIcc a b h x = ⟨b, right_mem_Icc.2 h⟩ := by
  simp [proj_Icc, hx, h]
#align set.proj_Icc_of_right_le Set.projIcc_of_right_le
-/

#print Set.projIcc_right /-
@[simp]
theorem projIcc_right : projIcc a b h b = ⟨b, right_mem_Icc.2 h⟩ :=
  projIcc_of_right_le h le_rfl
#align set.proj_Icc_right Set.projIcc_right
-/

#print Set.projIcc_eq_left /-
theorem projIcc_eq_left (h : a < b) : projIcc a b h.le x = ⟨a, left_mem_Icc.mpr h.le⟩ ↔ x ≤ a :=
  by
  refine' ⟨fun h' => _, proj_Icc_of_le_left _⟩
  simp_rw [Subtype.ext_iff_val, proj_Icc, max_eq_left_iff, min_le_iff, h.not_le, false_or_iff] at h'
  exact h'
#align set.proj_Icc_eq_left Set.projIcc_eq_left
-/

#print Set.projIcc_eq_right /-
theorem projIcc_eq_right (h : a < b) : projIcc a b h.le x = ⟨b, right_mem_Icc.mpr h.le⟩ ↔ b ≤ x :=
  by
  refine' ⟨fun h' => _, proj_Icc_of_right_le _⟩
  simp_rw [Subtype.ext_iff_val, proj_Icc] at h'
  have := ((max_choice _ _).resolve_left (by simp [h.ne', h'])).symm.trans h'
  exact min_eq_left_iff.mp this
#align set.proj_Icc_eq_right Set.projIcc_eq_right
-/

#print Set.projIcc_of_mem /-
theorem projIcc_of_mem (hx : x ∈ Icc a b) : projIcc a b h x = ⟨x, hx⟩ := by
  simp [proj_Icc, hx.1, hx.2]
#align set.proj_Icc_of_mem Set.projIcc_of_mem
-/

#print Set.projIcc_val /-
@[simp]
theorem projIcc_val (x : Icc a b) : projIcc a b h x = x :=
  by
  cases x
  apply proj_Icc_of_mem
#align set.proj_Icc_coe Set.projIcc_val
-/

#print Set.projIcc_surjOn /-
theorem projIcc_surjOn : SurjOn (projIcc a b h) (Icc a b) univ := fun x _ =>
  ⟨x, x.2, projIcc_val h x⟩
#align set.proj_Icc_surj_on Set.projIcc_surjOn
-/

#print Set.projIcc_surjective /-
theorem projIcc_surjective : Surjective (projIcc a b h) := fun x => ⟨x, projIcc_val h x⟩
#align set.proj_Icc_surjective Set.projIcc_surjective
-/

#print Set.range_projIcc /-
@[simp]
theorem range_projIcc : range (projIcc a b h) = univ :=
  (projIcc_surjective h).range_eq
#align set.range_proj_Icc Set.range_projIcc
-/

#print Set.monotone_projIcc /-
theorem monotone_projIcc : Monotone (projIcc a b h) := fun x y hxy =>
  max_le_max le_rfl <| min_le_min le_rfl hxy
#align set.monotone_proj_Icc Set.monotone_projIcc
-/

#print Set.strictMonoOn_projIcc /-
theorem strictMonoOn_projIcc : StrictMonoOn (projIcc a b h) (Icc a b) := fun x hx y hy hxy => by
  simpa only [proj_Icc_of_mem, hx, hy]
#align set.strict_mono_on_proj_Icc Set.strictMonoOn_projIcc
-/

#print Set.IccExtend /-
/-- Extend a function `[a, b] → β` to a map `α → β`. -/
def IccExtend {a b : α} (h : a ≤ b) (f : Icc a b → β) : α → β :=
  f ∘ projIcc a b h
#align set.Icc_extend Set.IccExtend
-/

/- warning: set.Icc_extend_range -> Set.IccExtend_range is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u1} α] {a : α} {b : α} (h : LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) a b) (f : (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)) -> β), Eq.{succ u2} (Set.{u2} β) (Set.range.{u2, succ u1} β α (Set.IccExtend.{u1, u2} α β _inst_1 a b h f)) (Set.range.{u2, succ u1} β (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)) f)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LinearOrder.{u2} α] {a : α} {b : α} (h : LE.le.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1)))))) a b) (f : (Set.Elem.{u2} α (Set.Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b)) -> β), Eq.{succ u1} (Set.{u1} β) (Set.range.{u1, succ u2} β α (Set.IccExtend.{u2, u1} α β _inst_1 a b h f)) (Set.range.{u1, succ u2} β (Set.Elem.{u2} α (Set.Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b)) f)
Case conversion may be inaccurate. Consider using '#align set.Icc_extend_range Set.IccExtend_rangeₓ'. -/
@[simp]
theorem IccExtend_range (f : Icc a b → β) : range (IccExtend h f) = range f := by
  simp only [Icc_extend, range_comp f, range_proj_Icc, range_id']
#align set.Icc_extend_range Set.IccExtend_range

/- warning: set.Icc_extend_of_le_left -> Set.IccExtend_of_le_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u1} α] {a : α} {b : α} (h : LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) a b) {x : α} (f : (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)) -> β), (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) x a) -> (Eq.{succ u2} β (Set.IccExtend.{u1, u2} α β _inst_1 a b h f x) (f (Subtype.mk.{succ u1} α (fun (x : α) => Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)) a (Iff.mpr (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)) (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) a b) (Set.left_mem_Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b) h))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LinearOrder.{u2} α] {a : α} {b : α} (h : LE.le.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1)))))) a b) {x : α} (f : (Set.Elem.{u2} α (Set.Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b)) -> β), (LE.le.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1)))))) x a) -> (Eq.{succ u1} β (Set.IccExtend.{u2, u1} α β _inst_1 a b h f x) (f (Subtype.mk.{succ u2} α (fun (x : α) => Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) x (Set.Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b)) a (Iff.mpr (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) a (Set.Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b)) (LE.le.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1)))))) a b) (Set.left_mem_Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b) h))))
Case conversion may be inaccurate. Consider using '#align set.Icc_extend_of_le_left Set.IccExtend_of_le_leftₓ'. -/
theorem IccExtend_of_le_left (f : Icc a b → β) (hx : x ≤ a) :
    IccExtend h f x = f ⟨a, left_mem_Icc.2 h⟩ :=
  congr_arg f <| projIcc_of_le_left h hx
#align set.Icc_extend_of_le_left Set.IccExtend_of_le_left

/- warning: set.Icc_extend_left -> Set.IccExtend_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u1} α] {a : α} {b : α} (h : LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) a b) (f : (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)) -> β), Eq.{succ u2} β (Set.IccExtend.{u1, u2} α β _inst_1 a b h f a) (f (Subtype.mk.{succ u1} α (fun (x : α) => Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)) a (Iff.mpr (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)) (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) a b) (Set.left_mem_Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b) h)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LinearOrder.{u2} α] {a : α} {b : α} (h : LE.le.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1)))))) a b) (f : (Set.Elem.{u2} α (Set.Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b)) -> β), Eq.{succ u1} β (Set.IccExtend.{u2, u1} α β _inst_1 a b h f a) (f (Subtype.mk.{succ u2} α (fun (x : α) => Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) x (Set.Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b)) a (Iff.mpr (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) a (Set.Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b)) (LE.le.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1)))))) a b) (Set.left_mem_Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b) h)))
Case conversion may be inaccurate. Consider using '#align set.Icc_extend_left Set.IccExtend_leftₓ'. -/
@[simp]
theorem IccExtend_left (f : Icc a b → β) : IccExtend h f a = f ⟨a, left_mem_Icc.2 h⟩ :=
  IccExtend_of_le_left h f le_rfl
#align set.Icc_extend_left Set.IccExtend_left

/- warning: set.Icc_extend_of_right_le -> Set.IccExtend_of_right_le is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u1} α] {a : α} {b : α} (h : LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) a b) {x : α} (f : (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)) -> β), (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) b x) -> (Eq.{succ u2} β (Set.IccExtend.{u1, u2} α β _inst_1 a b h f x) (f (Subtype.mk.{succ u1} α (fun (x : α) => Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)) b (Iff.mpr (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) b (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)) (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) a b) (Set.right_mem_Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b) h))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LinearOrder.{u2} α] {a : α} {b : α} (h : LE.le.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1)))))) a b) {x : α} (f : (Set.Elem.{u2} α (Set.Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b)) -> β), (LE.le.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1)))))) b x) -> (Eq.{succ u1} β (Set.IccExtend.{u2, u1} α β _inst_1 a b h f x) (f (Subtype.mk.{succ u2} α (fun (x : α) => Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) x (Set.Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b)) b (Iff.mpr (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) b (Set.Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b)) (LE.le.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1)))))) a b) (Set.right_mem_Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b) h))))
Case conversion may be inaccurate. Consider using '#align set.Icc_extend_of_right_le Set.IccExtend_of_right_leₓ'. -/
theorem IccExtend_of_right_le (f : Icc a b → β) (hx : b ≤ x) :
    IccExtend h f x = f ⟨b, right_mem_Icc.2 h⟩ :=
  congr_arg f <| projIcc_of_right_le h hx
#align set.Icc_extend_of_right_le Set.IccExtend_of_right_le

/- warning: set.Icc_extend_right -> Set.IccExtend_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u1} α] {a : α} {b : α} (h : LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) a b) (f : (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)) -> β), Eq.{succ u2} β (Set.IccExtend.{u1, u2} α β _inst_1 a b h f b) (f (Subtype.mk.{succ u1} α (fun (x : α) => Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)) b (Iff.mpr (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) b (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)) (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) a b) (Set.right_mem_Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b) h)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LinearOrder.{u2} α] {a : α} {b : α} (h : LE.le.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1)))))) a b) (f : (Set.Elem.{u2} α (Set.Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b)) -> β), Eq.{succ u1} β (Set.IccExtend.{u2, u1} α β _inst_1 a b h f b) (f (Subtype.mk.{succ u2} α (fun (x : α) => Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) x (Set.Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b)) b (Iff.mpr (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) b (Set.Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b)) (LE.le.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1)))))) a b) (Set.right_mem_Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b) h)))
Case conversion may be inaccurate. Consider using '#align set.Icc_extend_right Set.IccExtend_rightₓ'. -/
@[simp]
theorem IccExtend_right (f : Icc a b → β) : IccExtend h f b = f ⟨b, right_mem_Icc.2 h⟩ :=
  IccExtend_of_right_le h f le_rfl
#align set.Icc_extend_right Set.IccExtend_right

/- warning: set.Icc_extend_of_mem -> Set.IccExtend_of_mem is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u1} α] {a : α} {b : α} (h : LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) a b) {x : α} (f : (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)) -> β) (hx : Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)), Eq.{succ u2} β (Set.IccExtend.{u1, u2} α β _inst_1 a b h f x) (f (Subtype.mk.{succ u1} α (fun (x : α) => Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)) x hx))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LinearOrder.{u2} α] {a : α} {b : α} (h : LE.le.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1)))))) a b) {x : α} (f : (Set.Elem.{u2} α (Set.Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b)) -> β) (hx : Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) x (Set.Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b)), Eq.{succ u1} β (Set.IccExtend.{u2, u1} α β _inst_1 a b h f x) (f (Subtype.mk.{succ u2} α (fun (x : α) => Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) x (Set.Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b)) x hx))
Case conversion may be inaccurate. Consider using '#align set.Icc_extend_of_mem Set.IccExtend_of_memₓ'. -/
theorem IccExtend_of_mem (f : Icc a b → β) (hx : x ∈ Icc a b) : IccExtend h f x = f ⟨x, hx⟩ :=
  congr_arg f <| projIcc_of_mem h hx
#align set.Icc_extend_of_mem Set.IccExtend_of_mem

/- warning: set.Icc_extend_coe -> Set.Icc_extend_coe is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u1} α] {a : α} {b : α} (h : LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) a b) (f : (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)) -> β) (x : coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)), Eq.{succ u2} β (Set.IccExtend.{u1, u2} α β _inst_1 a b h f ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)) α (HasLiftT.mk.{succ u1, succ u1} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)) α (CoeTCₓ.coe.{succ u1, succ u1} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)) α (coeBase.{succ u1, succ u1} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)) α (coeSubtype.{succ u1} α (fun (x : α) => Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)))))) x)) (f x)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LinearOrder.{u2} α] {a : α} {b : α} (h : LE.le.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1)))))) a b) (f : (Set.Elem.{u2} α (Set.Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b)) -> β) (x : Set.Elem.{u2} α (Set.Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b)), Eq.{succ u1} β (Set.IccExtend.{u2, u1} α β _inst_1 a b h f (Subtype.val.{succ u2} α (fun (x : α) => Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) x (Set.Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b)) x)) (f x)
Case conversion may be inaccurate. Consider using '#align set.Icc_extend_coe Set.Icc_extend_coeₓ'. -/
@[simp]
theorem Icc_extend_coe (f : Icc a b → β) (x : Icc a b) : IccExtend h f x = f x :=
  congr_arg f <| projIcc_val h x
#align set.Icc_extend_coe Set.Icc_extend_coe

end Set

open Set

variable [Preorder β] {a b : α} (h : a ≤ b) {f : Icc a b → β}

/- warning: monotone.Icc_extend -> Monotone.IccExtend is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u1} α] [_inst_2 : Preorder.{u2} β] {a : α} {b : α} (h : LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) a b) {f : (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)) -> β}, (Monotone.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)) β (Subtype.preorder.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) (fun (x : α) => Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b))) _inst_2 f) -> (Monotone.{u1, u2} α β (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) _inst_2 (Set.IccExtend.{u1, u2} α β _inst_1 a b h f))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LinearOrder.{u2} α] [_inst_2 : Preorder.{u1} β] {a : α} {b : α} (h : LE.le.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1)))))) a b) {f : (Set.Elem.{u2} α (Set.Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b)) -> β}, (Monotone.{u2, u1} (Set.Elem.{u2} α (Set.Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b)) β (Subtype.preorder.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) (fun (x : α) => Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) x (Set.Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b))) _inst_2 f) -> (Monotone.{u2, u1} α β (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) _inst_2 (Set.IccExtend.{u2, u1} α β _inst_1 a b h f))
Case conversion may be inaccurate. Consider using '#align monotone.Icc_extend Monotone.IccExtendₓ'. -/
theorem Monotone.IccExtend (hf : Monotone f) : Monotone (IccExtend h f) :=
  hf.comp <| monotone_projIcc h
#align monotone.Icc_extend Monotone.IccExtend

/- warning: strict_mono.strict_mono_on_Icc_extend -> StrictMono.strictMonoOn_IccExtend is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u1} α] [_inst_2 : Preorder.{u2} β] {a : α} {b : α} (h : LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) a b) {f : (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)) -> β}, (StrictMono.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b)) β (Subtype.preorder.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) (fun (x : α) => Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b))) _inst_2 f) -> (StrictMonoOn.{u1, u2} α β (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) _inst_2 (Set.IccExtend.{u1, u2} α β _inst_1 a b h f) (Set.Icc.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) a b))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LinearOrder.{u2} α] [_inst_2 : Preorder.{u1} β] {a : α} {b : α} (h : LE.le.{u2} α (Preorder.toLE.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1)))))) a b) {f : (Set.Elem.{u2} α (Set.Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b)) -> β}, (StrictMono.{u2, u1} (Set.Elem.{u2} α (Set.Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b)) β (Subtype.preorder.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) (fun (x : α) => Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) x (Set.Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b))) _inst_2 f) -> (StrictMonoOn.{u2, u1} α β (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) _inst_2 (Set.IccExtend.{u2, u1} α β _inst_1 a b h f) (Set.Icc.{u2} α (PartialOrder.toPreorder.{u2} α (SemilatticeInf.toPartialOrder.{u2} α (Lattice.toSemilatticeInf.{u2} α (DistribLattice.toLattice.{u2} α (instDistribLattice.{u2} α _inst_1))))) a b))
Case conversion may be inaccurate. Consider using '#align strict_mono.strict_mono_on_Icc_extend StrictMono.strictMonoOn_IccExtendₓ'. -/
theorem StrictMono.strictMonoOn_IccExtend (hf : StrictMono f) :
    StrictMonoOn (IccExtend h f) (Icc a b) :=
  hf.comp_strictMonoOn (strictMonoOn_projIcc h)
#align strict_mono.strict_mono_on_Icc_extend StrictMono.strictMonoOn_IccExtend


/-
Author: <Redacted for anonymized submission>
-/

import .vars
import ...attributes
import ...integral_domain_tactic

/-!
# Knowledge Soundness

This file proves the knowledge-soundness property of a very trivial SNARK
-/

open_locale big_operators classical

section

open mv_polynomial vars

noncomputable theory

universes u

/-- The finite field parameter of our SNARK -/
parameter {F : Type u}
parameter [field F]

-- TODO

/-- Checks whether a statement witness pair satisfies the SSP -/
def satisfying (a b c d e : F) := a * d = e ∨ b * c = e

/-- The coefficients of the CRS elements in the algebraic adversary's representation -/
parameters {A B C D E  : F}

/-- Polynomial forms of the adversary's proof representation -/
def proof1 : mv_polynomial vars F :=
  mv_polynomial.C A * X vars.α
  +
  mv_polynomial.C B * X vars.β

def proof2 : mv_polynomial vars F :=
  mv_polynomial.C C * X vars.α
  +
  mv_polynomial.C D * X vars.β

def proof3 : mv_polynomial vars F :=
  mv_polynomial.C E * X vars.α * X vars.β

def verified  : Prop := proof1 * proof2 = proof3

open finsupp

/-- The main theorem for the soundness of the Groth '16 SNARK.
Show that if the adversary polynomials obey the equations,
then the coefficients give a satisfying witness. -/
theorem soundness :
  verified
  -> (satisfying A B C D E)
:=
begin

  intros eqn,
  rw satisfying,

  rw verified at eqn,
  rw [proof1, proof2, proof3] at eqn,

  simp only [] with polynomial_nf_3 at eqn,

  have h20 := congr_arg (coeff (single α 2 + single β 0)) eqn,
  have h11 := congr_arg (coeff (single α 1 + single β 1)) eqn,
  have h02 := congr_arg (coeff (single α 0 + single β 2)) eqn,

  clear eqn,

  simp only [finsupp_vars_eq_ext] with coeff_simp finsupp_eq at *,
  simp only [] with finsupp_simp at *,

  tactic.integral_domain_tactic_v3,
  repeat { apply or.inr, assumption, },
  repeat { apply or.inl, assumption, },

end

end


/-
Copyright (c) 2020 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Scott Morrison
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.pnat.basic
import Mathlib.data.finset.intervals
import Mathlib.PostPort

namespace Mathlib

namespace pnat

/-- `Ico l u` is the set of positive natural numbers `l ≤ k < u`. -/
def Ico (l : ℕ+) (u : ℕ+) : finset ℕ+ :=
  finset.map
    (function.embedding.mk
      (fun (n : Subtype fun (x : ℕ) => x ∈ finset.Ico ↑l ↑u) => { val := ↑n, property := sorry })
      sorry)
    (finset.attach (finset.Ico ↑l ↑u))

@[simp] theorem Ico.mem {n : ℕ+} {m : ℕ+} {l : ℕ+} : l ∈ Ico n m ↔ n ≤ l ∧ l < m := sorry

@[simp] theorem Ico.card (l : ℕ+) (u : ℕ+) : finset.card (Ico l u) = ↑u - ↑l := sorry

end Mathlib


import topology.algebra.ordered
import o_minimal.order

namespace omin

open o_minimal

universe u

variables (R : Type u) [decidable_linear_order R] (S : struc R)

variables {X : Type*} [has_coordinates R X]
variables {Y : Type*} [has_coordinates R Y]
variables {Z : Type*} [has_coordinates R Z]
variables {W : Type*} [has_coordinates R W]

section

local attribute [instance] preorder.topology

variables (X)
def coordinate_topology : topological_space X :=
topological_space.induced (@coords R X _) (show topological_space (fin _ → R), by apply_instance)

end

variables {X}
def is_open (s : set X) : Prop :=
@_root_.is_open X (coordinate_topology R X) s

def interior (s : set X) : set X :=
@_root_.interior X (coordinate_topology R X) s

def continuous (f : X → Y) : Prop :=
@_root_.continuous X Y (coordinate_topology R X) (coordinate_topology R Y) f

section

-- This seems like it shouldn't work, because we need to guess `R`,
-- but it does work here (but not above for some reason).
local attribute [instance] coordinate_topology

lemma is_open_univ : is_open R (set.univ : set X) :=
_root_.is_open_univ

-- etc.

lemma is_open_iff_subset_interior {s : set X} : is_open R s ↔ s ⊆ interior R s :=
subset_interior_iff_open.symm

lemma mem_interior_iff {s : set X} {x : X} :
  x ∈ interior R s ↔
  ∃ (l u : fin _ → R), (∀ i, l i < coords R x i ∧ coords R x i < u i)
    ∧ ∀ y, (∀ i, l i < coords R y i ∧ coords R y i < u i) → y ∈ s :=
sorry

end

-- Definability.

variables [is_definable_le S R]
variables [is_definable S X] [is_definable S Y] [is_definable S Z] [is_definable S W]

lemma def_interior {s : set X} (hs : def_set S s) : def_set S (interior R s) :=
begin
  -- TODO: would it make more sense to use `fin _ → R` for the types of `l`, `u`?
  -- bring back instance for `fin n → R` (or `fin n → X`)?
  suffices : def_set S
    {x | ∃ (l u : finvec _ R), (∀ i, l i < coords R x i ∧ coords R x i < u i)
         ∧ ∀ y, (∀ i, l i < coords R y i ∧ coords R y i < u i) → y ∈ s},
  { convert this,
    ext x,
    rw mem_interior_iff, refl },
  apply def_set.exists,
  apply def_set.exists,
  apply def_set.and,
  { apply def_set.forall_fintype, intro i,
    apply def_set.and,
    { apply definable_lt,
      exact (def_fun.coord_rn i).comp (def_fun.snd.comp def_fun.fst),
      exact (def_fun.coord i).comp (def_fun.fst.comp def_fun.fst) },
    { apply definable_lt,
      exact (def_fun.coord i).comp (def_fun.fst.comp def_fun.fst),
      exact (def_fun.coord_rn i).comp def_fun.snd } },
  { apply def_set.forall,
    apply def_set.imp,
    { apply def_set.forall_fintype, intro i,
      apply def_set.and,
      { apply definable_lt,
        exact (def_fun.coord_rn i).comp (def_fun.snd.comp (def_fun.fst.comp def_fun.fst)),
        exact (def_fun.coord i).comp def_fun.snd },
      { apply definable_lt,
        exact (def_fun.coord i).comp def_fun.snd,
        exact (def_fun.coord_rn i).comp (def_fun.snd.comp def_fun.fst) } },
    exact def_fun.preimage def_fun.snd hs }
end

end omin


/-
Copyright (c) 2020 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.monoidal.opposite
! leanprover-community/mathlib commit 14b69e9f3c16630440a2cbd46f1ddad0d561dee7
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.CategoryTheory.Monoidal.Coherence

/-!
# Monoidal opposites

We write `Cᵐᵒᵖ` for the monoidal opposite of a monoidal category `C`.
-/

universe v₁ v₂ u₁ u₂

variable {C : Type u₁}

namespace CategoryTheory

open CategoryTheory.MonoidalCategory

/-- A type synonym for the monoidal opposite. Use the notation `Cᴹᵒᵖ`. -/
@[nolint has_nonempty_instance]
def MonoidalOpposite (C : Type u₁) :=
  C
#align category_theory.monoidal_opposite CategoryTheory.MonoidalOpposite

namespace MonoidalOpposite

-- mathport name: «expr ᴹᵒᵖ»
notation:max C "ᴹᵒᵖ" => MonoidalOpposite C

/-- Think of an object of `C` as an object of `Cᴹᵒᵖ`. -/
@[pp_nodot]
def mop (X : C) : Cᴹᵒᵖ :=
  X
#align category_theory.monoidal_opposite.mop CategoryTheory.MonoidalOpposite.mop

/-- Think of an object of `Cᴹᵒᵖ` as an object of `C`. -/
@[pp_nodot]
def unmop (X : Cᴹᵒᵖ) : C :=
  X
#align category_theory.monoidal_opposite.unmop CategoryTheory.MonoidalOpposite.unmop

theorem op_injective : Function.Injective (mop : C → Cᴹᵒᵖ) := fun _ _ => id
#align category_theory.monoidal_opposite.op_injective CategoryTheory.MonoidalOpposite.op_injective

theorem unop_injective : Function.Injective (unmop : Cᴹᵒᵖ → C) := fun _ _ => id
#align category_theory.monoidal_opposite.unop_injective CategoryTheory.MonoidalOpposite.unop_injective

@[simp]
theorem op_inj_iff (x y : C) : mop x = mop y ↔ x = y :=
  Iff.rfl
#align category_theory.monoidal_opposite.op_inj_iff CategoryTheory.MonoidalOpposite.op_inj_iff

@[simp]
theorem unop_inj_iff (x y : Cᴹᵒᵖ) : unmop x = unmop y ↔ x = y :=
  Iff.rfl
#align category_theory.monoidal_opposite.unop_inj_iff CategoryTheory.MonoidalOpposite.unop_inj_iff

@[simp]
theorem mop_unmop (X : Cᴹᵒᵖ) : mop (unmop X) = X :=
  rfl
#align category_theory.monoidal_opposite.mop_unmop CategoryTheory.MonoidalOpposite.mop_unmop

@[simp]
theorem unmop_mop (X : C) : unmop (mop X) = X :=
  rfl
#align category_theory.monoidal_opposite.unmop_mop CategoryTheory.MonoidalOpposite.unmop_mop

instance monoidalOppositeCategory [I : Category.{v₁} C] : Category Cᴹᵒᵖ
    where
  Hom X Y := unmop X ⟶ unmop Y
  id X := 𝟙 (unmop X)
  comp X Y Z f g := f ≫ g
#align category_theory.monoidal_opposite.monoidal_opposite_category CategoryTheory.MonoidalOpposite.monoidalOppositeCategory

end MonoidalOpposite

end CategoryTheory

open CategoryTheory

open CategoryTheory.MonoidalOpposite

variable [Category.{v₁} C]

/-- The monoidal opposite of a morphism `f : X ⟶ Y` is just `f`, thought of as `mop X ⟶ mop Y`. -/
def Quiver.Hom.mop {X Y : C} (f : X ⟶ Y) : @Quiver.Hom Cᴹᵒᵖ _ (mop X) (mop Y) :=
  f
#align quiver.hom.mop Quiver.Hom.mop

/-- We can think of a morphism `f : mop X ⟶ mop Y` as a morphism `X ⟶ Y`. -/
def Quiver.Hom.unmop {X Y : Cᴹᵒᵖ} (f : X ⟶ Y) : unmop X ⟶ unmop Y :=
  f
#align quiver.hom.unmop Quiver.Hom.unmop

namespace CategoryTheory

theorem mop_inj {X Y : C} : Function.Injective (Quiver.Hom.mop : (X ⟶ Y) → (mop X ⟶ mop Y)) :=
  fun _ _ H => congr_arg Quiver.Hom.unmop H
#align category_theory.mop_inj CategoryTheory.mop_inj

theorem unmop_inj {X Y : Cᴹᵒᵖ} :
    Function.Injective (Quiver.Hom.unmop : (X ⟶ Y) → (unmop X ⟶ unmop Y)) := fun _ _ H =>
  congr_arg Quiver.Hom.mop H
#align category_theory.unmop_inj CategoryTheory.unmop_inj

@[simp]
theorem unmop_mop {X Y : C} {f : X ⟶ Y} : f.mop.unmop = f :=
  rfl
#align category_theory.unmop_mop CategoryTheory.unmop_mop

@[simp]
theorem mop_unmop {X Y : Cᴹᵒᵖ} {f : X ⟶ Y} : f.unmop.mop = f :=
  rfl
#align category_theory.mop_unmop CategoryTheory.mop_unmop

@[simp]
theorem mop_comp {X Y Z : C} {f : X ⟶ Y} {g : Y ⟶ Z} : (f ≫ g).mop = f.mop ≫ g.mop :=
  rfl
#align category_theory.mop_comp CategoryTheory.mop_comp

@[simp]
theorem mop_id {X : C} : (𝟙 X).mop = 𝟙 (mop X) :=
  rfl
#align category_theory.mop_id CategoryTheory.mop_id

@[simp]
theorem unmop_comp {X Y Z : Cᴹᵒᵖ} {f : X ⟶ Y} {g : Y ⟶ Z} : (f ≫ g).unmop = f.unmop ≫ g.unmop :=
  rfl
#align category_theory.unmop_comp CategoryTheory.unmop_comp

@[simp]
theorem unmop_id {X : Cᴹᵒᵖ} : (𝟙 X).unmop = 𝟙 (unmop X) :=
  rfl
#align category_theory.unmop_id CategoryTheory.unmop_id

@[simp]
theorem unmop_id_mop {X : C} : (𝟙 (mop X)).unmop = 𝟙 X :=
  rfl
#align category_theory.unmop_id_mop CategoryTheory.unmop_id_mop

@[simp]
theorem mop_id_unmop {X : Cᴹᵒᵖ} : (𝟙 (unmop X)).mop = 𝟙 X :=
  rfl
#align category_theory.mop_id_unmop CategoryTheory.mop_id_unmop

namespace Iso

variable {X Y : C}

/-- An isomorphism in `C` gives an isomorphism in `Cᴹᵒᵖ`. -/
@[simps]
def mop (f : X ≅ Y) : mop X ≅ mop Y where
  Hom := f.Hom.mop
  inv := f.inv.mop
  hom_inv_id' := unmop_inj f.hom_inv_id
  inv_hom_id' := unmop_inj f.inv_hom_id
#align category_theory.iso.mop CategoryTheory.Iso.mop

end Iso

variable [MonoidalCategory.{v₁} C]

open Opposite MonoidalCategory

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
instance monoidalCategoryOp : MonoidalCategory Cᵒᵖ
    where
  tensorObj X Y := op (unop X ⊗ unop Y)
  tensorHom X₁ Y₁ X₂ Y₂ f g := (f.unop ⊗ g.unop).op
  tensorUnit := op (𝟙_ C)
  associator X Y Z := (α_ (unop X) (unop Y) (unop Z)).symm.op
  leftUnitor X := (λ_ (unop X)).symm.op
  rightUnitor X := (ρ_ (unop X)).symm.op
  associator_naturality' := by
    intros
    apply Quiver.Hom.unop_inj
    simp
  leftUnitor_naturality' := by
    intros
    apply Quiver.Hom.unop_inj
    simp
  rightUnitor_naturality' := by
    intros
    apply Quiver.Hom.unop_inj
    simp
  triangle' := by
    intros
    apply Quiver.Hom.unop_inj
    coherence
  pentagon' := by
    intros
    apply Quiver.Hom.unop_inj
    coherence
#align category_theory.monoidal_category_op CategoryTheory.monoidalCategoryOp

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
theorem op_tensorObj (X Y : Cᵒᵖ) : X ⊗ Y = op (unop X ⊗ unop Y) :=
  rfl
#align category_theory.op_tensor_obj CategoryTheory.op_tensorObj

theorem op_tensorUnit : 𝟙_ Cᵒᵖ = op (𝟙_ C) :=
  rfl
#align category_theory.op_tensor_unit CategoryTheory.op_tensorUnit

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
instance monoidalCategoryMop : MonoidalCategory Cᴹᵒᵖ
    where
  tensorObj X Y := mop (unmop Y ⊗ unmop X)
  tensorHom X₁ Y₁ X₂ Y₂ f g := (g.unmop ⊗ f.unmop).mop
  tensorUnit := mop (𝟙_ C)
  associator X Y Z := (α_ (unmop Z) (unmop Y) (unmop X)).symm.mop
  leftUnitor X := (ρ_ (unmop X)).mop
  rightUnitor X := (λ_ (unmop X)).mop
  associator_naturality' := by
    intros
    apply unmop_inj
    simp
  leftUnitor_naturality' := by
    intros
    apply unmop_inj
    simp
  rightUnitor_naturality' := by
    intros
    apply unmop_inj
    simp
  triangle' := by
    intros
    apply unmop_inj
    coherence
  pentagon' := by
    intros
    apply unmop_inj
    coherence
#align category_theory.monoidal_category_mop CategoryTheory.monoidalCategoryMop

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
theorem mop_tensorObj (X Y : Cᴹᵒᵖ) : X ⊗ Y = mop (unmop Y ⊗ unmop X) :=
  rfl
#align category_theory.mop_tensor_obj CategoryTheory.mop_tensorObj

theorem mop_tensorUnit : 𝟙_ Cᴹᵒᵖ = mop (𝟙_ C) :=
  rfl
#align category_theory.mop_tensor_unit CategoryTheory.mop_tensorUnit

end CategoryTheory


universes u

namespace Ex1

structure A (α : Type u) :=
(x : α) (f : α → α := λ x => x)

structure B (α : Type u) extends A α :=
(y : α := f (f x)) (g : α → α → α := λ x y => f x)

structure C (α : Type u) extends B α :=
(z : α := g x y) (x := f z)

end Ex1

open Ex1

def c1 : C Nat := { x := 1 }

#check { c1 with z := 2 }

#check { c1 with z := 2 }

theorem ex1 : { c1 with z := 2 }.z = 2 :=
rfl

#check ex1

theorem ex2 : { c1 with z := 2 }.x = c1.x :=
rfl

#check ex2

def c2 : C (Nat × Nat) := { z := (1, 1) }

#check { c2 with x.fst := 2 }

#check { c2 with x.1 := 3 }


/-
Copyright © 2020 Nicolò Cavalleri. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Nicolò Cavalleri
-/

import geometry.manifold.algebra.monoid

/-!
# Lie groups

A Lie group is a group that is also a smooth manifold, in which the group operations of
multiplication and inversion are smooth maps. Smoothness of the group multiplication means that
multiplication is a smooth mapping of the product manifold `G` × `G` into `G`.

Note that, since a manifold here is not second-countable and Hausdorff a Lie group here is not
guaranteed to be second-countable (even though it can be proved it is Hausdorff). Note also that Lie
groups here are not necessarily finite dimensional.

## Main definitions and statements

* `lie_add_group I G` : a Lie additive group where `G` is a manifold on the model with corners `I`.
* `lie_group I G`     : a Lie multiplicative group where `G` is a manifold on the model with
                        corners `I`.
* `normed_space_lie_add_group` : a normed vector space over a nontrivially normed field
                                 is an additive Lie group.

## Implementation notes

A priori, a Lie group here is a manifold with corners.

The definition of Lie group cannot require `I : model_with_corners 𝕜 E E` with the same space as the
model space and as the model vector space, as one might hope, beause in the product situation,
the model space is `model_prod E E'` and the model vector space is `E × E'`, which are not the same,
so the definition does not apply. Hence the definition should be more general, allowing
`I : model_with_corners 𝕜 E H`.
-/

noncomputable theory

open_locale manifold

/-- A Lie (additive) group is a group and a smooth manifold at the same time in which
the addition and negation operations are smooth. -/
-- See note [Design choices about smooth algebraic structures]
@[ancestor has_smooth_add]
class lie_add_group {𝕜 : Type*} [nontrivially_normed_field 𝕜]
  {H : Type*} [topological_space H]
  {E : Type*} [normed_add_comm_group E] [normed_space 𝕜 E] (I : model_with_corners 𝕜 E H)
  (G : Type*) [add_group G] [topological_space G] [charted_space H G]
  extends has_smooth_add I G : Prop :=
(smooth_neg : smooth I I (λ a:G, -a))

/-- A Lie group is a group and a smooth manifold at the same time in which
the multiplication and inverse operations are smooth. -/
-- See note [Design choices about smooth algebraic structures]
@[ancestor has_smooth_mul, to_additive]
class lie_group {𝕜 : Type*} [nontrivially_normed_field 𝕜]
  {H : Type*} [topological_space H]
  {E : Type*} [normed_add_comm_group E] [normed_space 𝕜 E] (I : model_with_corners 𝕜 E H)
  (G : Type*) [group G] [topological_space G] [charted_space H G]
  extends has_smooth_mul I G : Prop :=
(smooth_inv : smooth I I (λ a:G, a⁻¹))

section lie_group

variables {𝕜 : Type*} [nontrivially_normed_field 𝕜]
{H : Type*} [topological_space H]
{E : Type*} [normed_add_comm_group E] [normed_space 𝕜 E] {I : model_with_corners 𝕜 E H}
{F : Type*} [normed_add_comm_group F] [normed_space 𝕜 F] {J : model_with_corners 𝕜 F F}
{G : Type*} [topological_space G] [charted_space H G] [group G] [lie_group I G]
{E' : Type*} [normed_add_comm_group E'] [normed_space 𝕜 E']
{H' : Type*} [topological_space H'] {I' : model_with_corners 𝕜 E' H'}
{M : Type*} [topological_space M] [charted_space H' M]
{E'' : Type*} [normed_add_comm_group E''] [normed_space 𝕜 E'']
{H'' : Type*} [topological_space H''] {I'' : model_with_corners 𝕜 E'' H''}
{M' : Type*} [topological_space M'] [charted_space H'' M']

section

variable (I)

@[to_additive]
lemma smooth_inv : smooth I I (λ x : G, x⁻¹) :=
lie_group.smooth_inv

/-- A Lie group is a topological group. This is not an instance for technical reasons,
see note [Design choices about smooth algebraic structures]. -/
@[to_additive
"An additive Lie group is an additive topological group. This is not an instance for technical
reasons, see note [Design choices about smooth algebraic structures]."]
lemma topological_group_of_lie_group : topological_group G :=
{ continuous_inv := (smooth_inv I).continuous,
  .. has_continuous_mul_of_smooth I }

end

@[to_additive]
lemma smooth.inv {f : M → G}
  (hf : smooth I' I f) : smooth I' I (λx, (f x)⁻¹) :=
(smooth_inv I).comp hf

@[to_additive]
lemma smooth_on.inv {f : M → G} {s : set M}
  (hf : smooth_on I' I f s) : smooth_on I' I (λx, (f x)⁻¹) s :=
(smooth_inv I).comp_smooth_on hf

@[to_additive]
lemma smooth.div {f g : M → G}
  (hf : smooth I' I f) (hg : smooth I' I g) : smooth I' I (f / g) :=
by { rw div_eq_mul_inv, exact ((smooth_mul I).comp (hf.prod_mk hg.inv) : _), }

@[to_additive]
lemma smooth_on.div {f g : M → G} {s : set M}
  (hf : smooth_on I' I f s) (hg : smooth_on I' I g s) : smooth_on I' I (f / g) s :=
by { rw div_eq_mul_inv, exact ((smooth_mul I).comp_smooth_on (hf.prod_mk hg.inv) : _), }

end lie_group

section prod_lie_group

/- Instance of product group -/
@[to_additive]
instance {𝕜 : Type*} [nontrivially_normed_field 𝕜] {H : Type*} [topological_space H]
  {E : Type*} [normed_add_comm_group E] [normed_space 𝕜 E]  {I : model_with_corners 𝕜 E H}
  {G : Type*} [topological_space G] [charted_space H G] [group G] [lie_group I G]
  {E' : Type*} [normed_add_comm_group E'] [normed_space 𝕜 E']
  {H' : Type*} [topological_space H'] {I' : model_with_corners 𝕜 E' H'}
  {G' : Type*} [topological_space G'] [charted_space H' G']
  [group G'] [lie_group I' G'] :
  lie_group (I.prod I') (G×G') :=
{ smooth_inv := smooth_fst.inv.prod_mk smooth_snd.inv,
  ..has_smooth_mul.prod _ _ _ _ }

end prod_lie_group

/-! ### Normed spaces are Lie groups -/

instance normed_space_lie_add_group {𝕜 : Type*} [nontrivially_normed_field 𝕜]
  {E : Type*} [normed_add_comm_group E] [normed_space 𝕜 E] :
  lie_add_group (𝓘(𝕜, E)) E :=
{ smooth_add := smooth_iff.2 ⟨continuous_add, λ x y, cont_diff_add.cont_diff_on⟩,
  smooth_neg := smooth_iff.2 ⟨continuous_neg, λ x y, cont_diff_neg.cont_diff_on⟩,
  .. model_space_smooth }