/-
The task:
1) Define clone (see Wikipedia https://en.wikipedia.org/wiki/Clone_(algebra) )
2) Proof that in every clone,
  if there is a ternary operation p satisfying p(y,x,x) = p(y,x,y) = p(x,x,y) = y
  then there is a ternary operation m satisfying m(y,x,x) = m(x,y,x) = m(x,x,y) = x
  (hint: m(a,b,c) = p(a,p(a,b,c),c) )

TODO:
1) expand forall conjunction -> conjunction forall by a tactic?
2) tactic for cases of fin
3) use several rewrite rules as long as possible
4) general version of compose3_lemma ()
-/

-- handy way of writing arrays
def list.a {α : Type} (l : list α) : array (l.length) α :=
{ data := λ i, l.nth_le i.val i.is_lt}
#check [2,3,4,5].a
#reduce [2,3,4,5].a

-- ability to map array from one type to another
def array.my_map {α β : Type} {n : ℕ} (f : α → β) (a : array n α) : array n β :=
{data := λ i : fin n, f (a.data i)}

-- lemma for case-analysis on natural numbers
theorem not_lt_cases {n : ℕ} {i_val : ℕ} (h : ¬i_val < n) :
i_val = n ∨ ¬(i_val < nat.succ(n)) :=
begin
have: i_val = n ∨ n < i_val, from nat.eq_or_lt_of_not_lt h,
cases this, left, assumption,
right, have: ¬ i_val ≤ n, from (nat.lt_iff_le_not_le.elim_left this).right,
intro a, revert this, apply non_contradictory_intro,
show i_val ≤ n, from nat.le_of_lt_succ a
end

-- operations, composition, projections
def operation (ar : ℕ) (α : Type) := (array ar α) → α
def compose {α : Type} {n m : ℕ} (f : operation n α)
  (g_tup : array n (operation m α)) :
operation m α :=
  assume input : array m α,
  f (g_tup.my_map (λ g, g input))

constants f : operation 3 ℕ
theorem compose3_lemma {α : Type} {m : ℕ} {f : operation 3 α} {g1 g2 g3 : operation m α}
: compose f [g1, g2, g3].a = λ input, f [g1 input, g2 input, g3 input].a :=
begin
apply funext,
intros,
apply congr_arg f,
apply array.ext,
intros,
cases i,
  by_cases i_val = 0, subst i_val, refl,
  by_cases i_val = 1, subst i_val, refl,
  by_cases i_val = 2, subst i_val, refl,
  have ineq := nat.not_lt_zero i_val,
  iterate 3 {have ineq := not_lt_cases ineq, cases ineq with neq ineq, contradiction},
  contradiction,
end

def projection (α : Type) (n : ℕ) (i : fin n) :
operation n α := λ input, input.read i

-- clone

def operation_set (α : Type) : Type := Π n : ℕ, set (operation n α)

def is_clone {α : Type} (ops : operation_set α) : Prop :=
(∀ n : ℕ, ∀ i : fin n, projection α n i ∈ ops n) ∧
(∀ n m : ℕ,
  ∀ f, f ∈ ops n →
  ∀ g_tup : array n (operation m α),
  (∀ i, g_tup.read i ∈ ops m) →
    compose f g_tup ∈ ops m)

-- testing proposition
theorem clone_prop {α : Type} (ops : operation_set α) (hclone: is_clone ops) :
(∃ (p : operation 3 α), p ∈ ops 3 ∧ ∀ x y,
   p [y,x,x].a = y ∧
   p [y,x,y].a = y ∧
   p [x,x,y].a = y
) → (∃ (m : operation 3 α), m ∈ ops 3 ∧ ∀ x y,
   m [y,x,x].a = x ∧
   m [x,y,x].a = x ∧
   m [x,x,y].a = x
) :=
let π₁ := projection α 3 (⟨0, by comp_val⟩ : fin 3) in
let π₃ := projection α 3 (⟨2, by comp_val⟩ : fin 3) in
begin
intro h,
cases h with p hp,
cases hclone with has_proj has_compositions,
cases hp,
existsi compose p [π₁, p, π₃].a,
constructor,
  -- m is in the clone
  have has_comp_concrete := has_compositions 3 3 p hp_left [π₁, p, π₃].a,
  apply has_comp_concrete,
  intro i, cases i,
  by_cases i_val = 0, subst i_val, show π₁ ∈ ops 3, from has_proj 3 0,
  by_cases i_val = 1, subst i_val, show p ∈ ops 3, by assumption,
  by_cases i_val = 2, subst i_val, show π₃ ∈ ops 3, from has_proj 3 2,
  have ineq := nat.not_lt_zero i_val,
  iterate 3 {have ineq := not_lt_cases ineq, cases ineq with neq ineq, contradiction},
  contradiction,

  -- m satisfies the identities

  -- state the necessary identities
  have p1: ∀ (x y : α), p [y, x, x].a = y, intros, apply (hp_right x y).left,
  have p2: ∀ (x y : α), p [y, x, y].a = y, intros, apply (hp_right x y).right.left,
  have p3: ∀ (x y : α), p [x, x, y].a = y, intros, apply (hp_right x y).right.right,
  have pi1: ∀ (a b c : α), π₁ [a, b, c].a = a, intros, refl,
  have pi3: ∀ (a b c : α), π₃ [a, b, c].a = c, intros, refl,
  intros,
  simp [compose3_lemma],
  -- and apply them
  iterate 10 { /- a bit hacky, how to just rewrite
                  all the (nested) occurences of (p1 p2 p3 pi1 pi3)? -/
    try {rw p1}, try {rw p2}, try {rw p3}, try {rw pi1}, try {rw pi3},
  },
  simp,
end


/-
Copyright (c) 2019 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.multiset.nodup
import Mathlib.data.list.intervals
import Mathlib.PostPort

namespace Mathlib

/-!
# Intervals in ℕ as multisets

For now this only covers `Ico n m`, the "closed-open" interval containing `[n, ..., m-1]`.
-/

namespace multiset

/-! ### Ico -/

/-- `Ico n m` is the multiset lifted from the list `Ico n m`, e.g. the set `{n, n+1, ..., m-1}`. -/
def Ico (n : ℕ) (m : ℕ) : multiset ℕ := ↑(list.Ico n m)

namespace Ico

theorem map_add (n : ℕ) (m : ℕ) (k : ℕ) : map (Add.add k) (Ico n m) = Ico (n + k) (m + k) :=
  congr_arg coe (list.Ico.map_add n m k)

theorem map_sub (n : ℕ) (m : ℕ) (k : ℕ) (h : k ≤ n) :
    map (fun (x : ℕ) => x - k) (Ico n m) = Ico (n - k) (m - k) :=
  congr_arg coe (list.Ico.map_sub n m k h)

theorem zero_bot (n : ℕ) : Ico 0 n = range n := congr_arg coe (list.Ico.zero_bot n)

@[simp] theorem card (n : ℕ) (m : ℕ) : coe_fn card (Ico n m) = m - n := list.Ico.length n m

theorem nodup (n : ℕ) (m : ℕ) : nodup (Ico n m) := list.Ico.nodup n m

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

theorem eq_zero_of_le {n : ℕ} {m : ℕ} (h : m ≤ n) : Ico n m = 0 :=
  congr_arg coe (list.Ico.eq_nil_of_le h)

@[simp] theorem self_eq_zero {n : ℕ} : Ico n n = 0 := eq_zero_of_le (le_refl n)

@[simp] theorem eq_zero_iff {n : ℕ} {m : ℕ} : Ico n m = 0 ↔ m ≤ n :=
  iff.trans (coe_eq_zero (list.Ico n m)) list.Ico.eq_empty_iff

theorem add_consecutive {n : ℕ} {m : ℕ} {l : ℕ} (hnm : n ≤ m) (hml : m ≤ l) :
    Ico n m + Ico m l = Ico n l :=
  congr_arg coe (list.Ico.append_consecutive hnm hml)

@[simp] theorem inter_consecutive (n : ℕ) (m : ℕ) (l : ℕ) : Ico n m ∩ Ico m l = 0 :=
  congr_arg coe (list.Ico.bag_inter_consecutive n m l)

@[simp] theorem succ_singleton {n : ℕ} : Ico n (n + 1) = singleton n :=
  congr_arg coe list.Ico.succ_singleton

theorem succ_top {n : ℕ} {m : ℕ} (h : n ≤ m) : Ico n (m + 1) = m ::ₘ Ico n m := sorry

theorem eq_cons {n : ℕ} {m : ℕ} (h : n < m) : Ico n m = n ::ₘ Ico (n + 1) m :=
  congr_arg coe (list.Ico.eq_cons h)

@[simp] theorem pred_singleton {m : ℕ} (h : 0 < m) : Ico (m - 1) m = singleton (m - 1) :=
  congr_arg coe (list.Ico.pred_singleton h)

@[simp] theorem not_mem_top {n : ℕ} {m : ℕ} : ¬m ∈ Ico n m := list.Ico.not_mem_top

theorem filter_lt_of_top_le {n : ℕ} {m : ℕ} {l : ℕ} (hml : m ≤ l) :
    filter (fun (x : ℕ) => x < l) (Ico n m) = Ico n m :=
  congr_arg coe (list.Ico.filter_lt_of_top_le hml)

theorem filter_lt_of_le_bot {n : ℕ} {m : ℕ} {l : ℕ} (hln : l ≤ n) :
    filter (fun (x : ℕ) => x < l) (Ico n m) = ∅ :=
  congr_arg coe (list.Ico.filter_lt_of_le_bot hln)

theorem filter_le_of_bot {n : ℕ} {m : ℕ} (hnm : n < m) :
    filter (fun (x : ℕ) => x ≤ n) (Ico n m) = singleton n :=
  congr_arg coe (list.Ico.filter_le_of_bot hnm)

theorem filter_lt_of_ge {n : ℕ} {m : ℕ} {l : ℕ} (hlm : l ≤ m) :
    filter (fun (x : ℕ) => x < l) (Ico n m) = Ico n l :=
  congr_arg coe (list.Ico.filter_lt_of_ge hlm)

@[simp] theorem filter_lt (n : ℕ) (m : ℕ) (l : ℕ) :
    filter (fun (x : ℕ) => x < l) (Ico n m) = Ico n (min m l) :=
  congr_arg coe (list.Ico.filter_lt n m l)

theorem filter_le_of_le_bot {n : ℕ} {m : ℕ} {l : ℕ} (hln : l ≤ n) :
    filter (fun (x : ℕ) => l ≤ x) (Ico n m) = Ico n m :=
  congr_arg coe (list.Ico.filter_le_of_le_bot hln)

theorem filter_le_of_top_le {n : ℕ} {m : ℕ} {l : ℕ} (hml : m ≤ l) :
    filter (fun (x : ℕ) => l ≤ x) (Ico n m) = ∅ :=
  congr_arg coe (list.Ico.filter_le_of_top_le hml)

theorem filter_le_of_le {n : ℕ} {m : ℕ} {l : ℕ} (hnl : n ≤ l) :
    filter (fun (x : ℕ) => l ≤ x) (Ico n m) = Ico l m :=
  congr_arg coe (list.Ico.filter_le_of_le hnl)

@[simp] theorem filter_le (n : ℕ) (m : ℕ) (l : ℕ) :
    filter (fun (x : ℕ) => l ≤ x) (Ico n m) = Ico (max n l) m :=
  congr_arg coe (list.Ico.filter_le n m l)

end Mathlib


/-
Copyright (c) 2021 Heather Macbeth. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Heather Macbeth
-/
import analysis.normed_space.add_torsor
import analysis.normed_space.linear_isometry

/-!
# Affine isometries

In this file we define `affine_isometry 𝕜 P P₂` to be an affine isometric embedding of normed
add-torsors `P` into `P₂` over normed `𝕜`-spaces and `affine_isometry_equiv` to be an affine
isometric equivalence between `P` and `P₂`.

We also prove basic lemmas and provide convenience constructors.  The choice of these lemmas and
constructors is closely modelled on those for the `linear_isometry` and `affine_map` theories.

Since many elementary properties don't require `∥x∥ = 0 → x = 0` we initially set up the theory for
`semi_normed_group` and specialize to `normed_group` only when needed.

## Notation

We introduce the notation `P →ᵃⁱ[𝕜] P₂` for `affine_isometry 𝕜 P P₂`, and `P ≃ᵃⁱ[𝕜] P₂` for
`affine_isometry_equiv 𝕜 P P₂`.  In contrast with the notation `→ₗᵢ` for linear isometries, `≃ᵢ`
for isometric equivalences, etc., the "i" here is a superscript.  This is for aesthetic reasons to
match the superscript "a" (note that in mathlib `→ᵃ` is an affine map, since `→ₐ` has been taken by
algebra-homomorphisms.)

-/
open function set

variables (𝕜 : Type*) {V V₁ V₂ V₃ V₄ : Type*} {P₁ : Type*} (P P₂ : Type*) {P₃ P₄ : Type*}
    [normed_field 𝕜]
  [semi_normed_group V] [semi_normed_group V₁] [semi_normed_group V₂] [semi_normed_group V₃]
    [semi_normed_group V₄]
  [normed_space 𝕜 V] [normed_space 𝕜 V₁] [normed_space 𝕜 V₂] [normed_space 𝕜 V₃]
    [normed_space 𝕜 V₄]
  [pseudo_metric_space P] [metric_space P₁] [pseudo_metric_space P₂] [pseudo_metric_space P₃]
    [pseudo_metric_space P₄]
  [normed_add_torsor V P] [normed_add_torsor V₁ P₁] [normed_add_torsor V₂ P₂]
    [normed_add_torsor V₃ P₃] [normed_add_torsor V₄ P₄]

include V V₂

/-- An `𝕜`-affine isometric embedding of one normed add-torsor over a normed `𝕜`-space into
another. -/
structure affine_isometry extends P →ᵃ[𝕜] P₂ :=
(norm_map : ∀ x : V, ∥linear x∥ = ∥x∥)

omit V V₂
variables {𝕜 P P₂}

-- `→ᵃᵢ` would be more consistent with the linear isometry notation, but it is uglier
notation P ` →ᵃⁱ[`:25 𝕜:25 `] `:0 P₂:0 := affine_isometry 𝕜 P P₂

namespace affine_isometry
variables (f : P →ᵃⁱ[𝕜] P₂)

/-- The underlying linear map of an affine isometry is in fact a linear isometry. -/
protected def linear_isometry : V →ₗᵢ[𝕜] V₂ :=
{ norm_map' := f.norm_map,
  .. f.linear }

@[simp] lemma linear_eq_linear_isometry : f.linear = f.linear_isometry.to_linear_map :=
by { ext, refl }

include V V₂
instance : has_coe_to_fun (P →ᵃⁱ[𝕜] P₂) (λ _, P → P₂) := ⟨λ f, f.to_fun⟩
omit V V₂

@[simp] lemma coe_to_affine_map : ⇑f.to_affine_map = f := rfl

include V V₂
lemma to_affine_map_injective : injective (to_affine_map : (P →ᵃⁱ[𝕜] P₂) → (P →ᵃ[𝕜] P₂))
| ⟨f, _⟩ ⟨g, _⟩ rfl := rfl

lemma coe_fn_injective : @injective (P →ᵃⁱ[𝕜] P₂) (P → P₂) coe_fn :=
affine_map.coe_fn_injective.comp to_affine_map_injective

@[ext] lemma ext {f g : P →ᵃⁱ[𝕜] P₂} (h : ∀ x, f x = g x) : f = g :=
coe_fn_injective $ funext h
omit V V₂

end affine_isometry

namespace linear_isometry
variables (f : V →ₗᵢ[𝕜] V₂)

/-- Reinterpret a linear isometry as an affine isometry. -/
def to_affine_isometry : V →ᵃⁱ[𝕜] V₂ :=
{ norm_map := f.norm_map,
  .. f.to_linear_map.to_affine_map }

@[simp] lemma coe_to_affine_isometry : ⇑(f.to_affine_isometry : V →ᵃⁱ[𝕜] V₂) = f := rfl

@[simp] lemma to_affine_isometry_linear_isometry : f.to_affine_isometry.linear_isometry = f :=
by { ext, refl }

-- somewhat arbitrary choice of simp direction
@[simp] lemma to_affine_isometry_to_affine_map :
  f.to_affine_isometry.to_affine_map = f.to_linear_map.to_affine_map :=
rfl

end linear_isometry

namespace affine_isometry

/-- We use `f₁` when we need the domain to be a `normed_space`. -/
variables (f : P →ᵃⁱ[𝕜] P₂) (f₁ : P₁ →ᵃⁱ[𝕜] P₂)

@[simp] lemma map_vadd (p : P) (v : V) : f (v +ᵥ p) = f.linear_isometry v +ᵥ f p :=
f.to_affine_map.map_vadd p v

@[simp] lemma map_vsub (p1 p2 : P) : f.linear_isometry (p1 -ᵥ p2) = f p1 -ᵥ f p2 :=
f.to_affine_map.linear_map_vsub p1 p2

@[simp] lemma dist_map (x y : P) : dist (f x) (f y) = dist x y :=
by rw [dist_eq_norm_vsub V₂, dist_eq_norm_vsub V, ← map_vsub, f.linear_isometry.norm_map]

@[simp] lemma nndist_map (x y : P) : nndist (f x) (f y) = nndist x y := by simp [nndist_dist]

@[simp] lemma edist_map (x y : P) : edist (f x) (f y) = edist x y := by simp [edist_dist]

protected lemma isometry : isometry f := f.edist_map

protected lemma injective : injective f₁ := f₁.isometry.injective

@[simp] lemma map_eq_iff {x y : P₁} : f₁ x = f₁ y ↔ x = y := f₁.injective.eq_iff

lemma map_ne {x y : P₁} (h : x ≠ y) : f₁ x ≠ f₁ y := f₁.injective.ne h

protected lemma lipschitz : lipschitz_with 1 f := f.isometry.lipschitz

protected lemma antilipschitz : antilipschitz_with 1 f := f.isometry.antilipschitz

@[continuity] protected lemma continuous : continuous f := f.isometry.continuous

lemma ediam_image (s : set P) : emetric.diam (f '' s) = emetric.diam s :=
f.isometry.ediam_image s

lemma ediam_range : emetric.diam (range f) = emetric.diam (univ : set P) :=
f.isometry.ediam_range

lemma diam_image (s : set P) : metric.diam (f '' s) = metric.diam s :=
f.isometry.diam_image s

lemma diam_range : metric.diam (range f) = metric.diam (univ : set P) :=
f.isometry.diam_range

@[simp] lemma comp_continuous_iff {α : Type*} [topological_space α] {g : α → P} :
  continuous (f ∘ g) ↔ continuous g :=
f.isometry.comp_continuous_iff

include V
/-- The identity affine isometry. -/
def id : P →ᵃⁱ[𝕜] P := ⟨affine_map.id 𝕜 P, λ x, rfl⟩

@[simp] lemma coe_id : ⇑(id : P →ᵃⁱ[𝕜] P) = _root_.id := rfl

@[simp] lemma id_apply (x : P) : (affine_isometry.id : P →ᵃⁱ[𝕜] P) x = x := rfl

@[simp] lemma id_to_affine_map : (id.to_affine_map : P →ᵃ[𝕜] P) = affine_map.id 𝕜 P := rfl

instance : inhabited (P →ᵃⁱ[𝕜] P) := ⟨id⟩

include V₂ V₃
/-- Composition of affine isometries. -/
def comp (g : P₂ →ᵃⁱ[𝕜] P₃) (f : P →ᵃⁱ[𝕜] P₂) : P →ᵃⁱ[𝕜] P₃ :=
⟨g.to_affine_map.comp f.to_affine_map, λ x, (g.norm_map _).trans (f.norm_map _)⟩

@[simp] lemma coe_comp (g : P₂ →ᵃⁱ[𝕜] P₃) (f : P →ᵃⁱ[𝕜] P₂) :
  ⇑(g.comp f) = g ∘ f :=
rfl

omit V V₂ V₃

@[simp] lemma id_comp : (id : P₂ →ᵃⁱ[𝕜] P₂).comp f = f := ext $ λ x, rfl

@[simp] lemma comp_id : f.comp id = f := ext $ λ x, rfl

include V V₂ V₃ V₄
lemma comp_assoc (f : P₃ →ᵃⁱ[𝕜] P₄) (g : P₂ →ᵃⁱ[𝕜] P₃) (h : P →ᵃⁱ[𝕜] P₂) :
  (f.comp g).comp h = f.comp (g.comp h) :=
rfl
omit V₂ V₃ V₄

instance : monoid (P →ᵃⁱ[𝕜] P) :=
{ one := id,
  mul := comp,
  mul_assoc := comp_assoc,
  one_mul := id_comp,
  mul_one := comp_id }

@[simp] lemma coe_one : ⇑(1 : P →ᵃⁱ[𝕜] P) = _root_.id := rfl
@[simp] lemma coe_mul (f g : P →ᵃⁱ[𝕜] P) : ⇑(f * g) = f ∘ g := rfl

end affine_isometry

-- remark: by analogy with the `linear_isometry` file from which this is adapted, there should
-- follow here a section defining an "inclusion" affine isometry from `p : affine_subspace 𝕜 P`
-- into `P`; we omit this for now

variables (𝕜 P P₂)
include V V₂

/-- A affine isometric equivalence between two normed vector spaces. -/
structure affine_isometry_equiv extends P ≃ᵃ[𝕜] P₂ :=
(norm_map : ∀ x, ∥linear x∥ = ∥x∥)

variables {𝕜 P P₂}
omit V V₂

-- `≃ᵃᵢ` would be more consistent with the linear isometry equiv notation, but it is uglier
notation P ` ≃ᵃⁱ[`:25 𝕜:25 `] `:0 P₂:0 := affine_isometry_equiv 𝕜 P P₂

namespace affine_isometry_equiv

variables (e : P ≃ᵃⁱ[𝕜] P₂)

/-- The underlying linear equiv of an affine isometry equiv is in fact a linear isometry equiv. -/
protected def linear_isometry_equiv : V ≃ₗᵢ[𝕜] V₂ :=
{ norm_map' := e.norm_map,
  .. e.linear }

@[simp] lemma linear_eq_linear_isometry : e.linear = e.linear_isometry_equiv.to_linear_equiv :=
by { ext, refl }

include V V₂
instance : has_coe_to_fun (P ≃ᵃⁱ[𝕜] P₂) (λ _, P → P₂) := ⟨λ f, f.to_fun⟩

@[simp] lemma coe_mk (e : P ≃ᵃ[𝕜] P₂) (he : ∀ x, ∥e.linear x∥ = ∥x∥) :
  ⇑(mk e he) = e :=
rfl

@[simp] lemma coe_to_affine_equiv (e : P ≃ᵃⁱ[𝕜] P₂) : ⇑e.to_affine_equiv = e := rfl

lemma to_affine_equiv_injective : injective (to_affine_equiv : (P ≃ᵃⁱ[𝕜] P₂) → (P ≃ᵃ[𝕜] P₂))
| ⟨e, _⟩ ⟨_, _⟩ rfl := rfl

@[ext] lemma ext {e e' : P ≃ᵃⁱ[𝕜] P₂} (h : ∀ x, e x = e' x) : e = e' :=
to_affine_equiv_injective $ affine_equiv.ext h
omit V V₂

/-- Reinterpret a `affine_isometry_equiv` as a `affine_isometry`. -/
def to_affine_isometry : P →ᵃⁱ[𝕜] P₂ := ⟨e.1.to_affine_map, e.2⟩

@[simp] lemma coe_to_affine_isometry : ⇑e.to_affine_isometry = e := rfl

/-- Construct an affine isometry equivalence by verifying the relation between the map and its
linear part at one base point. Namely, this function takes a map `e : P₁ → P₂`, a linear isometry
equivalence `e' : V₁ ≃ᵢₗ[k] V₂`, and a point `p` such that for any other point `p'` we have
`e p' = e' (p' -ᵥ p) +ᵥ e p`. -/
def mk' (e : P₁ → P₂) (e' : V₁ ≃ₗᵢ[𝕜] V₂) (p : P₁) (h : ∀ p' : P₁, e p' = e' (p' -ᵥ p) +ᵥ e p) :
  P₁ ≃ᵃⁱ[𝕜] P₂ :=
{ norm_map := e'.norm_map,
  .. affine_equiv.mk' e e'.to_linear_equiv p h }

@[simp] lemma coe_mk' (e : P₁ → P₂) (e' : V₁ ≃ₗᵢ[𝕜] V₂) (p h) : ⇑(mk' e e' p h) = e := rfl
@[simp] lemma linear_isometry_equiv_mk' (e : P₁ → P₂) (e' : V₁ ≃ₗᵢ[𝕜] V₂) (p h) :
  (mk' e e' p h).linear_isometry_equiv = e' := by { ext, refl }

end affine_isometry_equiv

namespace linear_isometry_equiv
variables (e : V ≃ₗᵢ[𝕜] V₂)

/-- Reinterpret a linear isometry equiv as an affine isometry equiv. -/
def to_affine_isometry_equiv  : V ≃ᵃⁱ[𝕜] V₂ :=
{ norm_map := e.norm_map,
  .. e.to_linear_equiv.to_affine_equiv }

@[simp] lemma coe_to_affine_isometry_equiv : ⇑(e.to_affine_isometry_equiv : V ≃ᵃⁱ[𝕜] V₂) = e := rfl

@[simp] lemma to_affine_isometry_equiv_linear_isometry_equiv :
  e.to_affine_isometry_equiv.linear_isometry_equiv = e :=
by { ext, refl }

-- somewhat arbitrary choice of simp direction
@[simp] lemma to_affine_isometry_equiv_to_affine_equiv :
  e.to_affine_isometry_equiv.to_affine_equiv = e.to_linear_equiv.to_affine_equiv :=
rfl

-- somewhat arbitrary choice of simp direction
@[simp] lemma to_affine_isometry_equiv_to_affine_isometry :
  e.to_affine_isometry_equiv.to_affine_isometry = e.to_linear_isometry.to_affine_isometry :=
rfl

end linear_isometry_equiv

namespace affine_isometry_equiv
variables (e : P ≃ᵃⁱ[𝕜] P₂)

protected lemma isometry : isometry e := e.to_affine_isometry.isometry

/-- Reinterpret a `affine_isometry_equiv` as an `isometric`. -/
def to_isometric : P ≃ᵢ P₂ := ⟨e.to_affine_equiv.to_equiv, e.isometry⟩

@[simp] lemma coe_to_isometric : ⇑e.to_isometric = e := rfl

include V V₂
lemma range_eq_univ (e : P ≃ᵃⁱ[𝕜] P₂) : set.range e = set.univ :=
by { rw ← coe_to_isometric, exact isometric.range_eq_univ _, }
omit V V₂

/-- Reinterpret a `affine_isometry_equiv` as an `homeomorph`. -/
def to_homeomorph : P ≃ₜ P₂ := e.to_isometric.to_homeomorph

@[simp] lemma coe_to_homeomorph : ⇑e.to_homeomorph = e := rfl

protected lemma continuous : continuous e := e.isometry.continuous
protected lemma continuous_at {x} : continuous_at e x := e.continuous.continuous_at
protected lemma continuous_on {s} : continuous_on e s := e.continuous.continuous_on

protected lemma continuous_within_at {s x} : continuous_within_at e s x :=
e.continuous.continuous_within_at

variables (𝕜 P)
include V

/-- Identity map as a `affine_isometry_equiv`. -/
def refl : P ≃ᵃⁱ[𝕜] P := ⟨affine_equiv.refl 𝕜 P, λ x, rfl⟩

variables {𝕜 P}

instance : inhabited (P ≃ᵃⁱ[𝕜] P) := ⟨refl 𝕜 P⟩

@[simp] lemma coe_refl : ⇑(refl 𝕜 P) = id := rfl
@[simp] lemma to_affine_equiv_refl : (refl 𝕜 P).to_affine_equiv = affine_equiv.refl 𝕜 P := rfl
@[simp] lemma to_isometric_refl : (refl 𝕜 P).to_isometric = isometric.refl P := rfl
@[simp] lemma to_homeomorph_refl : (refl 𝕜 P).to_homeomorph = homeomorph.refl P := rfl
omit V

/-- The inverse `affine_isometry_equiv`. -/
def symm : P₂ ≃ᵃⁱ[𝕜] P :=
{ norm_map := e.linear_isometry_equiv.symm.norm_map,
  .. e.to_affine_equiv.symm }

@[simp] lemma apply_symm_apply (x : P₂) : e (e.symm x) = x := e.to_affine_equiv.apply_symm_apply x
@[simp] lemma symm_apply_apply (x : P) : e.symm (e x) = x := e.to_affine_equiv.symm_apply_apply x
@[simp] lemma symm_symm : e.symm.symm = e := ext $ λ x, rfl

@[simp] lemma to_affine_equiv_symm : e.to_affine_equiv.symm = e.symm.to_affine_equiv := rfl
@[simp] lemma to_isometric_symm : e.to_isometric.symm = e.symm.to_isometric := rfl
@[simp] lemma to_homeomorph_symm : e.to_homeomorph.symm = e.symm.to_homeomorph := rfl

include V₃
/-- Composition of `affine_isometry_equiv`s as a `affine_isometry_equiv`. -/
def trans (e' : P₂ ≃ᵃⁱ[𝕜] P₃) : P ≃ᵃⁱ[𝕜] P₃ :=
⟨e.to_affine_equiv.trans e'.to_affine_equiv, λ x, (e'.norm_map _).trans (e.norm_map _)⟩

include V V₂
@[simp] lemma coe_trans (e₁ : P ≃ᵃⁱ[𝕜] P₂) (e₂ : P₂ ≃ᵃⁱ[𝕜] P₃) : ⇑(e₁.trans e₂) = e₂ ∘ e₁ := rfl
omit V V₂ V₃

@[simp] lemma trans_refl : e.trans (refl 𝕜 P₂) = e := ext $ λ x, rfl
@[simp] lemma refl_trans : (refl 𝕜 P).trans e = e := ext $ λ x, rfl
@[simp] lemma self_trans_symm : e.trans e.symm = refl 𝕜 P := ext e.symm_apply_apply
@[simp] lemma symm_trans_self : e.symm.trans e = refl 𝕜 P₂ := ext e.apply_symm_apply

include V V₂ V₃
@[simp] lemma coe_symm_trans (e₁ : P ≃ᵃⁱ[𝕜] P₂) (e₂ : P₂ ≃ᵃⁱ[𝕜] P₃) :
  ⇑(e₁.trans e₂).symm = e₁.symm ∘ e₂.symm :=
rfl

include V₄
lemma trans_assoc (ePP₂ : P ≃ᵃⁱ[𝕜] P₂) (eP₂G : P₂ ≃ᵃⁱ[𝕜] P₃) (eGG' : P₃ ≃ᵃⁱ[𝕜] P₄) :
  ePP₂.trans (eP₂G.trans eGG') = (ePP₂.trans eP₂G).trans eGG' :=
rfl
omit V₂ V₃ V₄

/-- The group of affine isometries of a `normed_add_torsor`, `P`. -/
instance : group (P ≃ᵃⁱ[𝕜] P) :=
{ mul := λ e₁ e₂, e₂.trans e₁,
  one := refl _ _,
  inv := symm,
  one_mul := trans_refl,
  mul_one := refl_trans,
  mul_assoc := λ _ _ _, trans_assoc _ _ _,
  mul_left_inv := self_trans_symm }

@[simp] lemma coe_one : ⇑(1 : P ≃ᵃⁱ[𝕜] P) = id := rfl
@[simp] lemma coe_mul (e e' : P ≃ᵃⁱ[𝕜] P) : ⇑(e * e') = e ∘ e' := rfl
@[simp] lemma coe_inv (e : P ≃ᵃⁱ[𝕜] P) : ⇑(e⁻¹) = e.symm := rfl

omit V

@[simp] lemma map_vadd (p : P) (v : V) : e (v +ᵥ p) = e.linear_isometry_equiv v +ᵥ e p :=
e.to_affine_isometry.map_vadd p v

@[simp] lemma map_vsub (p1 p2 : P) : e.linear_isometry_equiv (p1 -ᵥ p2) = e p1 -ᵥ e p2 :=
e.to_affine_isometry.map_vsub p1 p2

@[simp] lemma dist_map (x y : P) : dist (e x) (e y) = dist x y :=
e.to_affine_isometry.dist_map x y

@[simp] lemma edist_map (x y : P) : edist (e x) (e y) = edist x y :=
e.to_affine_isometry.edist_map x y

protected lemma bijective : bijective e := e.1.bijective
protected lemma injective : injective e := e.1.injective
protected lemma surjective : surjective e := e.1.surjective

@[simp] lemma map_eq_iff {x y : P} : e x = e y ↔ x = y := e.injective.eq_iff

lemma map_ne {x y : P} (h : x ≠ y) : e x ≠ e y := e.injective.ne h

protected lemma lipschitz : lipschitz_with 1 e := e.isometry.lipschitz

protected lemma antilipschitz : antilipschitz_with 1 e := e.isometry.antilipschitz

@[simp] lemma ediam_image (s : set P) : emetric.diam (e '' s) = emetric.diam s :=
e.isometry.ediam_image s

@[simp] lemma diam_image (s : set P) : metric.diam (e '' s) = metric.diam s :=
e.isometry.diam_image s

variables {α : Type*} [topological_space α]

@[simp] lemma comp_continuous_on_iff {f : α → P} {s : set α} :
  continuous_on (e ∘ f) s ↔ continuous_on f s :=
e.isometry.comp_continuous_on_iff

@[simp] lemma comp_continuous_iff {f : α → P} :
  continuous (e ∘ f) ↔ continuous f :=
e.isometry.comp_continuous_iff

section constructions

variables (𝕜)
/-- The map `v ↦ v +ᵥ p` as an affine isometric equivalence between `V` and `P`. -/
def vadd_const (p : P) : V ≃ᵃⁱ[𝕜] P :=
{ norm_map := λ x, rfl,
  .. affine_equiv.vadd_const 𝕜 p }
variables {𝕜}

include V
@[simp] lemma coe_vadd_const (p : P) : ⇑(vadd_const 𝕜 p) = λ v, v +ᵥ p := rfl

@[simp] lemma coe_vadd_const_symm (p : P) : ⇑(vadd_const 𝕜 p).symm = λ p', p' -ᵥ p :=
rfl

@[simp] lemma vadd_const_to_affine_equiv (p : P) :
  (vadd_const 𝕜 p).to_affine_equiv = affine_equiv.vadd_const 𝕜 p :=
rfl
omit V

variables (𝕜)
/-- `p' ↦ p -ᵥ p'` as an affine isometric equivalence. -/
def const_vsub (p : P) : P ≃ᵃⁱ[𝕜] V :=
{ norm_map := norm_neg,
  .. affine_equiv.const_vsub 𝕜 p }
variables {𝕜}

include V
@[simp] lemma coe_const_vsub (p : P) : ⇑(const_vsub 𝕜 p) = (-ᵥ) p := rfl

@[simp] lemma symm_const_vsub (p : P) :
  (const_vsub 𝕜 p).symm
  = (linear_isometry_equiv.neg 𝕜).to_affine_isometry_equiv.trans (vadd_const 𝕜 p) :=
by { ext, refl }
omit V

variables (𝕜 P)
/-- Translation by `v` (that is, the map `p ↦ v +ᵥ p`) as an affine isometric automorphism of `P`.
-/
def const_vadd (v : V) : P ≃ᵃⁱ[𝕜] P :=
{ norm_map := λ x, rfl,
  .. affine_equiv.const_vadd 𝕜 P v }
variables {𝕜 P}

@[simp] lemma coe_const_vadd (v : V) : ⇑(const_vadd 𝕜 P v : P ≃ᵃⁱ[𝕜] P) = (+ᵥ) v := rfl

@[simp] lemma const_vadd_zero : const_vadd 𝕜 P (0:V) = refl 𝕜 P := ext $ zero_vadd V

include 𝕜 V
/-- The map `g` from `V` to `V₂` corresponding to a map `f` from `P` to `P₂`, at a base point `p`,
is an isometry if `f` is one. -/
lemma vadd_vsub {f : P → P₂} (hf : isometry f) {p : P} {g : V → V₂}
  (hg : ∀ v, g v = f (v +ᵥ p) -ᵥ f p) : isometry g :=
begin
  convert (vadd_const 𝕜 (f p)).symm.isometry.comp (hf.comp (vadd_const 𝕜 p).isometry),
  exact funext hg
end
omit 𝕜

variables (𝕜)
/-- Point reflection in `x` as an affine isometric automorphism. -/
def point_reflection (x : P) : P ≃ᵃⁱ[𝕜] P := (const_vsub 𝕜 x).trans (vadd_const 𝕜 x)
variables {𝕜}

lemma point_reflection_apply (x y : P) : (point_reflection 𝕜 x) y = x -ᵥ y +ᵥ x := rfl

@[simp] lemma point_reflection_to_affine_equiv (x : P) :
  (point_reflection 𝕜 x).to_affine_equiv = affine_equiv.point_reflection 𝕜 x := rfl

@[simp] lemma point_reflection_self (x : P) : point_reflection 𝕜 x x = x :=
affine_equiv.point_reflection_self 𝕜 x

lemma point_reflection_involutive (x : P) : function.involutive (point_reflection 𝕜 x) :=
equiv.point_reflection_involutive x

@[simp] lemma point_reflection_symm (x : P) : (point_reflection 𝕜 x).symm = point_reflection 𝕜 x :=
to_affine_equiv_injective $ affine_equiv.point_reflection_symm 𝕜 x

@[simp] lemma dist_point_reflection_fixed (x y : P) :
  dist (point_reflection 𝕜 x y) x = dist y x :=
by rw [← (point_reflection 𝕜 x).dist_map y x, point_reflection_self]

lemma dist_point_reflection_self' (x y : P) :
  dist (point_reflection 𝕜 x y) y = ∥bit0 (x -ᵥ y)∥ :=
by rw [point_reflection_apply, dist_eq_norm_vsub V, vadd_vsub_assoc, bit0]

lemma dist_point_reflection_self (x y : P) :
  dist (point_reflection 𝕜 x y) y = ∥(2:𝕜)∥ * dist x y :=
by rw [dist_point_reflection_self', ← two_smul' 𝕜 (x -ᵥ y), norm_smul, ← dist_eq_norm_vsub V]

lemma point_reflection_fixed_iff [invertible (2:𝕜)] {x y : P} :
  point_reflection 𝕜 x y = y ↔ y = x :=
affine_equiv.point_reflection_fixed_iff_of_module 𝕜

variables [normed_space ℝ V]

lemma dist_point_reflection_self_real (x y : P) :
  dist (point_reflection ℝ x y) y = 2 * dist x y :=
by { rw [dist_point_reflection_self, real.norm_two] }

@[simp] lemma point_reflection_midpoint_left (x y : P) :
  point_reflection ℝ (midpoint ℝ x y) x = y :=
affine_equiv.point_reflection_midpoint_left x y

@[simp] lemma point_reflection_midpoint_right (x y : P) :
  point_reflection ℝ (midpoint ℝ x y) y = x :=
affine_equiv.point_reflection_midpoint_right x y

end constructions

end affine_isometry_equiv

include V V₂

/-- If `f` is an affine map, then its linear part is continuous iff `f` is continuous. -/
lemma affine_map.continuous_linear_iff {f : P →ᵃ[𝕜] P₂} :
  continuous f.linear ↔ continuous f :=
begin
  inhabit P,
  have : (f.linear : V → V₂) =
    (affine_isometry_equiv.vadd_const 𝕜 $ f default).to_homeomorph.symm ∘ f ∘
      (affine_isometry_equiv.vadd_const 𝕜 default).to_homeomorph,
  { ext v, simp },
  rw this,
  simp only [homeomorph.comp_continuous_iff, homeomorph.comp_continuous_iff'],
end

/-- If `f` is an affine map, then its linear part is an open map iff `f` is an open map. -/
lemma affine_map.is_open_map_linear_iff {f : P →ᵃ[𝕜] P₂} :
  is_open_map f.linear ↔ is_open_map f :=
begin
  inhabit P,
  have : (f.linear : V → V₂) =
    (affine_isometry_equiv.vadd_const 𝕜 $ f default).to_homeomorph.symm ∘ f ∘
      (affine_isometry_equiv.vadd_const 𝕜 default).to_homeomorph,
  { ext v, simp },
  rw this,
  simp only [homeomorph.comp_is_open_map_iff, homeomorph.comp_is_open_map_iff'],
end


import prelim.embed prelim.minmax set_tactic.solver tactic.derive_fintype
import matroid.rankfun matroid.dual .projection .matroid_in .delete_contract

open_locale classical
noncomputable theory

open matroid set

namespace matroid_in

variables {α β : Type*} [fintype α] [fintype β]

section minor

/- a structure whose existence certifies that N is a minor of M -/
@[ext] structure minor_pair (N M : matroid_in α) extends cd_pair M :=
(minor : M / C \ D = N)

instance coe_to_cd_pair (N M : matroid_in α) : has_coe (minor_pair N M) (cd_pair M) :=
⟨λ x, x.to_cd_pair⟩

@[simp] lemma to_cd_pair_as_coe {N M : matroid_in α} (p : minor_pair M N) :
  p.to_cd_pair = coe p :=
rfl

/-- minor relation on matroid_in α-/
def is_minor (N M : matroid_in α) : Prop :=
  nonempty (minor_pair N M)

def is_proper_minor (N M : matroid_in α) : Prop :=
  is_minor N M ∧ N ≠ M

def is_restriction (N M : matroid_in α) : Prop :=
  ∃ P: minor_pair N M, P.C = ∅

def is_contraction_minor (N M : matroid_in α) : Prop :=
  ∃ P: minor_pair N M, P.D = ∅

/-- constructs a minor pair from contract/delete sets C and D  -/
def to_minor_pair (M : matroid_in α) (C D : set α) :
  minor_pair (M / C \ D) M :=
{ C := M.E ∩ C ,
  D := (M.E ∩ D) \ C,
  disj := by {ext, simp, tauto, },
  C_ss_E := inter_subset_left _ _,
  D_ss_E := subset.trans (diff_subset _ _) (inter_subset_left _ _),
  minor := by {
    dsimp, rw [← con_eq_con_inter_E],
    nth_rewrite 1 [del_eq_del_inter_E],
    simp [diff_eq, inter_right_comm], }}

namespace minor_pair

variables {N M : matroid_in α} (mp : minor_pair N M)

/-- given a minor pair N M, returns the corresponding minor_pair in M.as_mat -/
def minor_pair_to_as_mat (P : minor_pair N M) :
    minor_pair ((M.as_mat : matroid_in M.E) / (coe ⁻¹' P.C) \ (coe ⁻¹' P.D)) M.as_mat :=
{  minor := rfl, .. cd_pair.to_as_mat (coe P) }

instance fintype : fintype (minor_pair N M) :=
fintype.of_injective (to_cd_pair) (λ x y hxy, by {cases x, cases y, tidy})

/-- constructs the trivial minor pair associated with M -/
def trivial (M : matroid_in α) : minor_pair M M :=
⟨cd_pair.trivial M, by simp [cd_pair.trivial]⟩

lemma union : mp.C ∪ mp.D = M.E \ N.E :=
let ⟨⟨C,D,disj,C_ss_E,D_ss_E⟩, hmin⟩ := mp in
begin
  show C ∪ D = M.E \ N.E,
  simp only [←hmin, diff_diff, diff_self_diff] with msimp,
  rw [eq_comm, ←subset_iff_inter_eq_right, union_subset_iff],
  split; assumption,
end

lemma minor_eq : M / mp.C \ mp.D = N := mp.minor

def of_restrict (M : matroid_in α){R : set α} (hR : R ⊆ M.E) :
  minor_pair (M ∣ R) M :=
{minor := by {dsimp only [cd_pair.of_restr, restrict], rw [con_empty, diff_eq, ← del_eq_del_inter_E],} ,
 .. cd_pair.of_restr M hR}

def of_contract_restrict (M : matroid_in α){C R : set α} (hC : C ⊆ M.E) (hR : R ⊆ M.E \ C) :
  minor_pair (M / C ∣ R) M :=
⟨ cd_pair.of_con_restr M hC hR,
  by {rw [matroid_in.restrict, del_eq_del_inter_E _ Rᶜ],
      congr' 1, simp [←inter_assoc, cd_pair.of_con_restr, diff_eq]}⟩

def choose_minor_pair (h : is_minor N M) : minor_pair N M :=
  classical.choice (h)

lemma minor_pair_to_is_minor :
  minor_pair N M → is_minor N M :=
nonempty.intro

lemma NE_ss_ME (p : minor_pair N M) :
  N.E ⊆ M.E :=
by {rw ←p.minor, intro x, simp, tauto}

@[simp, msimp] lemma NE_inter_D (p : minor_pair N M) :
  N.E ∩ p.D = ∅ :=
by {simp only [←p.minor, diff_eq] with msimp, rw inter_assoc, simp,}

@[simp, msimp] lemma D_inter_NE (p : minor_pair N M) :
  p.D ∩ N.E = ∅ :=
by rw [inter_comm, NE_inter_D]

lemma NE_disj_D (p : minor_pair N M) :
  disjoint N.E p.D :=
by {rw disjoint_iff_inter_eq_empty, apply NE_inter_D}

@[simp, msimp] lemma NE_inter_C (p : minor_pair N M) :
  N.E ∩ p.C = ∅ :=
by {simp only [←p.minor, diff_eq] with msimp, rw [inter_right_comm, inter_comm, inter_assoc M.E], simp,}

@[simp, msimp] lemma C_inter_NE (p : minor_pair N M) :
  p.C ∩ N.E = ∅ :=
by rw [inter_comm, NE_inter_C]

lemma NE_disj_C (p : minor_pair N M) :
  disjoint N.E p.C :=
by {rw disjoint_iff_inter_eq_empty, apply NE_inter_C}

lemma C_union_D_ss_E (p : minor_pair N M) :
  p.C ∪ p.D ⊆ M.E :=
union_subset p.C_ss_E p.D_ss_E

/-- converts a minor pair C D for N M to the minor pair D C for N* M*-/
def to_dual (p : minor_pair N M) : minor_pair N.dual M.dual :=
{ minor := by {simpa [←p.minor_eq, cd_pair.switch] using (dual_con_del M p.disj).symm,},
  ..((p : cd_pair M).switch) }

/-- converts a minor pair C D for N* M* to the minor pair D C for N M-/
def of_dual (p : minor_pair N.dual M.dual) : minor_pair N M :=
{ minor := by {conv_rhs {rw [←dual_dual N, ←p.minor]}, rw [dual_con_del _ (p.disj), dual_dual], refl, },
  .. ((p : cd_pair M.dual).of_eq_E (rfl : M.E = M.dual.E)).switch }

/-- natural bijection between minor pairs and dual minor pairs -/
def dual_equiv : minor_pair N M ≃ minor_pair N.dual M.dual :=
{ to_fun := to_dual,
  inv_fun := of_dual,
  left_inv := λ x, by { ext : 2; {simpa [of_dual, to_dual, cd_pair.switch]}},
  right_inv := λ x, by { ext : 2; {simpa [of_dual, to_dual, cd_pair.switch]}} }

@[simp, msimp] lemma dual_equiv_C (p : minor_pair N M) : (dual_equiv p).C = p.D := rfl
@[simp, msimp] lemma dual_equiv_D (p : minor_pair N M) : (dual_equiv p).D = p.C := rfl
@[simp, msimp] lemma dual_equiv_inv_C (p : minor_pair N.dual M.dual) :
  (dual_equiv.inv_fun p).C = p.D := rfl
@[simp, msimp] lemma dual_equiv_inv_D (p : minor_pair N.dual M.dual) :
  (dual_equiv.inv_fun p).D = p.C := rfl

lemma rank (p : minor_pair N M) (X : set α) (hX : X ⊆ N.E) :
  N.r X = M.r (X ∪ p.C) - M.r p.C :=
begin
  simp only [←p.minor] with msimp,  congr',
  refine subset_iff_inter_eq_left.mp (subset.trans hX _),
  exact subset_compl_iff_disjoint.mpr (p.NE_inter_D),
end

lemma rank_subtype (p : minor_pair N M) (X : set N.E) :
  N.r ↑X = M.r (↑X ∪ p.C) - M.r p.C :=
by {rw rank, rintro x hx, obtain ⟨⟨x,h⟩,-,rfl⟩ := (mem_image _ _ _).mp hx, exact h, }

/-- given minor pairs for M₁ M₂ and M₂ M₃, constructs a minor pair for M₁ M₃ -/
def trans {M₁ M₂ M₃ : matroid_in α} (p₁₂ : minor_pair M₁ M₂) (p₂₃ : minor_pair M₂ M₃) :
  minor_pair M₁ M₃ :=
have h₁ : p₁₂.C ∩ p₂₃.D = ∅ := disjoint_of_subset_left' p₁₂.C_ss_E p₂₃.NE_inter_D,
have h₂ : p₂₃.C ∩ p₁₂.D = ∅ := disjoint_of_subset_right' p₁₂.D_ss_E p₂₃.C_inter_NE,
{ C := p₁₂.C ∪ p₂₃.C,
  D := p₁₂.D ∪ p₂₃.D,
  disj := by simp_rw [inter_distrib_left, inter_distrib_right, h₁, h₂, p₁₂.disj, p₂₃.disj, union_self],
  C_ss_E := union_subset (subset.trans p₁₂.C_ss_E p₂₃.NE_ss_ME) (p₂₃.C_ss_E),
  D_ss_E := union_subset (subset.trans p₁₂.D_ss_E p₂₃.NE_ss_ME) (p₂₃.D_ss_E),
  minor := by {
    simp only [←p₁₂.minor, ←p₂₃.minor],
    rw [union_comm p₁₂.C, union_comm p₁₂.D, ←del_del, ←con_con, con_del_eq_del_con _ _ _ h₁], }}

/-- given a minor pair C,D and a subset of C whose removal doesn't drop the rank of C, moves
    that subset to D -/
def move_to_delete (p : minor_pair N M){A : set α}
(h₁ : A ⊆ p.C) (h₂ : M.r (p.C \ A) = M.r p.C) : minor_pair N M :=
let p' : cd_pair M := {
  C := p.C \ A,
  D := p.D ∪ A,
  disj := by {rw [diff_eq, inter_distrib_left, inter_right_comm, p.disj, inter_assoc],simp,},
  C_ss_E := subset.trans (diff_subset _ _) p.C_ss_E,
  D_ss_E := union_subset (p.D_ss_E) (subset.trans h₁ p.C_ss_E),} in
{ minor := begin
    ext, simp [← p.minor] with msimp, tauto,
    intros X hX, rw p.rank X _, swap,
    {simp only [←p.minor, diff_diff, pair_move _ h₁] with msimp at ⊢ hX, exact hX},
    rw cd_pair.rank _ _ hX,
    simp only [h₂],
    congr' 1, exact rank_eq_of_union_rank_diff_eq X h₂,
  end, ..p' }

/-- given a minor pair C,D and a subset of D that is spanned by C, moves that subset to C -/
def move_to_contract (p : minor_pair N M){A : set α}
(h₁ : A ⊆ p.D) (h₂ : M.r (p.C ∪ A) = M.r p.C) : minor_pair N M :=
let p' : cd_pair M := {
  C := p.C ∪ A,
  D := p.D \ A,
  disj := by {rw [diff_eq, inter_distrib_right, ←inter_assoc, p.disj, inter_comm p.D, ←inter_assoc], simp,},
  D_ss_E := subset.trans (diff_subset _ _) p.D_ss_E,
  C_ss_E := union_subset (p.C_ss_E) (subset.trans h₁ p.D_ss_E)} in
{ minor := begin
    ext, simp [← p.minor] with msimp, tauto,
    intros X hX, rw p.rank X _, swap,
    {simp only [←p.minor, diff_diff, union_comm (p.C ∪ A) , pair_move _ h₁]
       with msimp at hX ⊢, rwa [union_comm], },
    simp only [h₂] with msimp, congr' 1,
    have h := rank_eq_of_union_eq_rank_subset _ (subset_union_left p.C A) h₂.symm,
    simp_rw [r_carrier_eq_r, union_comm (p.C ∪ A)] at h, rw [← h, union_comm], congr' 2,
    simp only with msimp at hX, rw [←disjoint_iff_diff_eq_left],
    exact disjoint_of_subset_left' hX (diff_inter_right_eq_empty _ _),
  end .. p' }

/-- A minor pair C D with C dependent doesn't maximize r C + r* D -/
lemma suboptimal_goodness {N M : matroid_in α} (p : minor_pair N M) (hdep : ¬is_indep M p.C) :
∃ p': minor_pair N M, M.r p.C + M.dual.r p.D < M.r p'.C + M.dual.r p'.D :=
begin
  simp_rw [indep_iff_carrier, not_indep_iff_exists_removal, r_carrier_eq_r, ←singleton_subset_iff] at hdep,
  rcases hdep with ⟨e,heC, he⟩,
  use p.move_to_delete heC he,
  rw [minor_pair.move_to_delete], dsimp only, rw [he, dual_r],
  simp only [diff_eq, matroid_in.dual_r, set.compl_union, sub_lt_sub_iff_right, add_lt_add_iff_left],

  have h : size ((p.D ∪ {e}) ∩ M.E) = size (p.D ∩ M.E) + 1,
  { rw [inter_distrib_right, subset_iff_inter_eq_left.mp (subset.trans heC p.C_ss_E)],
    apply size_union_nonmem_singleton,
    by_contra hn,
    rw singleton_subset_iff at heC,
    have := eq_empty_iff_forall_not_mem.mp p.disj e,
    simp only [set.mem_inter_eq] at this hn,
    tauto, },
  rw h,
  apply (λ x y y' h', by {rw [add_right_comm, add_assoc],simp only [add_lt_add_iff_left, int.lt_add_one_iff, h'],} :
  ∀ (x y y' : ℤ), y ≤ y' → x + y < x + 1 + y'),
  simp_rw [←r_eq_inter_r],
  have hCD : p.C ∪ p.Dᶜ = p.Dᶜ := subset_iff_union_eq_left.mp (by {rw subset_compl_iff_disjoint, exact p.disj}),
  have h' := (rank_eq_of_union_eq_rank_subset (p.Dᶜ ∩ {e}ᶜ) (by simp) he).symm,

  convert (has_le.le.trans_eq (eq.le _) h'),
  { rw [diff_eq, ←inter_distrib_right, hCD], },
  convert rfl,
  rw [union_distrib_left, hCD, ←subset_iff_inter_eq_left],
  convert subset_univ _,
  rwa [←compl_subset_iff_union, ←compl_subset_compl.symm],
end

/-- each minor can be represented by a indep/coindep contract_delete pair -/
lemma minor_has_indep_coindep_pair {N M : matroid_in α} (h_minor : is_minor N M) :
   ∃ (p : minor_pair N M), (M.is_indep p.C) ∧ (M.dual.is_indep p.D) :=
begin
  letI : nonempty _ := h_minor,

  /- take a minor pair (C,D) maximizing r C + r* D-/
  rcases max_spec (λ (p : minor_pair N M), M.r p.C + M.dual.r p.D) with ⟨p,⟨h,h'⟩⟩,
  use p, by_contra hn, rw not_and_distrib at hn, cases hn,

  /- C is independent because otherwise we could improve C D  -/
  { rcases suboptimal_goodness p hn with ⟨p',hp'⟩, have := h' p', dsimp only at this, linarith,},

  /- D is coindependent by the dual argument -/
  rcases suboptimal_goodness (minor_pair.dual_equiv p) hn with ⟨p', hp'⟩,
  specialize h' (minor_pair.dual_equiv.inv_fun p'),
  simp only with msimp at h' hp',
  linarith,
end

/-- each minor N has a minor pair (C,D) with C independent and of size r M - r N. (Latter condition
is equivalent to the coindependence of D in M) -/
lemma minor_has_indep_coindep_pair' {N M : matroid_in α} (h_minor : is_minor N M) :
   ∃ (p : minor_pair N M), M.is_indep p.C ∧ size p.C = M.r M.E - N.r N.E :=
begin
  rcases minor_has_indep_coindep_pair h_minor with ⟨p,hC,hD⟩,
  refine ⟨p,hC,_⟩,
  rw indep_iff_r at hC,
  simp_rw [←p.minor],
  have : (M / p.C).dual.is_indep p.D,
  { rw [coindep_contract_iff, inter_comm], exact ⟨p.disj, hD⟩},
  rw [delete_coindep_rank_ground (M / p.C) this, con_rank_ground],
  linarith,
end

end minor_pair

lemma con_del_is_minor (M : matroid_in α) (C D : set α) :
  (M / C \ D).is_minor M :=
⟨to_minor_pair M C D⟩

lemma con_restr_is_minor (M : matroid_in α) (C R : set α) :
  (M / C ∣ R).is_minor M :=
con_del_is_minor _ _ _

lemma minor_iff_dual_minor {M N : matroid_in α} :
  N.is_minor M ↔ N.dual.is_minor M.dual :=
begin
  repeat {rw ←minor_pair.nonempty_iff},
  refine ⟨λ a, _, λ a, _⟩,
  cases a, apply nonempty.intro (a.to_dual),
  cases a, apply nonempty.intro (a.of_dual),
end

lemma del_con_is_minor (M : matroid_in α) (C D : set α) :
   (M \ D / C).is_minor M :=
by {rw [del_con_eq_con_del'], apply con_del_is_minor,  }

lemma dual_minor_of_minor {M N : matroid_in α} :
  N.is_minor M → N.dual.is_minor M.dual :=
minor_iff_dual_minor.mp

lemma deletion_is_restriction (M : matroid_in α) (D : set α) :
  (M \ D).is_restriction M :=
by {nth_rewrite 0 ←con_empty M, refine ⟨to_minor_pair M ∅ D, by simp [to_minor_pair]⟩}

lemma restriction_to_is_restriction (M : matroid_in α) (R : set α) :
  (M ∣ R).is_restriction M :=
deletion_is_restriction M Rᶜ

lemma contraction_is_contraction_minor (M : matroid_in α) (C : set α) :
  (M / C).is_contraction_minor M :=
by {rw ←del_empty (M / C), refine ⟨to_minor_pair M C ∅, by simp [to_minor_pair]⟩}

lemma restriction_is_minor (N M : matroid_in α) :
  N.is_restriction M → N.is_minor M :=
by {rintros ⟨h,-⟩, exact ⟨h⟩}

lemma contraction_minor_is_minor (N M : matroid_in α) :
  N.is_contraction_minor M → N.is_minor M :=
by {rintros ⟨h,-⟩, exact ⟨h⟩}

lemma minor_antisymm:
  anti_symmetric (@is_minor α _) :=
begin
  rintros M₁ M₂ ⟨p₁⟩ ⟨p₂⟩, --rw is_minor at h₁ h₂,
  have := p₁.minor, simp_rw ←p₂.minor at this ⊢,
  have h := congr_arg E this, simp only [diff_eq] with msimp at h,
  repeat {rw [inter_assoc] at h},
  repeat {rw [←compl_union] at h},
  repeat {rw [←union_assoc] at h},
  rw [←subset_iff_inter_eq_left] at h,
  have hC : p₂.C = ∅,
  { refine empty_of_subset_compl (subset.trans p₂.C_ss_E (subset.trans h _)),
    intro x, simp, tauto,  },
  have hD : p₂.D = ∅,
  { refine empty_of_subset_compl (subset.trans p₂.D_ss_E (subset.trans h _)),
    intro x, simp, tauto,  },
  simp only [hC,hD] with msimp,
end

lemma minor_trans:
  transitive (@is_minor α _) :=
by {rintros M₁ M₂ M₃ ⟨p₁⟩ ⟨p₂⟩, apply nonempty.intro (p₁.trans p₂)}

lemma minor_iff_has_contract {N M : matroid_in α} :
  N.is_minor M ↔ N.E ⊆ M.E ∧ ∃ C ⊆ M.E \ N.E, ∀ X ⊆ N.E, N.r X = M.r (X ∪ C) - M.r C :=
begin
  split,
  { rintro ⟨⟨C,D,disj,hC,hD⟩,rfl⟩, simp only with msimp,
    refine ⟨by {rw [diff_diff], apply diff_subset }, ⟨C,⟨_, λ X hX, _⟩⟩⟩,
    { simp only [diff_diff, diff_self_diff] with msimp,
      exact subset_inter hC (subset_union_left _ _), },
    congr', exact diff_eq_self_of_subset_diff hX, },
  rintro ⟨hE, C, hC, hr⟩,
  refine ⟨⟨⟨C,M.E \ (N.E ∪ C),_,subset.trans hC (diff_subset _ _ ),diff_subset _ _⟩,_⟩⟩,
  { rw ←diff_diff, apply inter_diff_self, },
  have h: M.E \ C \ (M.E \ (N.E ∪ C)) = N.E,
  { ext x, specialize @hC x, specialize @hE x, simp at *, tauto},
  ext : 1, simpa only with msimp using h,
  simp only [h] with msimp at *,
  intros X hX, rw [hr X hX], congr',
  { ext x, specialize @hC x, specialize @hE x, simp at *, tauto},
end

lemma con_or_del {N M : matroid_in α} {e : α} (h : is_minor N M) (he : e ∈ M.E \ N.E) :
  is_minor N (M / {e}) ∨ is_minor N (M \ {e}) :=
begin
  rw is_minor at h, rcases h with ⟨p⟩,
  rw [← p.union, mem_union] at he,
  rw ←p.minor,
  cases he,
  { rw [←union_mem_singleton he, union_comm, ←con_con],
    let Me := matroid_in.contract M {e},
    exact or.inl (con_del_is_minor (M / {e}) _ _),  },
  right, rw [con_del_eq_del_con _ _ _ p.disj, ←union_mem_singleton he, union_comm, ←del_del ],
  apply del_con_is_minor,
end

end minor

end matroid_in


import Categories.Notation

set_option autoImplicit false
universe u v w

theorem congr₂ {A : Sort u} {B : Sort v} {C : Sort w} {f : A → B → C} {a₁ a₂ : A} {b₁ b₂ : B} (h₁ : a₁ = a₂) (h₂ : b₁ = b₂) : f a₁ b₁ = f a₂ b₂ :=
h₁ ▸ h₂ ▸ rfl

theorem Sigma.eta {A : Type u} {B : A → Type v} {a₁ a₂ : A} {b₁ : B a₁} {b₂ : B a₂}
  (h₁ : a₁ = a₂) (h₂ : HEq b₁ b₂) : Sigma.mk a₁ b₁ = Sigma.mk a₂ b₂ :=
begin subst h₁; subst h₂; rfl end

theorem Prod.eq {A : Type u} {B : Type v} {w₁ w₂ : A × B} (h₁ : w₁.1 = w₂.1) (h₂ : w₁.2 = w₂.2) : w₁ = w₂ :=
begin cases w₁; cases w₂; simp at h₁ h₂; subst h₁; subst h₂; rfl end

notation "⊥" => False
notation "⊤" => True
notation "𝟎" => Empty
notation "𝟏" => Unit
notation "★" => Unit.unit
notation "𝟐" => Bool
notation "ℕ" => Nat

def ExistsUnique {A : Type u} (P : A → Prop) :=
{ x : A // P x ∧ ∀ y, P y → x = y }

open Lean.TSyntax.Compat in
macro "∃! " xs:Lean.explicitBinders ", " b:term : term =>
  Lean.expandExplicitBinders ``ExistsUnique xs b

namespace Mathematics

structure Category :=
(obj   : Type u)
(hom   : obj → obj → Type v)
(id    : Π x, hom x x)
(com   : Π {a b c : obj}, hom b c → hom a b → hom a c)
(lid   : Π {a b : obj} (f : hom a b), com (id b) f = f)
(rid   : Π {a b : obj} (f : hom a b), com f (id a) = f)
(assoc : Π {a b c d : obj} (f : hom c d) (g : hom b c) (h : hom a b), com (com f g) h = com f (com g h))

abbrev Hom := Category.hom

instance (C : Category) (x : C.obj) : OfNat (Hom C x x) 1 := ⟨C.id x⟩
infix:70 (priority := high) " ∘ " => Category.com _

def Category.zero : Category :=
{ obj   := 𝟎,
  hom   := λ _ _, 𝟎,
  id    := λ x, nomatch x,
  com   := λ x _, nomatch x,
  lid   := λ x, nomatch x,
  rid   := λ x, nomatch x,
  assoc := λ x _ _, nomatch x }

notation "𝟘" => Category.zero

def Category.one : Category :=
{ obj   := 𝟏,
  hom   := λ _ _, 𝟏,
  id    := λ ★, ★,
  com   := λ _ _, ★,
  lid   := @λ ★ ★ ★ => rfl,
  rid   := @λ ★ ★ ★ => rfl,
  assoc := @λ ★ ★ ★ ★ ★ ★ ★ => rfl }

notation "𝟙" => Category.one

def Opposite (C : Category) : Category :=
{ obj   := C.obj,
  hom   := λ a b, C.hom b a,
  id    := C.id,
  com   := λ f g, C.com g f,
  lid   := C.rid,
  rid   := C.lid,
  assoc := λ f g h, Eq.symm (C.assoc h g f) }

postfix:max "ᵒᵖ" => Opposite

example (C : Category) : C = (Cᵒᵖ)ᵒᵖ := rfl

section
  variable {C : Category} {a b : C.obj} (f : Hom C a b)

  def monic := ∀ (t : C.obj) (h k : Hom C t a), f ∘ h = f ∘ k → h = k
  def epic  := ∀ (t : C.obj) (h k : Hom C b t), h ∘ f = k ∘ f → h = k

  def splitEpic  := { g : Hom C b a // f ∘ g = 1 }
  def splitMonic := { g : Hom C b a // g ∘ f = 1 }
end

def hasInv {C : Category} {a b : C.obj} (f : Hom C a b) :=
{ g : Hom C b a // f ∘ g = 1 ∧ g ∘ f = 1 }

def Iso {C : Category} (a b : C.obj) :=
Σ (f : Hom C a b), hasInv f

infix:60 " ≅ " => Iso

def Iso.refl {C : Category} (c : C.obj) : c ≅ c :=
begin exists 1; exists 1; constructor <;> apply C.lid end

def Iso.symm {C : Category} {a b : C.obj} : a ≅ b → b ≅ a :=
begin intro H; exists H.2.1; exists H.1; constructor; exact H.2.2.2; exact H.2.2.1 end

postfix:max "⁻¹" => Iso.symm

def hasInvCom {C : Category} {a b c : C.obj} {f : Hom C b c} {g : Hom C a b} :
  hasInv f → hasInv g → hasInv (f ∘ g) :=
begin
  intros H₁ H₂; exists H₂.1 ∘ H₁.1; constructor;
  { rw [C.assoc, ←C.assoc g, H₂.2.1]; apply Eq.trans;
    apply congrArg; apply C.lid; apply H₁.2.1 };
  { rw [C.assoc, ←C.assoc H₁.1, H₁.2.2]; apply Eq.trans;
    apply congrArg; apply C.lid; apply H₂.2.2 }
end

def Iso.trans {C : Category} {a b c : C.obj} : a ≅ b → b ≅ c → a ≅ c :=
begin intro H₁ H₂; exists H₂.1 ∘ H₁.1; apply hasInvCom; exact H₂.2; exact H₁.2 end

infixl:70 " ⬝ " => Iso.trans

def isoInterchange₁ {C : Category} {a b c : C.obj} {φ : b ≅ c} {f : Hom C a b} {g : Hom C a c} : φ.1 ∘ f = g → f = φ.2.1 ∘ g :=
begin intro H; rw [←C.lid f]; apply Eq.trans; apply congrArg (· ∘ f); apply Eq.symm φ.2.2.2; rw [C.assoc, H] end

def isoInterchange₂ {C : Category} {a b c : C.obj} {φ : b ≅ c} {f : Hom C a b} {g : Hom C a c} : f = φ.2.1 ∘ g → φ.1 ∘ f = g :=
begin intro H; rw [←C.lid g]; apply Eq.symm; apply Eq.trans; apply congrArg (· ∘ g); apply Eq.symm φ.2.2.1; rw [C.assoc, H] end

section
  variable {C : Category} {a b : C.obj} (φ : a ≅ b)

  def isoMono : monic φ.1 :=
  begin
    intro c h k H; rw [←C.lid h]; apply Eq.trans;
    apply congrArg (· ∘ h); apply Eq.symm φ.2.2.2;
    rw [C.assoc, H, ←C.assoc, φ.2.2.2]; apply C.lid
  end

  def isoEpic : epic φ.1 :=
  begin
    intro c h k H; rw [←C.rid h]; apply Eq.trans;
    apply congrArg; apply Eq.symm φ.2.2.1;
    rw [←C.assoc, H, C.assoc, φ.2.2.1]; apply C.rid
  end

  def isoMonoRev : monic φ.2.1 := isoMono φ⁻¹
  def isoEpicRev : epic φ.2.1  := isoEpic φ⁻¹
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
-/
import data.set.pointwise

/-!
# (Pre)images of intervals

In this file we prove a bunch of trivial lemmas like “if we add `a` to all points of `[b, c]`,
then we get `[a + b, a + c]`”. For the functions `x ↦ x ± a`, `x ↦ a ± x`, and `x ↦ -x` we prove
lemmas about preimages and images of all intervals. We also prove a few lemmas about images under
`x ↦ a * x`, `x ↦ x * a` and `x ↦ x⁻¹`.
-/

universe u
open_locale pointwise

namespace set

section has_exists_add_of_le
/-!
The lemmas in this section state that addition maps intervals bijectively. The typeclass
`has_exists_add_of_le` is defined specifically to make them work when combined with
`ordered_cancel_add_comm_monoid`; the lemmas below therefore apply to all
`ordered_add_comm_group`, but also to `ℕ` and `ℝ≥0`, which are not groups.

TODO : move as much as possible in this file to the setting of this weaker typeclass.
-/

variables {α : Type u} [ordered_cancel_add_comm_monoid α] [has_exists_add_of_le α] (a b d : α)

lemma Icc_add_bij : bij_on (+d) (Icc a b) (Icc (a + d) (b + d)) :=
begin
  refine ⟨λ _ h, ⟨add_le_add_right h.1 _, add_le_add_right h.2 _⟩,
          λ _ _ _ _ h, add_right_cancel h,
          λ _ h, _⟩,
  obtain ⟨c, rfl⟩ := exists_add_of_le h.1,
  rw [mem_Icc, add_right_comm, add_le_add_iff_right, add_le_add_iff_right] at h,
  exact ⟨a + c, h, by rw add_right_comm⟩,
end

lemma Ioo_add_bij : bij_on (+d) (Ioo a b) (Ioo (a + d) (b + d)) :=
begin
  refine ⟨λ _ h, ⟨add_lt_add_right h.1 _, add_lt_add_right h.2 _⟩,
          λ _ _ _ _ h, add_right_cancel h,
          λ _ h, _⟩,
  obtain ⟨c, rfl⟩ := exists_add_of_le h.1.le,
  rw [mem_Ioo, add_right_comm, add_lt_add_iff_right, add_lt_add_iff_right] at h,
  exact ⟨a + c, h, by rw add_right_comm⟩,
end

lemma Ioc_add_bij : bij_on (+d) (Ioc a b) (Ioc (a + d) (b + d)) :=
begin
  refine ⟨λ _ h, ⟨add_lt_add_right h.1 _, add_le_add_right h.2 _⟩,
          λ _ _ _ _ h, add_right_cancel h,
          λ _ h, _⟩,
  obtain ⟨c, rfl⟩ := exists_add_of_le h.1.le,
  rw [mem_Ioc, add_right_comm, add_lt_add_iff_right, add_le_add_iff_right] at h,
  exact ⟨a + c, h, by rw add_right_comm⟩,
end

lemma Ico_add_bij : bij_on (+d) (Ico a b) (Ico (a + d) (b + d)) :=
begin
  refine ⟨λ _ h, ⟨add_le_add_right h.1 _, add_lt_add_right h.2 _⟩,
          λ _ _ _ _ h, add_right_cancel h,
          λ _ h, _⟩,
  obtain ⟨c, rfl⟩ := exists_add_of_le h.1,
  rw [mem_Ico, add_right_comm, add_le_add_iff_right, add_lt_add_iff_right] at h,
  exact ⟨a + c, h, by rw add_right_comm⟩,
end

lemma Ici_add_bij : bij_on (+d) (Ici a) (Ici (a + d)) :=
begin
  refine ⟨λ x h, add_le_add_right (mem_Ici.mp h) _, λ _ _ _ _ h, add_right_cancel h, λ _ h, _⟩,
  obtain ⟨c, rfl⟩ := exists_add_of_le (mem_Ici.mp h),
  rw [mem_Ici, add_right_comm, add_le_add_iff_right] at h,
  exact ⟨a + c, h, by rw add_right_comm⟩,
end

lemma Ioi_add_bij : bij_on (+d) (Ioi a) (Ioi (a + d)) :=
begin
  refine ⟨λ x h, add_lt_add_right (mem_Ioi.mp h) _, λ _ _ _ _ h, add_right_cancel h, λ _ h, _⟩,
  obtain ⟨c, rfl⟩ := exists_add_of_le (mem_Ioi.mp h).le,
  rw [mem_Ioi, add_right_comm, add_lt_add_iff_right] at h,
  exact ⟨a + c, h, by rw add_right_comm⟩,
end

end has_exists_add_of_le

section ordered_add_comm_group

variables {G : Type u} [ordered_add_comm_group G] (a b c : G)

/-!
### Preimages under `x ↦ a + x`
-/

@[simp] lemma preimage_const_add_Ici : (λ x, a + x) ⁻¹' (Ici b) = Ici (b - a) :=
ext $ λ x, sub_le_iff_le_add'.symm

@[simp] lemma preimage_const_add_Ioi : (λ x, a + x) ⁻¹' (Ioi b) = Ioi (b - a) :=
ext $ λ x, sub_lt_iff_lt_add'.symm

@[simp] lemma preimage_const_add_Iic : (λ x, a + x) ⁻¹' (Iic b) = Iic (b - a) :=
ext $ λ x, le_sub_iff_add_le'.symm

@[simp] lemma preimage_const_add_Iio : (λ x, a + x) ⁻¹' (Iio b) = Iio (b - a) :=
ext $ λ x, lt_sub_iff_add_lt'.symm

@[simp] lemma preimage_const_add_Icc : (λ x, a + x) ⁻¹' (Icc b c) = Icc (b - a) (c - a) :=
by simp [← Ici_inter_Iic]

@[simp] lemma preimage_const_add_Ico : (λ x, a + x) ⁻¹' (Ico b c) = Ico (b - a) (c - a) :=
by simp [← Ici_inter_Iio]

@[simp] lemma preimage_const_add_Ioc : (λ x, a + x) ⁻¹' (Ioc b c) = Ioc (b - a) (c - a) :=
by simp [← Ioi_inter_Iic]

@[simp] lemma preimage_const_add_Ioo : (λ x, a + x) ⁻¹' (Ioo b c) = Ioo (b - a) (c - a) :=
by simp [← Ioi_inter_Iio]

/-!
### Preimages under `x ↦ x + a`
-/

@[simp] lemma preimage_add_const_Ici : (λ x, x + a) ⁻¹' (Ici b) = Ici (b - a) :=
ext $ λ x, sub_le_iff_le_add.symm

@[simp] lemma preimage_add_const_Ioi : (λ x, x + a) ⁻¹' (Ioi b) = Ioi (b - a) :=
ext $ λ x, sub_lt_iff_lt_add.symm

@[simp] lemma preimage_add_const_Iic : (λ x, x + a) ⁻¹' (Iic b) = Iic (b - a) :=
ext $ λ x, le_sub_iff_add_le.symm

@[simp] lemma preimage_add_const_Iio : (λ x, x + a) ⁻¹' (Iio b) = Iio (b - a) :=
ext $ λ x, lt_sub_iff_add_lt.symm

@[simp] lemma preimage_add_const_Icc : (λ x, x + a) ⁻¹' (Icc b c) = Icc (b - a) (c - a) :=
by simp [← Ici_inter_Iic]

@[simp] lemma preimage_add_const_Ico : (λ x, x + a) ⁻¹' (Ico b c) = Ico (b - a) (c - a) :=
by simp [← Ici_inter_Iio]

@[simp] lemma preimage_add_const_Ioc : (λ x, x + a) ⁻¹' (Ioc b c) = Ioc (b - a) (c - a) :=
by simp [← Ioi_inter_Iic]

@[simp] lemma preimage_add_const_Ioo : (λ x, x + a) ⁻¹' (Ioo b c) = Ioo (b - a) (c - a) :=
by simp [← Ioi_inter_Iio]

/-!
### Preimages under `x ↦ -x`
-/

@[simp] lemma preimage_neg_Ici : - Ici a = Iic (-a) := ext $ λ x, le_neg
@[simp] lemma preimage_neg_Iic : - Iic a = Ici (-a) := ext $ λ x, neg_le
@[simp] lemma preimage_neg_Ioi : - Ioi a = Iio (-a) := ext $ λ x, lt_neg
@[simp] lemma preimage_neg_Iio : - Iio a = Ioi (-a) := ext $ λ x, neg_lt

@[simp] lemma preimage_neg_Icc : - Icc a b = Icc (-b) (-a) :=
by simp [← Ici_inter_Iic, inter_comm]

@[simp] lemma preimage_neg_Ico : - Ico a b = Ioc (-b) (-a) :=
by simp [← Ici_inter_Iio, ← Ioi_inter_Iic, inter_comm]

@[simp] lemma preimage_neg_Ioc : - Ioc a b = Ico (-b) (-a) :=
by simp [← Ioi_inter_Iic, ← Ici_inter_Iio, inter_comm]

@[simp] lemma preimage_neg_Ioo : - Ioo a b = Ioo (-b) (-a) :=
by simp [← Ioi_inter_Iio, inter_comm]

/-!
### Preimages under `x ↦ x - a`
-/

@[simp] lemma preimage_sub_const_Ici : (λ x, x - a) ⁻¹' (Ici b) = Ici (b + a) :=
by simp [sub_eq_add_neg]

@[simp] lemma preimage_sub_const_Ioi : (λ x, x - a) ⁻¹' (Ioi b) = Ioi (b + a) :=
by simp [sub_eq_add_neg]

@[simp] lemma preimage_sub_const_Iic : (λ x, x - a) ⁻¹' (Iic b) = Iic (b + a) :=
by simp [sub_eq_add_neg]

@[simp] lemma preimage_sub_const_Iio : (λ x, x - a) ⁻¹' (Iio b) = Iio (b + a) :=
by simp [sub_eq_add_neg]

@[simp] lemma preimage_sub_const_Icc : (λ x, x - a) ⁻¹' (Icc b c) = Icc (b + a) (c + a) :=
by simp [sub_eq_add_neg]

@[simp] lemma preimage_sub_const_Ico : (λ x, x - a) ⁻¹' (Ico b c) = Ico (b + a) (c + a) :=
by simp [sub_eq_add_neg]

@[simp] lemma preimage_sub_const_Ioc : (λ x, x - a) ⁻¹' (Ioc b c) = Ioc (b + a) (c + a) :=
by simp [sub_eq_add_neg]

@[simp] lemma preimage_sub_const_Ioo : (λ x, x - a) ⁻¹' (Ioo b c) = Ioo (b + a) (c + a) :=
by simp [sub_eq_add_neg]

/-!
### Preimages under `x ↦ a - x`
-/

@[simp] lemma preimage_const_sub_Ici : (λ x, a - x) ⁻¹' (Ici b) = Iic (a - b) :=
ext $ λ x, le_sub

@[simp] lemma preimage_const_sub_Iic : (λ x, a - x) ⁻¹' (Iic b) = Ici (a - b) :=
ext $ λ x, sub_le

@[simp] lemma preimage_const_sub_Ioi : (λ x, a - x) ⁻¹' (Ioi b) = Iio (a - b) :=
ext $ λ x, lt_sub

@[simp] lemma preimage_const_sub_Iio : (λ x, a - x) ⁻¹' (Iio b) = Ioi (a - b) :=
ext $ λ x, sub_lt

@[simp] lemma preimage_const_sub_Icc : (λ x, a - x) ⁻¹' (Icc b c) = Icc (a - c) (a - b) :=
by simp [← Ici_inter_Iic, inter_comm]

@[simp] lemma preimage_const_sub_Ico : (λ x, a - x) ⁻¹' (Ico b c) = Ioc (a - c) (a - b) :=
by simp [← Ioi_inter_Iic, ← Ici_inter_Iio, inter_comm]

@[simp] lemma preimage_const_sub_Ioc : (λ x, a - x) ⁻¹' (Ioc b c) = Ico (a - c) (a - b) :=
by simp [← Ioi_inter_Iic, ← Ici_inter_Iio, inter_comm]

@[simp] lemma preimage_const_sub_Ioo : (λ x, a - x) ⁻¹' (Ioo b c) = Ioo (a - c) (a - b) :=
by simp [← Ioi_inter_Iio, inter_comm]

/-!
### Images under `x ↦ a + x`
-/

@[simp] lemma image_const_add_Ici : (λ x, a + x) '' Ici b = Ici (a + b) :=
by simp [add_comm]

@[simp] lemma image_const_add_Iic : (λ x, a + x) '' Iic b = Iic (a + b) :=
by simp [add_comm]

@[simp] lemma image_const_add_Iio : (λ x, a + x) '' Iio b = Iio (a + b) :=
by simp [add_comm]

@[simp] lemma image_const_add_Ioi : (λ x, a + x) '' Ioi b = Ioi (a + b) :=
by simp [add_comm]

@[simp] lemma image_const_add_Icc : (λ x, a + x) '' Icc b c = Icc (a + b) (a + c) :=
by simp [add_comm]

@[simp] lemma image_const_add_Ico : (λ x, a + x) '' Ico b c = Ico (a + b) (a + c) :=
by simp [add_comm]

@[simp] lemma image_const_add_Ioc : (λ x, a + x) '' Ioc b c = Ioc (a + b) (a + c) :=
by simp [add_comm]

@[simp] lemma image_const_add_Ioo : (λ x, a + x) '' Ioo b c = Ioo (a + b) (a + c) :=
by simp [add_comm]

/-!
### Images under `x ↦ x + a`
-/

@[simp] lemma image_add_const_Ici : (λ x, x + a) '' Ici b = Ici (b + a) := by simp
@[simp] lemma image_add_const_Iic : (λ x, x + a) '' Iic b = Iic (b + a) := by simp
@[simp] lemma image_add_const_Iio : (λ x, x + a) '' Iio b = Iio (b + a) := by simp
@[simp] lemma image_add_const_Ioi : (λ x, x + a) '' Ioi b = Ioi (b + a) := by simp

@[simp] lemma image_add_const_Icc : (λ x, x + a) '' Icc b c = Icc (b + a) (c + a) :=
by simp

@[simp] lemma image_add_const_Ico : (λ x, x + a) '' Ico b c = Ico (b + a) (c + a) :=
by simp

@[simp] lemma image_add_const_Ioc : (λ x, x + a) '' Ioc b c = Ioc (b + a) (c + a) :=
by simp

@[simp] lemma image_add_const_Ioo : (λ x, x + a) '' Ioo b c = Ioo (b + a) (c + a) :=
by simp

/-!
### Images under `x ↦ -x`
-/

lemma image_neg_Ici : has_neg.neg '' (Ici a) = Iic (-a) := by simp
lemma image_neg_Iic : has_neg.neg '' (Iic a) = Ici (-a) := by simp
lemma image_neg_Ioi : has_neg.neg '' (Ioi a) = Iio (-a) := by simp
lemma image_neg_Iio : has_neg.neg '' (Iio a) = Ioi (-a) := by simp
lemma image_neg_Icc : has_neg.neg '' (Icc a b) = Icc (-b) (-a) := by simp
lemma image_neg_Ico : has_neg.neg '' (Ico a b) = Ioc (-b) (-a) := by simp
lemma image_neg_Ioc : has_neg.neg '' (Ioc a b) = Ico (-b) (-a) := by simp
lemma image_neg_Ioo : has_neg.neg '' (Ioo a b) = Ioo (-b) (-a) := by simp

/-!
### Images under `x ↦ a - x`
-/

@[simp] lemma image_const_sub_Ici : (λ x, a - x) '' Ici b = Iic (a - b) :=
by simp [sub_eq_add_neg, image_comp (λ x, a + x) (λ x, -x)]

@[simp] lemma image_const_sub_Iic : (λ x, a - x) '' Iic b = Ici (a - b) :=
by simp [sub_eq_add_neg, image_comp (λ x, a + x) (λ x, -x)]

@[simp] lemma image_const_sub_Ioi : (λ x, a - x) '' Ioi b = Iio (a - b) :=
by simp [sub_eq_add_neg, image_comp (λ x, a + x) (λ x, -x)]

@[simp] lemma image_const_sub_Iio : (λ x, a - x) '' Iio b = Ioi (a - b) :=
by simp [sub_eq_add_neg, image_comp (λ x, a + x) (λ x, -x)]

@[simp] lemma image_const_sub_Icc : (λ x, a - x) '' Icc b c = Icc (a - c) (a - b) :=
by simp [sub_eq_add_neg, image_comp (λ x, a + x) (λ x, -x)]

@[simp] lemma image_const_sub_Ico : (λ x, a - x) '' Ico b c = Ioc (a - c) (a - b) :=
by simp [sub_eq_add_neg, image_comp (λ x, a + x) (λ x, -x)]

@[simp] lemma image_const_sub_Ioc : (λ x, a - x) '' Ioc b c = Ico (a - c) (a - b) :=
by simp [sub_eq_add_neg, image_comp (λ x, a + x) (λ x, -x)]

@[simp] lemma image_const_sub_Ioo : (λ x, a - x) '' Ioo b c = Ioo (a - c) (a - b) :=
by simp [sub_eq_add_neg, image_comp (λ x, a + x) (λ x, -x)]

/-!
### Images under `x ↦ x - a`
-/

@[simp] lemma image_sub_const_Ici : (λ x, x - a) '' Ici b = Ici (b - a) := by simp [sub_eq_neg_add]
@[simp] lemma image_sub_const_Iic : (λ x, x - a) '' Iic b = Iic (b - a) := by simp [sub_eq_neg_add]
@[simp] lemma image_sub_const_Ioi : (λ x, x - a) '' Ioi b = Ioi (b - a) := by simp [sub_eq_neg_add]
@[simp] lemma image_sub_const_Iio : (λ x, x - a) '' Iio b = Iio (b - a) := by simp [sub_eq_neg_add]

@[simp] lemma image_sub_const_Icc : (λ x, x - a) '' Icc b c = Icc (b - a) (c - a) :=
by simp [sub_eq_neg_add]

@[simp] lemma image_sub_const_Ico : (λ x, x - a) '' Ico b c = Ico (b - a) (c - a) :=
by simp [sub_eq_neg_add]

@[simp] lemma image_sub_const_Ioc : (λ x, x - a) '' Ioc b c = Ioc (b - a) (c - a) :=
by simp [sub_eq_neg_add]

@[simp] lemma image_sub_const_Ioo : (λ x, x - a) '' Ioo b c = Ioo (b - a) (c - a) :=
by simp [sub_eq_neg_add]

/-!
### Bijections
-/

lemma Iic_add_bij : bij_on (+a) (Iic b) (Iic (b + a)) :=
begin
  refine ⟨λ x h, add_le_add_right (mem_Iic.mp h) _, λ _ _ _ _ h, add_right_cancel h, λ _ h, _⟩,
  simpa [add_comm a] using h,
end

lemma Iio_add_bij : bij_on (+a) (Iio b) (Iio (b + a)) :=
begin
  refine ⟨λ x h, add_lt_add_right (mem_Iio.mp h) _, λ _ _ _ _ h, add_right_cancel h, λ _ h, _⟩,
  simpa [add_comm a] using h,
end

end ordered_add_comm_group

/-!
### Multiplication and inverse in a field
-/

section linear_ordered_field

variables {k : Type u} [linear_ordered_field k]

@[simp] lemma preimage_mul_const_Iio (a : k) {c : k} (h : 0 < c) :
  (λ x, x * c) ⁻¹' (Iio a) = Iio (a / c) :=
ext $ λ x, (lt_div_iff h).symm

@[simp] lemma preimage_mul_const_Ioi (a : k) {c : k} (h : 0 < c) :
  (λ x, x * c) ⁻¹' (Ioi a) = Ioi (a / c) :=
ext $ λ x, (div_lt_iff h).symm

@[simp] lemma preimage_mul_const_Iic (a : k) {c : k} (h : 0 < c) :
  (λ x, x * c) ⁻¹' (Iic a) = Iic (a / c) :=
ext $ λ x, (le_div_iff h).symm

@[simp] lemma preimage_mul_const_Ici (a : k) {c : k} (h : 0 < c) :
  (λ x, x * c) ⁻¹' (Ici a) = Ici (a / c) :=
ext $ λ x, (div_le_iff h).symm

@[simp] lemma preimage_mul_const_Ioo (a b : k) {c : k} (h : 0 < c) :
  (λ x, x * c) ⁻¹' (Ioo a b) = Ioo (a / c) (b / c) :=
by simp [← Ioi_inter_Iio, h]

@[simp] lemma preimage_mul_const_Ioc (a b : k) {c : k} (h : 0 < c) :
  (λ x, x * c) ⁻¹' (Ioc a b) = Ioc (a / c) (b / c) :=
by simp [← Ioi_inter_Iic, h]

@[simp] lemma preimage_mul_const_Ico (a b : k) {c : k} (h : 0 < c) :
  (λ x, x * c) ⁻¹' (Ico a b) = Ico (a / c) (b / c) :=
by simp [← Ici_inter_Iio, h]

@[simp] lemma preimage_mul_const_Icc (a b : k) {c : k} (h : 0 < c) :
  (λ x, x * c) ⁻¹' (Icc a b) = Icc (a / c) (b / c) :=
by simp [← Ici_inter_Iic, h]

@[simp] lemma preimage_mul_const_Iio_of_neg (a : k) {c : k} (h : c < 0) :
  (λ x, x * c) ⁻¹' (Iio a) = Ioi (a / c) :=
ext $ λ x, (div_lt_iff_of_neg h).symm

@[simp] lemma preimage_mul_const_Ioi_of_neg (a : k) {c : k} (h : c < 0) :
  (λ x, x * c) ⁻¹' (Ioi a) = Iio (a / c) :=
ext $ λ x, (lt_div_iff_of_neg h).symm

@[simp] lemma preimage_mul_const_Iic_of_neg (a : k) {c : k} (h : c < 0) :
  (λ x, x * c) ⁻¹' (Iic a) = Ici (a / c) :=
ext $ λ x, (div_le_iff_of_neg h).symm

@[simp] lemma preimage_mul_const_Ici_of_neg (a : k) {c : k} (h : c < 0) :
  (λ x, x * c) ⁻¹' (Ici a) = Iic (a / c) :=
ext $ λ x, (le_div_iff_of_neg h).symm

@[simp] lemma preimage_mul_const_Ioo_of_neg (a b : k) {c : k} (h : c < 0) :
  (λ x, x * c) ⁻¹' (Ioo a b) = Ioo (b / c) (a / c) :=
by simp [← Ioi_inter_Iio, h, inter_comm]

@[simp] lemma preimage_mul_const_Ioc_of_neg (a b : k) {c : k} (h : c < 0) :
  (λ x, x * c) ⁻¹' (Ioc a b) = Ico (b / c) (a / c) :=
by simp [← Ioi_inter_Iic, ← Ici_inter_Iio, h, inter_comm]

@[simp] lemma preimage_mul_const_Ico_of_neg (a b : k) {c : k} (h : c < 0) :
  (λ x, x * c) ⁻¹' (Ico a b) = Ioc (b / c) (a / c) :=
by simp [← Ici_inter_Iio, ← Ioi_inter_Iic, h, inter_comm]

@[simp] lemma preimage_mul_const_Icc_of_neg (a b : k) {c : k} (h : c < 0) :
  (λ x, x * c) ⁻¹' (Icc a b) = Icc (b / c) (a / c) :=
by simp [← Ici_inter_Iic, h, inter_comm]

@[simp] lemma preimage_const_mul_Iio (a : k) {c : k} (h : 0 < c) :
  ((*) c) ⁻¹' (Iio a) = Iio (a / c) :=
ext $ λ x, (lt_div_iff' h).symm

@[simp] lemma preimage_const_mul_Ioi (a : k) {c : k} (h : 0 < c) :
  ((*) c) ⁻¹' (Ioi a) = Ioi (a / c) :=
ext $ λ x, (div_lt_iff' h).symm

@[simp] lemma preimage_const_mul_Iic (a : k) {c : k} (h : 0 < c) :
  ((*) c) ⁻¹' (Iic a) = Iic (a / c) :=
ext $ λ x, (le_div_iff' h).symm

@[simp] lemma preimage_const_mul_Ici (a : k) {c : k} (h : 0 < c) :
  ((*) c) ⁻¹' (Ici a) = Ici (a / c) :=
ext $ λ x, (div_le_iff' h).symm

@[simp] lemma preimage_const_mul_Ioo (a b : k) {c : k} (h : 0 < c) :
  ((*) c) ⁻¹' (Ioo a b) = Ioo (a / c) (b / c) :=
by simp [← Ioi_inter_Iio, h]

@[simp] lemma preimage_const_mul_Ioc (a b : k) {c : k} (h : 0 < c) :
  ((*) c) ⁻¹' (Ioc a b) = Ioc (a / c) (b / c) :=
by simp [← Ioi_inter_Iic, h]

@[simp] lemma preimage_const_mul_Ico (a b : k) {c : k} (h : 0 < c) :
  ((*) c) ⁻¹' (Ico a b) = Ico (a / c) (b / c) :=
by simp [← Ici_inter_Iio, h]

@[simp] lemma preimage_const_mul_Icc (a b : k) {c : k} (h : 0 < c) :
  ((*) c) ⁻¹' (Icc a b) = Icc (a / c) (b / c) :=
by simp [← Ici_inter_Iic, h]

@[simp] lemma preimage_const_mul_Iio_of_neg (a : k) {c : k} (h : c < 0) :
  ((*) c) ⁻¹' (Iio a) = Ioi (a / c) :=
by simpa only [mul_comm] using preimage_mul_const_Iio_of_neg a h

@[simp] lemma preimage_const_mul_Ioi_of_neg (a : k) {c : k} (h : c < 0) :
  ((*) c) ⁻¹' (Ioi a) = Iio (a / c) :=
by simpa only [mul_comm] using preimage_mul_const_Ioi_of_neg a h

@[simp] lemma preimage_const_mul_Iic_of_neg (a : k) {c : k} (h : c < 0) :
  ((*) c) ⁻¹' (Iic a) = Ici (a / c) :=
by simpa only [mul_comm] using preimage_mul_const_Iic_of_neg a h

@[simp] lemma preimage_const_mul_Ici_of_neg (a : k) {c : k} (h : c < 0) :
  ((*) c) ⁻¹' (Ici a) = Iic (a / c) :=
by simpa only [mul_comm] using preimage_mul_const_Ici_of_neg a h

@[simp] lemma preimage_const_mul_Ioo_of_neg (a b : k) {c : k} (h : c < 0) :
  ((*) c) ⁻¹' (Ioo a b) = Ioo (b / c) (a / c) :=
by simpa only [mul_comm] using preimage_mul_const_Ioo_of_neg a b h

@[simp] lemma preimage_const_mul_Ioc_of_neg (a b : k) {c : k} (h : c < 0) :
  ((*) c) ⁻¹' (Ioc a b) = Ico (b / c) (a / c) :=
by simpa only [mul_comm] using preimage_mul_const_Ioc_of_neg a b h

@[simp] lemma preimage_const_mul_Ico_of_neg (a b : k) {c : k} (h : c < 0) :
  ((*) c) ⁻¹' (Ico a b) = Ioc (b / c) (a / c) :=
by simpa only [mul_comm] using preimage_mul_const_Ico_of_neg a b h

@[simp] lemma preimage_const_mul_Icc_of_neg (a b : k) {c : k} (h : c < 0) :
  ((*) c) ⁻¹' (Icc a b) = Icc (b / c) (a / c) :=
by simpa only [mul_comm] using preimage_mul_const_Icc_of_neg a b h

lemma image_mul_right_Icc' (a b : k) {c : k} (h : 0 < c) :
  (λ x, x * c) '' Icc a b = Icc (a * c) (b * c) :=
((units.mk0 c h.ne').mul_right.image_eq_preimage _).trans (by simp [h, division_def])

lemma image_mul_right_Icc {a b c : k} (hab : a ≤ b) (hc : 0 ≤ c) :
  (λ x, x * c) '' Icc a b = Icc (a * c) (b * c) :=
begin
  cases eq_or_lt_of_le hc,
  { subst c,
    simp [(nonempty_Icc.2 hab).image_const] },
  exact image_mul_right_Icc' a b ‹0 < c›
end

lemma image_mul_left_Icc' {a : k} (h : 0 < a) (b c : k) :
  ((*) a) '' Icc b c = Icc (a * b) (a * c) :=
by { convert image_mul_right_Icc' b c h using 1; simp only [mul_comm _ a] }

lemma image_mul_left_Icc {a b c : k} (ha : 0 ≤ a) (hbc : b ≤ c) :
  ((*) a) '' Icc b c = Icc (a * b) (a * c) :=
by { convert image_mul_right_Icc hbc ha using 1; simp only [mul_comm _ a] }

lemma image_mul_right_Ioo (a b : k) {c : k} (h : 0 < c) :
  (λ x, x * c) '' Ioo a b = Ioo (a * c) (b * c) :=
((units.mk0 c h.ne').mul_right.image_eq_preimage _).trans (by simp [h, division_def])

lemma image_mul_left_Ioo {a : k} (h : 0 < a) (b c : k) :
  ((*) a) '' Ioo b c = Ioo (a * b) (a * c) :=
by { convert image_mul_right_Ioo b c h using 1; simp only [mul_comm _ a] }

/-- The image under `inv` of `Ioo 0 a` is `Ioi a⁻¹`. -/
lemma image_inv_Ioo_0_left {a : k} (ha : 0 < a) : has_inv.inv '' Ioo 0 a = Ioi a⁻¹ :=
begin
  ext x,
  exact ⟨λ ⟨y, ⟨hy0, hya⟩, hyx⟩, hyx ▸ (inv_lt_inv ha hy0).2 hya, λ h, ⟨x⁻¹, ⟨inv_pos.2 (lt_trans
    (inv_pos.2 ha) h), (inv_lt ha (lt_trans (inv_pos.2 ha) h)).1 h⟩, inv_inv x⟩⟩,
end

/-!
### Images under `x ↦ a * x + b`
-/

@[simp] lemma image_affine_Icc' {a : k} (h : 0 < a) (b c d : k) :
  (λ x, a * x + b) '' Icc c d = Icc (a * c + b) (a * d + b) :=
begin
  suffices : (λ x, x + b) '' ((λ x, a * x) '' Icc c d) = Icc (a * c + b) (a * d + b),
  { rwa set.image_image at this, },
  rw [image_mul_left_Icc' h, image_add_const_Icc],
end

end linear_ordered_field
end set


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

! This file was ported from Lean 3 source module data.finset.sym
! leanprover-community/mathlib commit cb3ceec8485239a61ed51d944cb9a95b68c6bafc
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Finset.Lattice
import Mathbin.Data.Fintype.Prod
import Mathbin.Data.Fintype.Vector
import Mathbin.Data.Sym.Sym2

/-!
# Symmetric powers of a finset

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

This file defines the symmetric powers of a finset as `finset (sym α n)` and `finset (sym2 α)`.

## Main declarations

* `finset.sym`: The symmetric power of a finset. `s.sym n` is all the multisets of cardinality `n`
  whose elements are in `s`.
* `finset.sym2`: The symmetric square of a finset. `s.sym2` is all the pairs whose elements are in
  `s`.

## TODO

`finset.sym` forms a Galois connection between `finset α` and `finset (sym α n)`. Similar for
`finset.sym2`.
-/

namespace Finset

variable {α : Type _} [DecidableEq α] {s t : Finset α} {a b : α}

#print Finset.isDiag_mk'_of_mem_diag /-
theorem isDiag_mk'_of_mem_diag {a : α × α} (h : a ∈ s.diag) : Sym2.IsDiag ⟦a⟧ :=
  (Sym2.isDiag_iff_proj_eq _).2 (mem_diag.1 h).2
#align finset.is_diag_mk_of_mem_diag Finset.isDiag_mk'_of_mem_diag
-/

#print Finset.not_isDiag_mk'_of_mem_offDiag /-
theorem not_isDiag_mk'_of_mem_offDiag {a : α × α} (h : a ∈ s.offDiag) : ¬Sym2.IsDiag ⟦a⟧ :=
  by
  rw [Sym2.isDiag_iff_proj_eq]
  exact (mem_off_diag.1 h).2.2
#align finset.not_is_diag_mk_of_mem_off_diag Finset.not_isDiag_mk'_of_mem_offDiag
-/

section Sym2

variable {m : Sym2 α}

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
#print Finset.sym2 /-
/-- Lifts a finset to `sym2 α`. `s.sym2` is the finset of all pairs with elements in `s`. -/
protected def sym2 (s : Finset α) : Finset (Sym2 α) :=
  (s ×ˢ s).image Quotient.mk'
#align finset.sym2 Finset.sym2
-/

#print Finset.mem_sym2_iff /-
@[simp]
theorem mem_sym2_iff : m ∈ s.Sym2 ↔ ∀ a ∈ m, a ∈ s :=
  by
  refine'
    mem_image.trans
      ⟨_, fun h => ⟨m.out, mem_product.2 ⟨h _ m.out_fst_mem, h _ m.out_snd_mem⟩, m.out_eq⟩⟩
  rintro ⟨⟨a, b⟩, h, rfl⟩
  rw [Sym2.ball]
  rwa [mem_product] at h
#align finset.mem_sym2_iff Finset.mem_sym2_iff
-/

#print Finset.mk'_mem_sym2_iff /-
theorem mk'_mem_sym2_iff : ⟦(a, b)⟧ ∈ s.Sym2 ↔ a ∈ s ∧ b ∈ s := by rw [mem_sym2_iff, Sym2.ball]
#align finset.mk_mem_sym2_iff Finset.mk'_mem_sym2_iff
-/

#print Finset.sym2_empty /-
@[simp]
theorem sym2_empty : (∅ : Finset α).Sym2 = ∅ :=
  rfl
#align finset.sym2_empty Finset.sym2_empty
-/

#print Finset.sym2_eq_empty /-
@[simp]
theorem sym2_eq_empty : s.Sym2 = ∅ ↔ s = ∅ := by
  rw [Finset.sym2, image_eq_empty, product_eq_empty, or_self_iff]
#align finset.sym2_eq_empty Finset.sym2_eq_empty
-/

#print Finset.sym2_nonempty /-
@[simp]
theorem sym2_nonempty : s.Sym2.Nonempty ↔ s.Nonempty := by
  rw [Finset.sym2, nonempty.image_iff, nonempty_product, and_self_iff]
#align finset.sym2_nonempty Finset.sym2_nonempty
-/

alias sym2_nonempty ↔ _ nonempty.sym2
#align finset.nonempty.sym2 Finset.nonempty.sym2

attribute [protected] nonempty.sym2

#print Finset.sym2_univ /-
@[simp]
theorem sym2_univ [Fintype α] : (univ : Finset α).Sym2 = univ :=
  rfl
#align finset.sym2_univ Finset.sym2_univ
-/

#print Finset.sym2_singleton /-
@[simp]
theorem sym2_singleton (a : α) : ({a} : Finset α).Sym2 = {Sym2.diag a} := by
  rw [Finset.sym2, singleton_product_singleton, image_singleton, Sym2.diag]
#align finset.sym2_singleton Finset.sym2_singleton
-/

#print Finset.diag_mem_sym2_iff /-
@[simp]
theorem diag_mem_sym2_iff : Sym2.diag a ∈ s.Sym2 ↔ a ∈ s :=
  mk'_mem_sym2_iff.trans <| and_self_iff _
#align finset.diag_mem_sym2_iff Finset.diag_mem_sym2_iff
-/

#print Finset.sym2_mono /-
@[simp]
theorem sym2_mono (h : s ⊆ t) : s.Sym2 ⊆ t.Sym2 := fun m he =>
  mem_sym2_iff.2 fun a ha => h <| mem_sym2_iff.1 he _ ha
#align finset.sym2_mono Finset.sym2_mono
-/

#print Finset.image_diag_union_image_offDiag /-
theorem image_diag_union_image_offDiag :
    s.diag.image Quotient.mk' ∪ s.offDiag.image Quotient.mk' = s.Sym2 :=
  by
  rw [← image_union, diag_union_off_diag]
  rfl
#align finset.image_diag_union_image_off_diag Finset.image_diag_union_image_offDiag
-/

end Sym2

section Sym

variable {n : ℕ} {m : Sym α n}

#print Finset.sym /-
/-- Lifts a finset to `sym α n`. `s.sym n` is the finset of all unordered tuples of cardinality `n`
with elements in `s`. -/
protected def sym (s : Finset α) : ∀ n, Finset (Sym α n)
  | 0 => {∅}
  | n + 1 => s.sup fun a => (Sym n).image <| Sym.cons a
#align finset.sym Finset.sym
-/

/- warning: finset.sym_zero -> Finset.sym_zero is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] {s : Finset.{u1} α}, Eq.{succ u1} (Finset.{u1} (Sym.{u1} α (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))))) (Finset.sym.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) (Singleton.singleton.{u1, u1} (Sym.{u1} α (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) (Finset.{u1} (Sym.{u1} α (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))))) (Finset.hasSingleton.{u1} (Sym.{u1} α (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero))))) (EmptyCollection.emptyCollection.{u1} (Sym.{u1} α (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) (Sym.hasEmptyc.{u1} α)))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] {s : Finset.{u1} α}, Eq.{succ u1} (Finset.{u1} (Sym.{u1} α (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)))) (Finset.sym.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) (Singleton.singleton.{u1, u1} (Sym.{u1} α (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) (Finset.{u1} (Sym.{u1} α (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)))) (Finset.instSingletonFinset.{u1} (Sym.{u1} α (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0)))) (EmptyCollection.emptyCollection.{u1} (Sym.{u1} α (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) (Sym.instEmptyCollectionSymOfNatNatInstOfNatNat.{u1} α)))
Case conversion may be inaccurate. Consider using '#align finset.sym_zero Finset.sym_zeroₓ'. -/
@[simp]
theorem sym_zero : s.Sym 0 = {∅} :=
  rfl
#align finset.sym_zero Finset.sym_zero

#print Finset.sym_succ /-
@[simp]
theorem sym_succ : s.Sym (n + 1) = s.sup fun a => (s.Sym n).image <| Sym.cons a :=
  rfl
#align finset.sym_succ Finset.sym_succ
-/

/- warning: finset.mem_sym_iff -> Finset.mem_sym_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] {s : Finset.{u1} α} {n : Nat} {m : Sym.{u1} α n}, Iff (Membership.Mem.{u1, u1} (Sym.{u1} α n) (Finset.{u1} (Sym.{u1} α n)) (Finset.hasMem.{u1} (Sym.{u1} α n)) m (Finset.sym.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s n)) (forall (a : α), (Membership.Mem.{u1, u1} α (Sym.{u1} α n) (Sym.hasMem.{u1} α n) a m) -> (Membership.Mem.{u1, u1} α (Finset.{u1} α) (Finset.hasMem.{u1} α) a s))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] {s : Finset.{u1} α} {n : Nat} {m : Sym.{u1} α n}, Iff (Membership.mem.{u1, u1} (Sym.{u1} α n) (Finset.{u1} (Sym.{u1} α n)) (Finset.instMembershipFinset.{u1} (Sym.{u1} α n)) m (Finset.sym.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s n)) (forall (a : α), (Membership.mem.{u1, u1} α (Sym.{u1} α n) (Sym.instMembershipSym.{u1} α n) a m) -> (Membership.mem.{u1, u1} α (Finset.{u1} α) (Finset.instMembershipFinset.{u1} α) a s))
Case conversion may be inaccurate. Consider using '#align finset.mem_sym_iff Finset.mem_sym_iffₓ'. -/
@[simp]
theorem mem_sym_iff : m ∈ s.Sym n ↔ ∀ a ∈ m, a ∈ s :=
  by
  induction' n with n ih
  · refine' mem_singleton.trans ⟨_, fun _ => Sym.eq_nil_of_card_zero _⟩
    rintro rfl
    exact fun a ha => ha.elim
  refine' mem_sup.trans ⟨_, fun h => _⟩
  · rintro ⟨a, ha, he⟩ b hb
    rw [mem_image] at he
    obtain ⟨m, he, rfl⟩ := he
    rw [Sym.mem_cons] at hb
    obtain rfl | hb := hb
    · exact ha
    · exact ih.1 he _ hb
  · obtain ⟨a, m, rfl⟩ := m.exists_eq_cons_of_succ
    exact
      ⟨a, h _ <| Sym.mem_cons_self _ _,
        mem_image_of_mem _ <| ih.2 fun b hb => h _ <| Sym.mem_cons_of_mem hb⟩
#align finset.mem_sym_iff Finset.mem_sym_iff

#print Finset.sym_empty /-
@[simp]
theorem sym_empty (n : ℕ) : (∅ : Finset α).Sym (n + 1) = ∅ :=
  rfl
#align finset.sym_empty Finset.sym_empty
-/

#print Finset.replicate_mem_sym /-
theorem replicate_mem_sym (ha : a ∈ s) (n : ℕ) : Sym.replicate n a ∈ s.Sym n :=
  mem_sym_iff.2 fun b hb => by rwa [(Sym.mem_replicate.1 hb).2]
#align finset.replicate_mem_sym Finset.replicate_mem_sym
-/

#print Finset.Nonempty.sym /-
protected theorem Nonempty.sym (h : s.Nonempty) (n : ℕ) : (s.Sym n).Nonempty :=
  let ⟨a, ha⟩ := h
  ⟨_, replicate_mem_sym ha n⟩
#align finset.nonempty.sym Finset.Nonempty.sym
-/

#print Finset.sym_singleton /-
@[simp]
theorem sym_singleton (a : α) (n : ℕ) : ({a} : Finset α).Sym n = {Sym.replicate n a} :=
  eq_singleton_iff_unique_mem.2
    ⟨replicate_mem_sym (mem_singleton.2 rfl) _, fun s hs =>
      Sym.eq_replicate_iff.2 fun b hb => eq_of_mem_singleton <| mem_sym_iff.1 hs _ hb⟩
#align finset.sym_singleton Finset.sym_singleton
-/

#print Finset.eq_empty_of_sym_eq_empty /-
theorem eq_empty_of_sym_eq_empty (h : s.Sym n = ∅) : s = ∅ :=
  by
  rw [← not_nonempty_iff_eq_empty] at h⊢
  exact fun hs => h (hs.Sym _)
#align finset.eq_empty_of_sym_eq_empty Finset.eq_empty_of_sym_eq_empty
-/

/- warning: finset.sym_eq_empty -> Finset.sym_eq_empty is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] {s : Finset.{u1} α} {n : Nat}, Iff (Eq.{succ u1} (Finset.{u1} (Sym.{u1} α n)) (Finset.sym.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s n) (EmptyCollection.emptyCollection.{u1} (Finset.{u1} (Sym.{u1} α n)) (Finset.hasEmptyc.{u1} (Sym.{u1} α n)))) (And (Ne.{1} Nat n (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) (Eq.{succ u1} (Finset.{u1} α) s (EmptyCollection.emptyCollection.{u1} (Finset.{u1} α) (Finset.hasEmptyc.{u1} α))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] {s : Finset.{u1} α} {n : Nat} {m : Sym.{u1} α n}, Iff (Eq.{succ u1} (Finset.{u1} (Sym.{u1} α n)) (Finset.sym.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s n) (EmptyCollection.emptyCollection.{u1} (Finset.{u1} (Sym.{u1} α n)) (Finset.instEmptyCollectionFinset.{u1} (Sym.{u1} α n)))) (And (Ne.{1} Nat n (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) (Eq.{succ u1} (Finset.{u1} α) s (EmptyCollection.emptyCollection.{u1} (Finset.{u1} α) (Finset.instEmptyCollectionFinset.{u1} α))))
Case conversion may be inaccurate. Consider using '#align finset.sym_eq_empty Finset.sym_eq_emptyₓ'. -/
@[simp]
theorem sym_eq_empty : s.Sym n = ∅ ↔ n ≠ 0 ∧ s = ∅ :=
  by
  cases n
  · exact iff_of_false (singleton_ne_empty _) fun h => (h.1 rfl).elim
  · refine' ⟨fun h => ⟨n.succ_ne_zero, eq_empty_of_sym_eq_empty h⟩, _⟩
    rintro ⟨_, rfl⟩
    exact sym_empty _
#align finset.sym_eq_empty Finset.sym_eq_empty

/- warning: finset.sym_nonempty -> Finset.sym_nonempty is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] {s : Finset.{u1} α} {n : Nat}, Iff (Finset.Nonempty.{u1} (Sym.{u1} α n) (Finset.sym.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s n)) (Or (Eq.{1} Nat n (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) (Finset.Nonempty.{u1} α s))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] {s : Finset.{u1} α} {n : Nat} {m : Sym.{u1} α n}, Iff (Finset.Nonempty.{u1} (Sym.{u1} α n) (Finset.sym.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s n)) (Or (Eq.{1} Nat n (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) (Finset.Nonempty.{u1} α s))
Case conversion may be inaccurate. Consider using '#align finset.sym_nonempty Finset.sym_nonemptyₓ'. -/
@[simp]
theorem sym_nonempty : (s.Sym n).Nonempty ↔ n = 0 ∨ s.Nonempty := by
  simp_rw [nonempty_iff_ne_empty, Ne.def, sym_eq_empty, not_and_or, not_ne_iff]
#align finset.sym_nonempty Finset.sym_nonempty

#print Finset.sym_univ /-
@[simp]
theorem sym_univ [Fintype α] (n : ℕ) : (univ : Finset α).Sym n = univ :=
  eq_univ_iff_forall.2 fun s => mem_sym_iff.2 fun a _ => mem_univ _
#align finset.sym_univ Finset.sym_univ
-/

#print Finset.sym_mono /-
@[simp]
theorem sym_mono (h : s ⊆ t) (n : ℕ) : s.Sym n ⊆ t.Sym n := fun m hm =>
  mem_sym_iff.2 fun a ha => h <| mem_sym_iff.1 hm _ ha
#align finset.sym_mono Finset.sym_mono
-/

/- warning: finset.sym_inter -> Finset.sym_inter is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] (s : Finset.{u1} α) (t : Finset.{u1} α) (n : Nat), Eq.{succ u1} (Finset.{u1} (Sym.{u1} α n)) (Finset.sym.{u1} α (fun (a : α) (b : α) => _inst_1 a b) (Inter.inter.{u1} (Finset.{u1} α) (Finset.hasInter.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) s t) n) (Inter.inter.{u1} (Finset.{u1} (Sym.{u1} α n)) (Finset.hasInter.{u1} (Sym.{u1} α n) (fun (a : Sym.{u1} α n) (b : Sym.{u1} α n) => Subtype.decidableEq.{u1} (Multiset.{u1} α) (fun (x : Multiset.{u1} α) => Eq.{1} Nat (coeFn.{succ u1, succ u1} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (fun (_x : AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) => (Multiset.{u1} α) -> Nat) (AddMonoidHom.hasCoeToFun.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.card.{u1} α) x) n) (fun (a : Multiset.{u1} α) (b : Multiset.{u1} α) => Multiset.decidableEq.{u1} α (fun (a : α) (b : α) => _inst_1 a b) a b) a b)) (Finset.sym.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s n) (Finset.sym.{u1} α (fun (a : α) (b : α) => _inst_1 a b) t n))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] (s : Finset.{u1} α) (t : Finset.{u1} α) (n : Nat), Eq.{succ u1} (Finset.{u1} (Sym.{u1} α n)) (Finset.sym.{u1} α (fun (a : α) (b : α) => _inst_1 a b) (Inter.inter.{u1} (Finset.{u1} α) (Finset.instInterFinset.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) s t) n) (Inter.inter.{u1} (Finset.{u1} (Sym.{u1} α n)) (Finset.instInterFinset.{u1} (Sym.{u1} α n) (fun (a : Sym.{u1} α n) (b : Sym.{u1} α n) => Finset.instDecidableEqSym.{u1} α (fun (x : α) (b : α) => _inst_1 x b) n a b)) (Finset.sym.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s n) (Finset.sym.{u1} α (fun (a : α) (b : α) => _inst_1 a b) t n))
Case conversion may be inaccurate. Consider using '#align finset.sym_inter Finset.sym_interₓ'. -/
@[simp]
theorem sym_inter (s t : Finset α) (n : ℕ) : (s ∩ t).Sym n = s.Sym n ∩ t.Sym n :=
  by
  ext m
  simp only [mem_inter, mem_sym_iff, imp_and, forall_and]
#align finset.sym_inter Finset.sym_inter

/- warning: finset.sym_union -> Finset.sym_union is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] (s : Finset.{u1} α) (t : Finset.{u1} α) (n : Nat), HasSubset.Subset.{u1} (Finset.{u1} (Sym.{u1} α n)) (Finset.hasSubset.{u1} (Sym.{u1} α n)) (Union.union.{u1} (Finset.{u1} (Sym.{u1} α n)) (Finset.hasUnion.{u1} (Sym.{u1} α n) (fun (a : Sym.{u1} α n) (b : Sym.{u1} α n) => Subtype.decidableEq.{u1} (Multiset.{u1} α) (fun (x : Multiset.{u1} α) => Eq.{1} Nat (coeFn.{succ u1, succ u1} (AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (fun (_x : AddMonoidHom.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) => (Multiset.{u1} α) -> Nat) (AddMonoidHom.hasCoeToFun.{u1, 0} (Multiset.{u1} α) Nat (AddMonoid.toAddZeroClass.{u1} (Multiset.{u1} α) (AddRightCancelMonoid.toAddMonoid.{u1} (Multiset.{u1} α) (AddCancelMonoid.toAddRightCancelMonoid.{u1} (Multiset.{u1} α) (AddCancelCommMonoid.toAddCancelMonoid.{u1} (Multiset.{u1} α) (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{u1} (Multiset.{u1} α) (Multiset.orderedCancelAddCommMonoid.{u1} α)))))) (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)) (Multiset.card.{u1} α) x) n) (fun (a : Multiset.{u1} α) (b : Multiset.{u1} α) => Multiset.decidableEq.{u1} α (fun (a : α) (b : α) => _inst_1 a b) a b) a b)) (Finset.sym.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s n) (Finset.sym.{u1} α (fun (a : α) (b : α) => _inst_1 a b) t n)) (Finset.sym.{u1} α (fun (a : α) (b : α) => _inst_1 a b) (Union.union.{u1} (Finset.{u1} α) (Finset.hasUnion.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) s t) n)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] (s : Finset.{u1} α) (t : Finset.{u1} α) (n : Nat), HasSubset.Subset.{u1} (Finset.{u1} (Sym.{u1} α n)) (Finset.instHasSubsetFinset.{u1} (Sym.{u1} α n)) (Union.union.{u1} (Finset.{u1} (Sym.{u1} α n)) (Finset.instUnionFinset.{u1} (Sym.{u1} α n) (fun (a : Sym.{u1} α n) (b : Sym.{u1} α n) => Finset.instDecidableEqSym.{u1} α (fun (x : α) (b : α) => _inst_1 x b) n a b)) (Finset.sym.{u1} α (fun (a : α) (b : α) => _inst_1 a b) s n) (Finset.sym.{u1} α (fun (a : α) (b : α) => _inst_1 a b) t n)) (Finset.sym.{u1} α (fun (a : α) (b : α) => _inst_1 a b) (Union.union.{u1} (Finset.{u1} α) (Finset.instUnionFinset.{u1} α (fun (a : α) (b : α) => _inst_1 a b)) s t) n)
Case conversion may be inaccurate. Consider using '#align finset.sym_union Finset.sym_unionₓ'. -/
@[simp]
theorem sym_union (s t : Finset α) (n : ℕ) : s.Sym n ∪ t.Sym n ⊆ (s ∪ t).Sym n :=
  union_subset (sym_mono (subset_union_left s t) n) (sym_mono (subset_union_right s t) n)
#align finset.sym_union Finset.sym_union

#print Finset.sym_fill_mem /-
theorem sym_fill_mem (a : α) {i : Fin (n + 1)} {m : Sym α (n - i)} (h : m ∈ s.Sym (n - i)) :
    m.fill a i ∈ (insert a s).Sym n :=
  mem_sym_iff.2 fun b hb =>
    mem_insert.2 <| (Sym.mem_fill_iff.1 hb).imp And.right <| mem_sym_iff.1 h b
#align finset.sym_fill_mem Finset.sym_fill_mem
-/

#print Finset.sym_filterNe_mem /-
theorem sym_filterNe_mem (a : α) (h : m ∈ s.Sym n) :
    (m.filter_ne a).2 ∈ (s.eraseₓ a).Sym (n - (m.filter_ne a).1) :=
  mem_sym_iff.2 fun b H =>
    mem_erase.2 <| (Multiset.mem_filter.1 H).symm.imp Ne.symm <| mem_sym_iff.1 h b
#align finset.sym_filter_ne_mem Finset.sym_filterNe_mem
-/

#print Finset.symInsertEquiv /-
/-- If `a` does not belong to the finset `s`, then the `n`th symmetric power of `{a} ∪ s` is
  in 1-1 correspondence with the disjoint union of the `n - i`th symmetric powers of `s`,
  for `0 ≤ i ≤ n`. -/
@[simps]
def symInsertEquiv (h : a ∉ s) : (insert a s).Sym n ≃ Σi : Fin (n + 1), s.Sym (n - i)
    where
  toFun m := ⟨_, (m.1.filter_ne a).2, by convert sym_filter_ne_mem a m.2 <;> rw [erase_insert h]⟩
  invFun m := ⟨m.2.1.fill a m.1, sym_fill_mem a m.2.2⟩
  left_inv m := Subtype.ext <| m.1.fill_filterNe a
  right_inv := fun ⟨i, m, hm⟩ =>
    by
    refine' (_ : id.injective).sigma_map (fun i => _) _
    · exact fun i => Sym α (n - i)
    swap; · exact fun _ _ => id
    swap; · exact Subtype.coe_injective
    refine' Eq.trans _ (Sym.filter_ne_fill a _ _)
    exacts[rfl, h ∘ mem_sym_iff.1 hm a]
#align finset.sym_insert_equiv Finset.symInsertEquiv
-/

end Sym

end Finset