import GMLInit.Data.Set.Basic
import GMLInit.Data.Set.Insert

set_option checkBinderAnnotations false in
class inductive Set.IsFinite : Set α → Prop
| protected empty : IsFinite Set.empty
| protected insert (s : Set α) [inst : IsFinite s] (a : α) : IsFinite (s.insert a)
attribute [instance] Set.IsFinite.empty Set.IsFinite.insert

namespace Set.IsFinite
open Set Set.Notation

protected instance pure (a : α) : IsFinite (Set.pure a) := by
  rw [←union_empty (Set.pure a)]
  infer_instance

protected instance union (s t : Set α) [hs : IsFinite s] [ht : IsFinite t] : IsFinite (s ∪ t) := by
  induction hs with
  | empty =>
    rw [empty_union]
    exact ht
  | insert s a H =>
    rw [insert_union_left]
    exact IsFinite.insert (inst:=H) ..

protected instance map (f : α → β) (s : Set α) [hs : IsFinite s] : IsFinite (s.map f) := by
  induction hs with
  | empty =>
    rw [empty_map]
    exact IsFinite.empty
  | insert s a H =>
    rw [insert_map]
    exact IsFinite.insert (inst:=H) ..

protected instance bind (f : α → Set β) [hf : (x : α) → IsFinite (f x)] (s : Set α) [hs : IsFinite s] : IsFinite (s.bind f) := by
  induction hs with
  | empty =>
    rw [empty_bind]
    exact IsFinite.empty
  | insert s a H =>
    rw [insert_bind]
    exact IsFinite.union (ht:=H) ..

protected instance seq (f : Set (α → β)) [hf : IsFinite f] (s : Set α) [hs : IsFinite s] : IsFinite (Set.seq f s) := by
  induction hs with
  | empty =>
    rw [empty_seq]
    exact IsFinite.empty
  | insert s a H =>
    rw [insert_seq]
    exact IsFinite.union (ht:=H) ..

protected instance seqLeft (s : Set α) [hs : IsFinite s] (t : Set β) [ht : IsFinite t] : IsFinite (Set.seqLeft s t) := by
  clean unfold Set.seqLeft
  infer_instance

protected instance seqRight (s : Set α) [hs : IsFinite s] (t : Set β) [ht : IsFinite t] : IsFinite (Set.seqRight s t) := by
  clean unfold Set.seqRight
  infer_instance

end Set.IsFinite

structure Finset (α) extends Set α where
  isFinite : toSet.IsFinite
attribute [instance] Finset.isFinite

instance (α) : CoeSort (Finset α) (Set α) := ⟨Finset.toSet⟩

protected theorem Finset.eq {α} : {s t : Finset α} → s.toSet = t.toSet → s = t
| ⟨_,_⟩, ⟨_,_⟩, rfl => rfl

protected theorem Finset.ext {α} {s t : Finset α} : (∀ x, s.Mem x ↔ t.Mem x) → s = t :=
  λ h => Finset.eq (Set.ext h)

instance : Monad Finset where
  pure a := ⟨pure a, Set.IsFinite.pure a⟩
  map f s := ⟨f <$> s.toSet, Set.IsFinite.map f s.toSet⟩
  bind s f := ⟨s.toSet >>= λ x => (f x).toSet, Set.IsFinite.bind (λ x => (f x).toSet) s.toSet⟩
  seq f s := ⟨f.toSet <*> (s ()).toSet, Set.IsFinite.seq f.toSet (s ()).toSet⟩
  seqLeft s t := ⟨s.toSet <* (t ()).toSet, Set.IsFinite.seqLeft s.toSet (t ()).toSet⟩
  seqRight s t := ⟨s.toSet *> (t ()).toSet, Set.IsFinite.seqRight s.toSet (t ()).toSet⟩

instance : LawfulMonad Finset where
  id_map := by
    intro _ ⟨_,_⟩
    apply Finset.eq
    simp only [Functor.map, Set.id_map]
  comp_map := by
    intro _ _ _ f g ⟨_,_⟩
    apply Finset.eq
    simp only [Functor.map, Set.comp_map]
  map_const := rfl
  map_pure f a := by
    apply Finset.eq
    simp only [Functor.map, Pure.pure, Set.map_pure]
  pure_bind := by
    intros
    apply Finset.eq
    simp only [Bind.bind, Pure.pure, Set.pure_bind]
  bind_assoc := by
    intros
    apply Finset.eq
    simp only [Bind.bind, Set.bind_assoc]
  bind_pure_comp := by
    intros
    apply Finset.eq
    simp only [Bind.bind, Pure.pure, Functor.map, Set.bind_pure_comp]
  bind_map := by
    intros
    apply Finset.eq
    simp only [Bind.bind, Functor.map, Seq.seq, Set.bind_map]
  pure_seq f s := by
    intros
    apply Finset.eq
    simp only [Seq.seq, Pure.pure, Functor.map, Set.pure_seq]
  seq_assoc := by
    intros
    apply Finset.eq
    simp only [Seq.seq, Functor.map, Set.seq_assoc]
  seqLeft_eq _ _ := rfl
  seqRight_eq _ _ := rfl


/-
Copyright (c) 2022 Georgi Kocharyan. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.txt.
Author: Georgi Kocharyan.
-/

import tactic
import data.real.basic
import topology.metric_space.isometry
import topology.metric_space.emetric_space
import data.set.finite
import algebra.big_operators

open_locale big_operators

open classical
open metric

@[class]
structure isom_action  (α : Type*) (β : Type*) [monoid α] [pseudo_metric_space β] extends mul_action α β :=
(isom : ∀ g : α, isometry (λ x : β , g•x))

/-instance (α : Type*) (β : Type*) [monoid α] [metric_space β]
  : has_coe (isom_action α β) (mul_action α β)
:= ⟨isom_action.to_mul_action⟩
-/
open metric

lemma isom_of_isom_action {α : Type*} {β : Type*} [monoid α] [pseudo_metric_space β]
[isom_action α β] :
∀ g : α, isometry (λ x : β , g•x)
:= isom_action.isom

lemma dist_of_isom {α : Type*} {β : Type*} [monoid α] [pseudo_metric_space β]
[isom_action α β] (g : α) (x y : β) : dist x y = dist (g • x) (g • y) :=
begin
  apply eq.symm,
  apply isometry.dist_eq,
  exact isom_of_isom_action g,
end

lemma dist_of_inv_isom (α : Type*) (β : Type*) [group α] [pseudo_metric_space β]
[isom_action α β] (g h : α) (x y : β) : dist (g • x) (h • y) = dist (x) ((g⁻¹*h) • y) :=
begin
  have k : dist (g • x) (h • y) = dist ((g⁻¹*g) • x) ((g⁻¹ * h) • y),
  repeat {rw ← smul_smul,},
  rw ← dist_of_isom (g⁻¹),
  rw k,
  simp,
  rw smul_smul,
end

def isom_img {α : Type*} {β : Type*} [monoid α] [pseudo_metric_space β]
[isom_action α β] (g : α) (s : set β) :=
(λ x : β , g•x) '' s
--{x : β | ∃ y : β, y ∈ s ∧ g•y = x}

-- das hätte ich gerne mit • notation

theorem isom_img_inv {α : Type*} {β : Type*} [group α] [pseudo_metric_space β]
[isom_action α β] (g : α) (s : set β) (x : β) (hx : x ∈ isom_img g s ) : g⁻¹ • x ∈ s :=
begin
rcases hx with ⟨ z, ⟨ zs, hz⟩ ⟩, dsimp at hz,
rw ← hz,
simp,
exact zs,
end

theorem isom_img_self {α : Type*} {β : Type*} [group α] [pseudo_metric_space β]
[isom_action α β] (g : α) (s : set β) {x : β} (hx : x ∈ s) : g • x ∈ isom_img g s :=
begin
use x,
split,
exact hx,
simp,
end

theorem isom_img_one {α : Type*} {β : Type*} [group α] [pseudo_metric_space β]
[isom_action α β] (s : set β) : s = isom_img (1:α) s :=
begin
apply subset_antisymm,
intros x hx,
use x,
split,
exact hx,
simp,
intros y hy,
rcases hy with ⟨z, ⟨hz1, hz2 ⟩⟩,
dsimp at hz2,
rw ← hz2,simp,
exact hz1,
end

theorem isom_img_mul {α : Type*} {β : Type*} [group α] [pseudo_metric_space β]
[isom_action α β] (g: α) (s : set β) (x : β) (hx : x ∈ isom_img g s ) (h : α): h • x ∈ isom_img (h*g) s  :=
begin
rcases hx with ⟨ z, ⟨ zs, hz⟩ ⟩, dsimp at hz,
rw ← hz,
use z,
split,
exact zs,
dsimp,
rw smul_smul,
end

lemma diam_preserved {α : Type*} {β : Type*} [monoid α] [pseudo_metric_space β]
[isom_action α β] (s : set β) (g : α) : diam (isom_img g s) = diam s :=
begin
rw isom_img,
apply isometry.diam_image,
exact isom_action.isom g,
end

 def set_closed_ball {α : Type*} [pseudo_metric_space α] (s : set α) (ε : ℝ):
set α :=
{y : α | ∃ x : α, x ∈ s ∧ dist y x ≤ ε}

lemma self_set_closed_ball {α : Type*} [pseudo_metric_space α] (s : set α) (ε : ℝ) (enonneg: ε ≥ 0) (x : α) :
x ∈ s → x ∈ set_closed_ball s ε :=
begin
intro xs,
use x,
split,
exact xs,
rw dist_self, exact enonneg,
end

theorem isom_of_set_closed_ball {α : Type*} {β : Type*} [group α] [pseudo_metric_space β] [isom_action α β]
 (s : set β) (g : α) (ε : ℝ): isom_img g (set_closed_ball s ε) = set_closed_ball (isom_img g s) ε :=
begin
apply subset_antisymm,
intros x hx,
rw set_closed_ball,
dsimp,
rcases hx with ⟨y, ⟨⟨z, ⟨hz1, hz2⟩⟩, hy2⟩ ⟩,
use g • z,
split,
use z, split,
exact hz1,
dsimp, refl,
dsimp at hy2,
rw ← hy2,
rw ← dist_of_isom, exact hz2,
-- before here could have had α monoid
rintros x ⟨y, ⟨⟨ z, ⟨hz1, hz2⟩⟩, hy2⟩⟩,
rw isom_img,
use g⁻¹ • x, split,
rw set_closed_ball, dsimp,
use z,
split,
{ exact hz1, },
dsimp at hz2,
have inv: z = g⁻¹ • y,
  {rw ← hz2, rw ← smul_assoc, simp,},
rw inv,
rw ← dist_of_isom, exact hy2, dsimp,
rw smul_smul,
simp,
end

theorem diam_of_ball_of_diam {α : Type*} [pseudo_metric_space α] (B : set α) (ε : ℝ) (epos: ε ≥ 0) : diam (set_closed_ball B ε ) ≤ 2*ε + diam B :=
begin
  apply diam_le_of_forall_dist_le,
  linarith [@diam_nonneg _ _ B],
  rintros x ⟨a, ⟨ha1, ha2 ⟩ ⟩ y ⟨b, ⟨hb1, hb2⟩ ⟩,
  calc
    dist x y ≤ dist x a + dist a y  : dist_triangle x a y
    ...      ≤ dist x a + dist a b + dist b y : begin rw add_assoc, apply add_le_add (le_refl (dist x a)) (dist_triangle a b y), end
    ...      = dist x a + dist b y + dist a b : by rw add_comm (dist a b) (dist b y)
    ...      ≤ ε + ε + dist a b : by sorry
    ...      ≤ 2*ε + diam B : by sorry
end

def proper_action_set (α : Type*) {β : Type*} [monoid α] [pseudo_metric_space β]
  [isom_action α β] (s : set β) : set α :=
   {g : α | s ∩ (isom_img g s) ≠ ∅}

def translates_cover (α : Type*) {β : Type*} [monoid α] [pseudo_metric_space β]
  [isom_action α β] (s : set β) : Prop :=
  ∀ x : β, x ∈ (⋃ g : α, isom_img g s)

theorem exists_cover_element {α : Type*} {β : Type*} [monoid α] [pseudo_metric_space β]
  [isom_action α β] {s : set β} (h : translates_cover α s) (x : β) : ∃ g : α, x ∈ isom_img g s  :=
  begin
  have hx : x ∈ (⋃ g : α, isom_img g s),
    apply h,
  rw set.mem_Union at hx,
  cases hx with i hi,
  use i, exact hi,
  end

  noncomputable def cover_element {α : Type*} {β : Type*} [monoid α] [pseudo_metric_space β]
  [isom_action α β] {s : set β} (h : translates_cover α s) (x : β) : α :=
  classical.some (exists_cover_element h x)


/-
Copyright (c) 2021 Rémy Degenne. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Zhouhang Zhou, Yury Kudryashov, Sébastien Gouëzel, Rémy Degenne
-/
import measure_theory.function.simple_func_dense

/-!
# Extension of a linear function from indicators to L1

Let `T : set α → E →L[ℝ] F` be additive for measurable sets with finite measure, in the sense that
for `s, t` two such sets, `s ∩ t = ∅ → T (s ∪ t) = T s + T t`. `T` is akin to a bilinear map on
`set α × E`, or a linear map on indicator functions.

This file constructs an extension of `T` to integrable simple functions, which are finite sums of
indicators of measurable sets with finite measure, then to integrable functions, which are limits of
integrable simple functions.

The main result is a continuous linear map `(α →₁[μ] E) →L[ℝ] F`. This extension process is used to
define the Bochner integral in the `measure_theory.integral.bochner` file and the conditional
expectation of an integrable function in `measure_theory.function.conditional_expectation`.

## Main Definitions

- `fin_meas_additive μ T`: the property that `T` is additive on measurable sets with finite measure.
  For two such sets, `s ∩ t = ∅ → T (s ∪ t) = T s + T t`.
- `dominated_fin_meas_additive μ T C`: `fin_meas_additive μ T ∧ ∀ s, ∥T s∥ ≤ C * (μ s).to_real`.
  This is the property needed to perform the extension from indicators to L1.
- `set_to_L1 (hT : dominated_fin_meas_additive μ T C) : (α →₁[μ] E) →L[ℝ] F`: the extension of `T`
  from indicators to L1.
- `set_to_fun (hT : dominated_fin_meas_additive μ T C) (f : α → E) : F`: a version of the extension
  which applies to functions (with value 0 if the function is not integrable).

## Implementation notes

The starting object `T : set α → E →L[ℝ] F` matters only through its restriction on measurable sets
with finite measure. Its value on other sets is ignored.

The extension step from integrable simple functions to L1 relies on a `second_countable_topology`
assumption. Without it, we could only extend to `ae_fin_strongly_measurable` functions. (TODO: this
might be worth doing?)

-/

noncomputable theory
open_locale classical topological_space big_operators nnreal ennreal measure_theory pointwise
open set filter topological_space ennreal emetric

local attribute [instance] fact_one_le_one_ennreal

namespace measure_theory

variables {α E F F' G 𝕜 : Type*} {p : ℝ≥0∞}
  [normed_group E] [measurable_space E] [normed_space ℝ E]
  [normed_group F] [normed_space ℝ F]
  [normed_group F'] [normed_space ℝ F']
  [normed_group G] [measurable_space G]
  {m : measurable_space α} {μ : measure α}

local infixr ` →ₛ `:25 := simple_func

open finset

section fin_meas_additive

/-- A set function is `fin_meas_additive` if its value on the union of two disjoint measurable
sets with finite measure is the sum of its values on each set. -/
def fin_meas_additive {β} [add_monoid β] {m : measurable_space α}
  (μ : measure α) (T : set α → β) : Prop :=
∀ s t, measurable_set s → measurable_set t → μ s ≠ ∞ → μ t ≠ ∞ → s ∩ t = ∅ → T (s ∪ t) = T s + T t

lemma map_empty_eq_zero_of_map_union {β} [add_cancel_monoid β] (T : set α → β)
  (h_add : fin_meas_additive μ T) :
  T ∅ = 0 :=
begin
  have h_empty : μ ∅ ≠ ∞, from (measure_empty.le.trans_lt ennreal.coe_lt_top).ne,
  specialize h_add ∅ ∅ measurable_set.empty measurable_set.empty h_empty h_empty
    (set.inter_empty ∅),
  rw set.union_empty at h_add,
  nth_rewrite 0 ← add_zero (T ∅) at h_add,
  exact (add_left_cancel h_add).symm,
end

lemma map_Union_fin_meas_set_eq_sum {β} [add_comm_monoid β] (T : set α → β) (T_empty : T ∅ = 0)
  (h_add : fin_meas_additive μ T)
  {ι} (S : ι → set α) (sι : finset ι) (hS_meas : ∀ i, measurable_set (S i))
  (hSp : ∀ i ∈ sι, μ (S i) ≠ ∞) (h_disj : ∀ i j ∈ sι, i ≠ j → disjoint (S i) (S j)) :
  T (⋃ i ∈ sι, S i) = ∑ i in sι, T (S i) :=
begin
  revert hSp h_disj,
  refine finset.induction_on sι _ _,
  { simp only [finset.not_mem_empty, forall_false_left, Union_false, Union_empty, sum_empty,
    forall_2_true_iff, implies_true_iff, forall_true_left, not_false_iff, T_empty], },
  intros a s has h hps h_disj,
  rw [finset.sum_insert has, ← h],
  swap, { exact λ i hi, hps i (finset.mem_insert_of_mem hi), },
  swap, { exact λ i j hi hj hij,
    h_disj i j (finset.mem_insert_of_mem hi) (finset.mem_insert_of_mem hj) hij, },
  rw ← h_add (S a) (⋃ i ∈ s, S i) (hS_meas a) (measurable_set_bUnion _ (λ i _, hS_meas i))
    (hps a (finset.mem_insert_self a s)),
  { congr, convert finset.supr_insert a s S, },
  { exact ((measure_bUnion_finset_le _ _).trans_lt $
      ennreal.sum_lt_top $ λ i hi, hps i $ finset.mem_insert_of_mem hi).ne, },
  { simp_rw set.inter_Union,
    refine Union_eq_empty.mpr (λ i, Union_eq_empty.mpr (λ hi, _)),
    rw ← set.disjoint_iff_inter_eq_empty,
    refine h_disj a i (finset.mem_insert_self a s) (finset.mem_insert_of_mem hi) (λ hai, _),
    rw ← hai at hi,
    exact has hi, },
end

/-- A `fin_meas_additive` set function whose norm on every set is less than the measure of the
set (up to a multiplicative constant). -/
def dominated_fin_meas_additive {β} [normed_group β] {m : measurable_space α}
  (μ : measure α) (T : set α → β) (C : ℝ) : Prop :=
fin_meas_additive μ T ∧ ∀ s, ∥T s∥ ≤ C * (μ s).to_real

end fin_meas_additive

namespace simple_func

/-- Extend `set α → (F →L[ℝ] F')` to `(α →ₛ F) → F'`. -/
def set_to_simple_func {m : measurable_space α} (T : set α → F →L[ℝ] F') (f : α →ₛ F) : F' :=
∑ x in f.range, T (f ⁻¹' {x}) x

@[simp] lemma set_to_simple_func_zero {m : measurable_space α} (f : α →ₛ F) :
  set_to_simple_func (0 : set α → F →L[ℝ] F') f = 0 :=
by simp [set_to_simple_func]

@[simp] lemma set_to_simple_func_zero_apply {m : measurable_space α} (T : set α → F →L[ℝ] F') :
  set_to_simple_func T (0 : α →ₛ F) = 0 :=
by casesI is_empty_or_nonempty α; simp [set_to_simple_func]

lemma set_to_simple_func_eq_sum_filter {m : measurable_space α}
  (T : set α → F →L[ℝ] F') (f : α →ₛ F) :
  set_to_simple_func T f = ∑ x in f.range.filter (λ x, x ≠ 0), (T (f ⁻¹' {x})) x :=
begin
  symmetry,
  refine sum_filter_of_ne (λ x hx, mt (λ hx0, _)),
  rw hx0,
  exact continuous_linear_map.map_zero _,
end

lemma set_to_simple_func_mono {G} [normed_linear_ordered_group G] [normed_space ℝ G]
  {m : measurable_space α}
  (T : set α → F →L[ℝ] G) (T' : set α → F →L[ℝ] G) (hTT' : ∀ s x, T s x ≤ T' s x) (f : α →ₛ F) :
  set_to_simple_func T f ≤ set_to_simple_func T' f :=
by { simp_rw set_to_simple_func, exact sum_le_sum (λ i hi, hTT' _ i), }

lemma map_set_to_simple_func (T : set α → F →L[ℝ] F') (h_add : fin_meas_additive μ T)
  {f : α →ₛ G} (hf : integrable f μ) {g : G → F} (hg : g 0 = 0) :
  (f.map g).set_to_simple_func T = ∑ x in f.range, T (f ⁻¹' {x}) (g x) :=
begin
  have T_empty : T ∅ = 0, from map_empty_eq_zero_of_map_union T h_add,
  have hfp : ∀ x ∈ f.range, x ≠ 0 → μ (f ⁻¹' {x}) ≠ ∞,
    from λ x hx hx0, (measure_preimage_lt_top_of_integrable f hf hx0).ne,
  simp only [set_to_simple_func, range_map],
  refine finset.sum_image' _ (assume b hb, _),
  rcases mem_range.1 hb with ⟨a, rfl⟩,
  by_cases h0 : g (f a) = 0,
  { simp_rw h0,
    rw [continuous_linear_map.map_zero, finset.sum_eq_zero (λ x hx, _)],
    rw mem_filter at hx,
    rw [hx.2, continuous_linear_map.map_zero], },
  have h_left_eq : T ((map g f) ⁻¹' {g (f a)}) (g (f a))
    = T (f ⁻¹' ↑(f.range.filter (λ b, g b = g (f a)))) (g (f a)),
  { congr, rw map_preimage_singleton, },
  rw h_left_eq,
  have h_left_eq' : T (f ⁻¹' ↑(filter (λ (b : G), g b = g (f a)) f.range)) (g (f a))
    = T (⋃ y ∈ (filter (λ (b : G), g b = g (f a)) f.range), f ⁻¹' {y}) (g (f a)),
  { congr, rw ← finset.set_bUnion_preimage_singleton, },
  rw h_left_eq',
  rw map_Union_fin_meas_set_eq_sum T T_empty h_add,
  { simp only [filter_congr_decidable, sum_apply, continuous_linear_map.coe_sum'],
    refine finset.sum_congr rfl (λ x hx, _),
    rw mem_filter at hx,
    rw hx.2, },
  { exact λ i, measurable_set_fiber _ _, },
  { intros i hi,
    rw mem_filter at hi,
    refine hfp i hi.1 (λ hi0, _),
    rw [hi0, hg] at hi,
    exact h0 hi.2.symm, },
  { intros i j hi hj hij,
    rw set.disjoint_iff,
    intros x hx,
    rw [set.mem_inter_iff, set.mem_preimage, set.mem_preimage, set.mem_singleton_iff,
      set.mem_singleton_iff] at hx,
    rw [← hx.1, ← hx.2] at hij,
    exact absurd rfl hij, },
end

lemma set_to_simple_func_congr' (T : set α → E →L[ℝ] F) (h_add : fin_meas_additive μ T)
  {f g : α →ₛ E} (hf : integrable f μ) (hg : integrable g μ)
  (h : ∀ x y, x ≠ y → T ((f ⁻¹' {x}) ∩ (g ⁻¹' {y})) = 0) :
  f.set_to_simple_func T = g.set_to_simple_func T :=
show ((pair f g).map prod.fst).set_to_simple_func T
  = ((pair f g).map prod.snd).set_to_simple_func T, from
begin
  have h_pair : integrable (f.pair g) μ, from integrable_pair hf hg,
  rw map_set_to_simple_func T h_add h_pair prod.fst_zero,
  rw map_set_to_simple_func T h_add h_pair prod.snd_zero,
  refine finset.sum_congr rfl (λ p hp, _),
  rcases mem_range.1 hp with ⟨a, rfl⟩,
  by_cases eq : f a = g a,
  { dsimp only [pair_apply], rw eq },
  { have : T ((pair f g) ⁻¹' {(f a, g a)}) = 0,
    { have h_eq : T (⇑(f.pair g) ⁻¹' {(f a, g a)}) = T ((f ⁻¹' {f a}) ∩ (g ⁻¹' {g a})),
      { congr, rw pair_preimage_singleton f g, },
      rw h_eq,
      exact h (f a) (g a) eq, },
    simp only [this, continuous_linear_map.zero_apply, pair_apply], },
end

lemma set_to_simple_func_congr (T : set α → (E →L[ℝ] F))
  (h_zero : ∀ s, measurable_set s → μ s = 0 → T s = 0) (h_add : fin_meas_additive μ T)
  {f g : α →ₛ E} (hf : integrable f μ) (h : f =ᵐ[μ] g) :
  f.set_to_simple_func T = g.set_to_simple_func T :=
begin
  refine set_to_simple_func_congr' T h_add hf ((integrable_congr h).mp hf) _,
  refine λ x y hxy, h_zero _ ((measurable_set_fiber f x).inter (measurable_set_fiber g y)) _,
  rw [eventually_eq, ae_iff] at h,
  refine measure_mono_null (λ z, _) h,
  simp_rw [set.mem_inter_iff, set.mem_set_of_eq, set.mem_preimage, set.mem_singleton_iff],
  intro h,
  rwa [h.1, h.2],
end

lemma set_to_simple_func_add_left {m : measurable_space α} (T T' : set α → F →L[ℝ] F')
  {f : α →ₛ F} :
  set_to_simple_func (T + T') f = set_to_simple_func T f + set_to_simple_func T' f :=
begin
  simp_rw [set_to_simple_func, pi.add_apply],
  push_cast,
  simp_rw [pi.add_apply, sum_add_distrib],
end

lemma set_to_simple_func_add_left' (T T' T'' : set α → E →L[ℝ] F)
  (h_add : ∀ s, measurable_set s → μ s ≠ ∞ → T'' s = T s + T' s) {f : α →ₛ E}
  (hf : integrable f μ) :
  set_to_simple_func (T'') f = set_to_simple_func T f + set_to_simple_func T' f :=
begin
  simp_rw [set_to_simple_func_eq_sum_filter],
  suffices : ∀ x ∈ filter (λ (x : E), x ≠ 0) f.range,
    T'' (f ⁻¹' {x}) = T (f ⁻¹' {x}) + T' (f ⁻¹' {x}),
  { rw ← sum_add_distrib,
    refine finset.sum_congr rfl (λ x hx, _),
    rw this x hx,
    push_cast,
    rw pi.add_apply, },
  intros x hx,
  refine h_add (f ⁻¹' {x}) (measurable_set_preimage _ _)
    (measure_preimage_lt_top_of_integrable _ hf _).ne,
  rw mem_filter at hx,
  exact hx.2,
end

lemma set_to_simple_func_add (T : set α → E →L[ℝ] F) (h_add : fin_meas_additive μ T)
  {f g : α →ₛ E} (hf : integrable f μ) (hg : integrable g μ) :
  set_to_simple_func T (f + g) = set_to_simple_func T f + set_to_simple_func T g :=
have hp_pair : integrable (f.pair g) μ, from integrable_pair hf hg,
calc set_to_simple_func T (f + g) = ∑ x in (pair f g).range,
       T ((pair f g) ⁻¹' {x}) (x.fst + x.snd) :
  by { rw [add_eq_map₂, map_set_to_simple_func T h_add hp_pair], simp, }
... = ∑ x in (pair f g).range, (T ((pair f g) ⁻¹' {x}) x.fst + T ((pair f g) ⁻¹' {x}) x.snd) :
  finset.sum_congr rfl $ assume a ha, continuous_linear_map.map_add _ _ _
... = ∑ x in (pair f g).range, T ((pair f g) ⁻¹' {x}) x.fst +
      ∑ x in (pair f g).range, T ((pair f g) ⁻¹' {x}) x.snd :
  by rw finset.sum_add_distrib
... = ((pair f g).map prod.fst).set_to_simple_func T
    + ((pair f g).map prod.snd).set_to_simple_func T :
  by rw [map_set_to_simple_func T h_add hp_pair prod.snd_zero,
    map_set_to_simple_func T h_add hp_pair prod.fst_zero]

lemma set_to_simple_func_neg (T : set α → E →L[ℝ] F) (h_add : fin_meas_additive μ T)
  {f : α →ₛ E} (hf : integrable f μ) :
  set_to_simple_func T (-f) = - set_to_simple_func T f :=
calc set_to_simple_func T (-f) = set_to_simple_func T (f.map (has_neg.neg)) : rfl
  ... = - set_to_simple_func T f :
  begin
    rw [map_set_to_simple_func T h_add hf neg_zero, set_to_simple_func,
      ← sum_neg_distrib],
    exact finset.sum_congr rfl (λ x h, continuous_linear_map.map_neg _ _),
  end

lemma set_to_simple_func_sub (T : set α → E →L[ℝ] F) (h_add : fin_meas_additive μ T)
  {f g : α →ₛ E} (hf : integrable f μ) (hg : integrable g μ) :
  set_to_simple_func T (f - g) = set_to_simple_func T f - set_to_simple_func T g :=
begin
  rw [sub_eq_add_neg, set_to_simple_func_add T h_add hf,
    set_to_simple_func_neg T h_add hg, sub_eq_add_neg],
  rw integrable_iff at hg ⊢,
  intros x hx_ne,
  change μ ((has_neg.neg ∘ g) ⁻¹' {x}) < ∞,
  rw [preimage_comp, neg_preimage, neg_singleton],
  refine hg (-x) _,
  simp [hx_ne],
end

lemma set_to_simple_func_smul_real (T : set α → E →L[ℝ] F) (h_add : fin_meas_additive μ T)
  (c : ℝ) {f : α →ₛ E} (hf : integrable f μ) :
  set_to_simple_func T (c • f) = c • set_to_simple_func T f :=
calc set_to_simple_func T (c • f) = ∑ x in f.range, T (f ⁻¹' {x}) (c • x) :
  by { rw [smul_eq_map c f, map_set_to_simple_func T h_add hf], rw smul_zero, }
... = ∑ x in f.range, c • (T (f ⁻¹' {x}) x) :
  finset.sum_congr rfl $ λ b hb, by { rw continuous_linear_map.map_smul (T (f ⁻¹' {b})) c b, }
... = c • set_to_simple_func T f :
by simp only [set_to_simple_func, smul_sum, smul_smul, mul_comm]

lemma set_to_simple_func_smul {E} [measurable_space E] [normed_group E] [normed_field 𝕜]
  [normed_space 𝕜 E] [normed_space ℝ E] [normed_space 𝕜 F] (T : set α → E →L[ℝ] F)
  (h_add : fin_meas_additive μ T) (h_smul : ∀ c : 𝕜, ∀ s x, T s (c • x) = c • T s x)
  (c : 𝕜) {f : α →ₛ E} (hf : integrable f μ) :
  set_to_simple_func T (c • f) = c • set_to_simple_func T f :=
calc set_to_simple_func T (c • f) = ∑ x in f.range, T (f ⁻¹' {x}) (c • x) :
  by { rw [smul_eq_map c f, map_set_to_simple_func T h_add hf],
    rw smul_zero, }
... = ∑ x in f.range, c • (T (f ⁻¹' {x}) x) : finset.sum_congr rfl $ λ b hb, by { rw h_smul, }
... = c • set_to_simple_func T f : by simp only [set_to_simple_func, smul_sum, smul_smul, mul_comm]

lemma norm_set_to_simple_func_le_sum_op_norm {m : measurable_space α}
  (T : set α → F' →L[ℝ] F) (f : α →ₛ F') :
  ∥f.set_to_simple_func T∥ ≤ ∑ x in f.range, ∥T (f ⁻¹' {x})∥ * ∥x∥ :=
calc ∥∑ x in f.range, T (f ⁻¹' {x}) x∥
    ≤ ∑ x in f.range, ∥T (f ⁻¹' {x}) x∥ : norm_sum_le _ _
... ≤ ∑ x in f.range, ∥T (f ⁻¹' {x})∥ * ∥x∥ :
  by { refine finset.sum_le_sum (λb hb, _), simp_rw continuous_linear_map.le_op_norm, }

lemma norm_set_to_simple_func_le_sum_mul_norm (T : set α → F →L[ℝ] F') {C : ℝ}
  (hT_norm : ∀ s, ∥T s∥ ≤ C * (μ s).to_real) (f : α →ₛ F) :
  ∥f.set_to_simple_func T∥ ≤ C * ∑ x in f.range, (μ (f ⁻¹' {x})).to_real * ∥x∥ :=
calc ∥f.set_to_simple_func T∥
    ≤ ∑ x in f.range, ∥T (f ⁻¹' {x})∥ * ∥x∥ : norm_set_to_simple_func_le_sum_op_norm T f
... ≤ ∑ x in f.range, C * (μ (f ⁻¹' {x})).to_real * ∥x∥ :
  begin
    refine finset.sum_le_sum (λ b hb, _),
    by_cases hb : ∥b∥ = 0,
    { rw hb, simp, },
    rw _root_.mul_le_mul_right _,
    { exact hT_norm _, },
    { exact lt_of_le_of_ne (norm_nonneg _) (ne.symm hb), },
  end
... ≤ C * ∑ x in f.range, (μ (f ⁻¹' {x})).to_real * ∥x∥ : by simp_rw [mul_sum, ← mul_assoc]

lemma set_to_simple_func_indicator (T : set α → F →L[ℝ] F') (hT_empty : T ∅ = 0)
  {m : measurable_space α} {s : set α} (hs : measurable_set s) (x : F) :
  simple_func.set_to_simple_func T
    (simple_func.piecewise s hs (simple_func.const α x) (simple_func.const α 0))
  = T s x :=
begin
  by_cases hs_empty : s = ∅,
  { simp only [hs_empty, hT_empty, continuous_linear_map.zero_apply, piecewise_empty, const_zero,
    set_to_simple_func_zero_apply], },
  by_cases hs_univ : s = univ,
  { casesI hα : is_empty_or_nonempty α,
    { refine absurd _ hs_empty,
      haveI : subsingleton (set α), by { unfold set, apply_instance, },
      exact subsingleton.elim s ∅, },
    simp [hs_univ, set_to_simple_func], },
  simp_rw set_to_simple_func,
  rw [← ne.def, set.ne_empty_iff_nonempty] at hs_empty,
  rw range_indicator hs hs_empty hs_univ,
  by_cases hx0 : x = 0,
  { simp_rw hx0, simp, },
  rw sum_insert,
  swap, { rw finset.mem_singleton, exact hx0, },
  rw [sum_singleton, (T _).map_zero, add_zero],
  congr,
  simp only [coe_piecewise, piecewise_eq_indicator, coe_const, pi.const_zero,
    piecewise_eq_indicator],
  rw [indicator_preimage, preimage_const_of_mem],
  swap, { exact set.mem_singleton x, },
  rw [← pi.const_zero, preimage_const_of_not_mem],
  swap, { rw set.mem_singleton_iff, exact ne.symm hx0, },
  simp,
end

end simple_func

namespace L1

open ae_eq_fun Lp.simple_func Lp

variables {α E μ}

namespace simple_func

lemma norm_eq_sum_mul [second_countable_topology G] [borel_space G] (f : α →₁ₛ[μ] G) :
  ∥f∥ = ∑ x in (to_simple_func f).range, (μ ((to_simple_func f) ⁻¹' {x})).to_real * ∥x∥ :=
begin
  rw [norm_to_simple_func, snorm_one_eq_lintegral_nnnorm],
  have h_eq := simple_func.map_apply (λ x, (nnnorm x : ℝ≥0∞)) (to_simple_func f),
  dsimp only at h_eq,
  simp_rw ← h_eq,
  rw [simple_func.lintegral_eq_lintegral, simple_func.map_lintegral, ennreal.to_real_sum],
  { congr,
    ext1 x,
    rw [ennreal.to_real_mul, mul_comm, ← of_real_norm_eq_coe_nnnorm,
      ennreal.to_real_of_real (norm_nonneg _)], },
  { intros x hx,
    by_cases hx0 : x = 0,
    { rw hx0, simp, },
    { exact ennreal.mul_ne_top ennreal.coe_ne_top
        (simple_func.measure_preimage_lt_top_of_integrable _ (simple_func.integrable f) hx0).ne } }
end

section set_to_L1s

variables [second_countable_topology E] [borel_space E] [normed_field 𝕜] [normed_space 𝕜 E]

local attribute [instance] Lp.simple_func.module
local attribute [instance] Lp.simple_func.normed_space

/-- Extend `set α → (E →L[ℝ] F')` to `(α →₁ₛ[μ] E) → F'`. -/
def set_to_L1s (T : set α → E →L[ℝ] F) (f : α →₁ₛ[μ] E) : F :=
(to_simple_func f).set_to_simple_func T

lemma set_to_L1s_eq_set_to_simple_func (T : set α → E →L[ℝ] F) (f : α →₁ₛ[μ] E) :
  set_to_L1s T f = (to_simple_func f).set_to_simple_func T :=
rfl

lemma set_to_L1s_congr (T : set α → E →L[ℝ] F) (h_zero : ∀ s, measurable_set s → μ s = 0 → T s = 0)
  (h_add : fin_meas_additive μ T)
  {f g : α →₁ₛ[μ] E} (h : to_simple_func f =ᵐ[μ] to_simple_func g) :
  set_to_L1s T f = set_to_L1s T g :=
simple_func.set_to_simple_func_congr T h_zero h_add (simple_func.integrable f) h

lemma set_to_L1s_add (T : set α → E →L[ℝ] F) (h_zero : ∀ s, measurable_set s → μ s = 0 → T s = 0)
  (h_add : fin_meas_additive μ T) (f g : α →₁ₛ[μ] E) :
  set_to_L1s T (f + g) = set_to_L1s T f + set_to_L1s T g :=
begin
  simp_rw set_to_L1s,
  rw ← simple_func.set_to_simple_func_add T h_add
    (simple_func.integrable f) (simple_func.integrable g),
  exact simple_func.set_to_simple_func_congr T h_zero h_add (simple_func.integrable _)
    (add_to_simple_func f g),
end

lemma set_to_L1s_smul_real (T : set α → E →L[ℝ] F)
  (h_zero : ∀ s, measurable_set s → μ s = 0 → T s = 0) (h_add : fin_meas_additive μ T)
  (c : ℝ) (f : α →₁ₛ[μ] E) :
  set_to_L1s T (c • f) = c • set_to_L1s T f :=
begin
  simp_rw set_to_L1s,
  rw ← simple_func.set_to_simple_func_smul_real T h_add c (simple_func.integrable f),
  refine simple_func.set_to_simple_func_congr T h_zero h_add (simple_func.integrable _) _,
  exact smul_to_simple_func c f,
end

lemma set_to_L1s_smul {E} [normed_group E] [measurable_space E] [normed_space ℝ E]
  [normed_space 𝕜 E] [second_countable_topology E] [borel_space E] [normed_space 𝕜 F]
  [measurable_space 𝕜] [opens_measurable_space 𝕜]
  (T : set α → E →L[ℝ] F) (h_zero : ∀ s, measurable_set s → μ s = 0 → T s = 0)
  (h_add : fin_meas_additive μ T)
  (h_smul : ∀ c : 𝕜, ∀ s x, T s (c • x) = c • T s x) (c : 𝕜) (f : α →₁ₛ[μ] E) :
  set_to_L1s T (c • f) = c • set_to_L1s T f :=
begin
  simp_rw set_to_L1s,
  rw ← simple_func.set_to_simple_func_smul T h_add h_smul c (simple_func.integrable f),
  refine simple_func.set_to_simple_func_congr T h_zero h_add (simple_func.integrable _) _,
  exact smul_to_simple_func c f,
end

lemma norm_set_to_L1s_le (T : set α → E →L[ℝ] F) {C : ℝ} (hT_norm : ∀ s, ∥T s∥ ≤ C * (μ s).to_real)
  (f : α →₁ₛ[μ] E) :
  ∥set_to_L1s T f∥ ≤ C * ∥f∥ :=
begin
  rw [set_to_L1s, norm_eq_sum_mul f],
  exact simple_func.norm_set_to_simple_func_le_sum_mul_norm T hT_norm _,
end

lemma set_to_L1s_indicator_const {T : set α → E →L[ℝ] F} {C : ℝ} {s : set α}
  (hT : dominated_fin_meas_additive μ T C) (hs : measurable_set s) (hμs : μ s ≠ ∞) (x : E) :
  set_to_L1s T (simple_func.indicator_const 1 hs hμs x) = T s x :=
begin
  have h_zero : ∀ s (hs : measurable_set s) (hs_zero : μ s = 0), T s = 0,
  { refine λ s hs hs0, norm_eq_zero.mp _,
    refine le_antisymm ((hT.2 s).trans (le_of_eq _)) (norm_nonneg _),
    rw [hs0, ennreal.zero_to_real, mul_zero], },
  have h_empty : T ∅ = 0, from h_zero ∅ measurable_set.empty measure_empty,
  rw set_to_L1s_eq_set_to_simple_func,
  refine eq.trans _ (simple_func.set_to_simple_func_indicator T h_empty hs x),
  refine simple_func.set_to_simple_func_congr T h_zero hT.1 (simple_func.integrable _) _,
  exact Lp.simple_func.to_simple_func_indicator_const hs hμs x,
end

variables [normed_space 𝕜 F] [measurable_space 𝕜] [opens_measurable_space 𝕜]

variables (α E μ 𝕜)
/-- Extend `set α → E →L[ℝ] F` to `(α →₁ₛ[μ] E) →L[𝕜] F`. -/
def set_to_L1s_clm' {T : set α → E →L[ℝ] F} {C : ℝ} (hT : dominated_fin_meas_additive μ T C)
  (h_smul : ∀ c : 𝕜, ∀ s x, T s (c • x) = c • T s x) :
  (α →₁ₛ[μ] E) →L[𝕜] F :=
have h_zero : ∀ s (hs : measurable_set s) (hs_zero : μ s = 0), T s = 0,
{ refine λ s hs hs0, norm_eq_zero.mp _,
  refine le_antisymm ((hT.2 s).trans (le_of_eq _)) (norm_nonneg _),
  rw [hs0, ennreal.zero_to_real, mul_zero], },
linear_map.mk_continuous ⟨set_to_L1s T, set_to_L1s_add T h_zero hT.1,
  set_to_L1s_smul T h_zero hT.1 h_smul⟩ C (λ f, norm_set_to_L1s_le T hT.2 f)

/-- Extend `set α → E →L[ℝ] F` to `(α →₁ₛ[μ] E) →L[ℝ] F`. -/
def set_to_L1s_clm {T : set α → E →L[ℝ] F} {C : ℝ} (hT : dominated_fin_meas_additive μ T C) :
  (α →₁ₛ[μ] E) →L[ℝ] F :=
have h_zero : ∀ s (hs : measurable_set s) (hs_zero : μ s = 0), T s = 0,
{ refine λ s hs hs0, norm_eq_zero.mp _,
  refine le_antisymm ((hT.2 s).trans (le_of_eq _)) (norm_nonneg _),
  rw [hs0, ennreal.zero_to_real, mul_zero], },
linear_map.mk_continuous ⟨set_to_L1s T, set_to_L1s_add T h_zero hT.1,
  set_to_L1s_smul_real T h_zero hT.1⟩ C (λ f, norm_set_to_L1s_le T hT.2 f)

variables {α E μ 𝕜}

lemma norm_set_to_L1s_clm_le {T : set α → E →L[ℝ] F} {C : ℝ}
  (hT : dominated_fin_meas_additive μ T C) (hC : 0 ≤ C) :
  ∥set_to_L1s_clm α E μ hT∥ ≤ C :=
linear_map.mk_continuous_norm_le _ hC _

lemma norm_set_to_L1s_clm_le' {T : set α → E →L[ℝ] F} {C : ℝ}
  (hT : dominated_fin_meas_additive μ T C) :
  ∥set_to_L1s_clm α E μ hT∥ ≤ max C 0 :=
linear_map.mk_continuous_norm_le' _ _

end set_to_L1s

end simple_func

open simple_func

section set_to_L1

local attribute [instance] Lp.simple_func.module
local attribute [instance] Lp.simple_func.normed_space

variables (𝕜) [nondiscrete_normed_field 𝕜] [measurable_space 𝕜] [opens_measurable_space 𝕜]
  [second_countable_topology E] [borel_space E] [normed_space 𝕜 E]
  [normed_space 𝕜 F] [complete_space F]
  {T : set α → E →L[ℝ] F} {C : ℝ}

/-- Extend `set α → (E →L[ℝ] F)` to `(α →₁[μ] E) →L[𝕜] F`. -/
def set_to_L1' (hT : dominated_fin_meas_additive μ T C)
  (h_smul : ∀ c : 𝕜, ∀ s x, T s (c • x) = c • T s x) :
  (α →₁[μ] E) →L[𝕜] F :=
(set_to_L1s_clm' α E 𝕜 μ hT h_smul).extend
  (coe_to_Lp α E 𝕜) (simple_func.dense_range one_ne_top) simple_func.uniform_inducing

variables {𝕜}

/-- Extend `set α → E →L[ℝ] F` to `(α →₁[μ] E) →L[ℝ] F`. -/
def set_to_L1 (hT : dominated_fin_meas_additive μ T C) : (α →₁[μ] E) →L[ℝ] F :=
(set_to_L1s_clm α E μ hT).extend
  (coe_to_Lp α E ℝ) (simple_func.dense_range one_ne_top) simple_func.uniform_inducing

lemma set_to_L1_eq_set_to_L1s_clm (hT : dominated_fin_meas_additive μ T C) (f : α →₁ₛ[μ] E) :
  set_to_L1 hT f = set_to_L1s_clm α E μ hT f :=
uniformly_extend_of_ind simple_func.uniform_inducing (simple_func.dense_range one_ne_top)
  (set_to_L1s_clm α E μ hT).uniform_continuous _

lemma set_to_L1_eq_set_to_L1' (hT : dominated_fin_meas_additive μ T C)
  (h_smul : ∀ c : 𝕜, ∀ s x, T s (c • x) = c • T s x) (f : α →₁[μ] E) :
  set_to_L1 hT f = set_to_L1' 𝕜 hT h_smul f :=
rfl

lemma set_to_L1_smul (hT : dominated_fin_meas_additive μ T C)
  (h_smul : ∀ c : 𝕜, ∀ s x, T s (c • x) = c • T s x) (c : 𝕜) (f : α →₁[μ] E) :
  set_to_L1 hT (c • f) = c • set_to_L1 hT f :=
begin
  rw [set_to_L1_eq_set_to_L1' hT h_smul, set_to_L1_eq_set_to_L1' hT h_smul],
  exact continuous_linear_map.map_smul _ _ _,
end

lemma set_to_L1_indicator_const_Lp (hT : dominated_fin_meas_additive μ T C) {s : set α}
  (hs : measurable_set s) (hμs : μ s ≠ ∞) (x : E) :
  set_to_L1 hT (indicator_const_Lp 1 hs hμs x) = T s x :=
begin
  rw [← Lp.simple_func.coe_indicator_const hs hμs x, set_to_L1_eq_set_to_L1s_clm],
  exact set_to_L1s_indicator_const hT hs hμs x,
end

lemma norm_set_to_L1_le_norm_set_to_L1s_clm (hT : dominated_fin_meas_additive μ T C) :
  ∥set_to_L1 hT∥ ≤ ∥set_to_L1s_clm α E μ hT∥ :=
calc ∥set_to_L1 hT∥
    ≤ (1 : ℝ≥0) * ∥set_to_L1s_clm α E μ hT∥ : begin
      refine continuous_linear_map.op_norm_extend_le (set_to_L1s_clm α E μ hT) (coe_to_Lp α E ℝ)
        (simple_func.dense_range one_ne_top) (λ x, le_of_eq _),
      rw [nnreal.coe_one, one_mul],
      refl,
    end
... = ∥set_to_L1s_clm α E μ hT∥ : by rw [nnreal.coe_one, one_mul]

lemma norm_set_to_L1_le_mul_norm (hT : dominated_fin_meas_additive μ T C) (hC : 0 ≤ C)
  (f : α →₁[μ] E) :
  ∥set_to_L1 hT f∥ ≤ C * ∥f∥ :=
calc ∥set_to_L1 hT f∥
    ≤ ∥set_to_L1s_clm α E μ hT∥ * ∥f∥ :
  continuous_linear_map.le_of_op_norm_le _ (norm_set_to_L1_le_norm_set_to_L1s_clm hT) _
... ≤ C * ∥f∥ : mul_le_mul (norm_set_to_L1s_clm_le hT hC) le_rfl (norm_nonneg _) hC

lemma norm_set_to_L1_le_mul_norm' (hT : dominated_fin_meas_additive μ T C) (f : α →₁[μ] E) :
  ∥set_to_L1 hT f∥ ≤ max C 0 * ∥f∥ :=
calc ∥set_to_L1 hT f∥
    ≤ ∥set_to_L1s_clm α E μ hT∥ * ∥f∥ :
  continuous_linear_map.le_of_op_norm_le _ (norm_set_to_L1_le_norm_set_to_L1s_clm hT) _
... ≤ max C 0 * ∥f∥ :
  mul_le_mul (norm_set_to_L1s_clm_le' hT) le_rfl (norm_nonneg _) (le_max_right _ _)

lemma norm_set_to_L1_le (hT : dominated_fin_meas_additive μ T C) (hC : 0 ≤ C) :
  ∥set_to_L1 hT∥ ≤ C :=
continuous_linear_map.op_norm_le_bound _ hC (norm_set_to_L1_le_mul_norm hT hC)

lemma norm_set_to_L1_le' (hT : dominated_fin_meas_additive μ T C) :
  ∥set_to_L1 hT∥ ≤ max C 0 :=
continuous_linear_map.op_norm_le_bound _ (le_max_right _ _) (norm_set_to_L1_le_mul_norm' hT)

lemma set_to_L1_lipschitz (hT : dominated_fin_meas_additive μ T C) :
  lipschitz_with (real.to_nnreal C) (set_to_L1 hT) :=
(set_to_L1 hT).lipschitz.weaken (norm_set_to_L1_le' hT)

/-- If `fs i → f` in `L1`, then `set_to_L1 hT (fs i) → set_to_L1 hT f`. -/
lemma tendsto_set_to_L1 (hT : dominated_fin_meas_additive μ T C) (f : α →₁[μ] E)
  {ι} (fs : ι → α →₁[μ] E) {l : filter ι} (hfs : tendsto fs l (𝓝 f)) :
  tendsto (λ i, set_to_L1 hT (fs i)) l (𝓝 $ set_to_L1 hT f) :=
((set_to_L1 hT).continuous.tendsto _).comp hfs

end set_to_L1

end L1

section function

variables [second_countable_topology E] [borel_space E] [complete_space F]
  {T : set α → E →L[ℝ] F} {C : ℝ} {f g : α → E}

/-- Extend `T : set α → E →L[ℝ] F` to `(α → E) → F` (for integrable functions `α → E`). We set it to
0 if the function is not integrable. -/
def set_to_fun (hT : dominated_fin_meas_additive μ T C) (f : α → E) : F :=
if hf : integrable f μ then L1.set_to_L1 hT (hf.to_L1 f) else 0

lemma set_to_fun_eq (hT : dominated_fin_meas_additive μ T C) (hf : integrable f μ) :
  set_to_fun hT f = L1.set_to_L1 hT (hf.to_L1 f) :=
dif_pos hf

lemma L1.set_to_fun_eq_set_to_L1 (hT : dominated_fin_meas_additive μ T C) (f : α →₁[μ] E) :
  set_to_fun hT f = L1.set_to_L1 hT f :=
by rw [set_to_fun_eq hT (L1.integrable_coe_fn f), integrable.to_L1_coe_fn]

lemma set_to_fun_undef (hT : dominated_fin_meas_additive μ T C) (hf : ¬ integrable f μ) :
  set_to_fun hT f = 0 :=
dif_neg hf

lemma set_to_fun_non_ae_measurable (hT : dominated_fin_meas_additive μ T C)
  (hf : ¬ ae_measurable f μ) :
  set_to_fun hT f = 0 :=
set_to_fun_undef hT (not_and_of_not_left _ hf)

@[simp] lemma set_to_fun_zero (hT : dominated_fin_meas_additive μ T C) :
  set_to_fun hT (0 : α → E) = 0 :=
begin
  rw set_to_fun_eq hT,
  { simp only [integrable.to_L1_zero, continuous_linear_map.map_zero], },
  { exact integrable_zero _ _ _, },
end

lemma set_to_fun_add (hT : dominated_fin_meas_additive μ T C)
  (hf : integrable f μ) (hg : integrable g μ) :
  set_to_fun hT (f + g) = set_to_fun hT f + set_to_fun hT g :=
by rw [set_to_fun_eq hT (hf.add hg), set_to_fun_eq hT hf, set_to_fun_eq hT hg, integrable.to_L1_add,
  (L1.set_to_L1 hT).map_add]

lemma set_to_fun_neg (hT : dominated_fin_meas_additive μ T C) (f : α → E) :
  set_to_fun hT (-f) = - set_to_fun hT f :=
begin
  by_cases hf : integrable f μ,
  { rw [set_to_fun_eq hT hf, set_to_fun_eq hT hf.neg,
      integrable.to_L1_neg, (L1.set_to_L1 hT).map_neg], },
  { rw [set_to_fun_undef hT hf, set_to_fun_undef hT, neg_zero],
    rwa [← integrable_neg_iff] at hf, }
end

lemma set_to_fun_sub (hT : dominated_fin_meas_additive μ T C)
  (hf : integrable f μ) (hg : integrable g μ) :
  set_to_fun hT (f - g) = set_to_fun hT f - set_to_fun hT g :=
by rw [sub_eq_add_neg, sub_eq_add_neg, set_to_fun_add hT hf hg.neg, set_to_fun_neg hT g]

lemma set_to_fun_smul [nondiscrete_normed_field 𝕜] [measurable_space 𝕜] [opens_measurable_space 𝕜]
  [normed_space 𝕜 E] [normed_space 𝕜 F] (hT : dominated_fin_meas_additive μ T C)
  (h_smul : ∀ c : 𝕜, ∀ s x, T s (c • x) = c • T s x) (c : 𝕜) (f : α → E) :
  set_to_fun hT (c • f) = c • set_to_fun hT f :=
begin
  by_cases hf : integrable f μ,
  { rw [set_to_fun_eq hT hf, set_to_fun_eq hT, integrable.to_L1_smul',
      L1.set_to_L1_smul hT h_smul c _], },
  { by_cases hr : c = 0,
    { rw hr, simp, },
    { have hf' : ¬ integrable (c • f) μ, by rwa [integrable_smul_iff hr f],
      rw [set_to_fun_undef hT hf, set_to_fun_undef hT hf',
        smul_zero], }, },
end

lemma set_to_fun_congr_ae (hT : dominated_fin_meas_additive μ T C) (h : f =ᵐ[μ] g) :
  set_to_fun hT f = set_to_fun hT g :=
begin
  by_cases hfi : integrable f μ,
  { have hgi : integrable g μ := hfi.congr h,
    rw [set_to_fun_eq hT hfi, set_to_fun_eq hT hgi,
      (integrable.to_L1_eq_to_L1_iff f g hfi hgi).2 h] },
  { have hgi : ¬ integrable g μ, { rw integrable_congr h at hfi, exact hfi },
    rw [set_to_fun_undef hT hfi, set_to_fun_undef hT hgi] },
end

lemma set_to_fun_to_L1 (hT : dominated_fin_meas_additive μ T C) (hf : integrable f μ) :
  set_to_fun hT (hf.to_L1 f) = set_to_fun hT f :=
set_to_fun_congr_ae hT hf.coe_fn_to_L1

lemma set_to_fun_indicator_const (hT : dominated_fin_meas_additive μ T C) {s : set α}
  (hs : measurable_set s) (hμs : μ s ≠ ∞) (x : E) :
  set_to_fun hT (s.indicator (λ _, x)) = T s x :=
begin
  rw set_to_fun_congr_ae hT (@indicator_const_Lp_coe_fn _ _ _ 1 _ _ _ _ hs hμs x _ _).symm,
  rw L1.set_to_fun_eq_set_to_L1 hT,
  exact L1.set_to_L1_indicator_const_Lp hT hs hμs x,
end

@[continuity]
lemma continuous_set_to_fun (hT : dominated_fin_meas_additive μ T C) :
  continuous (λ (f : α →₁[μ] E), set_to_fun hT f) :=
by { simp_rw L1.set_to_fun_eq_set_to_L1 hT, exact continuous_linear_map.continuous _, }

lemma norm_set_to_fun_le_mul_norm (hT : dominated_fin_meas_additive μ T C) (f : α →₁[μ] E)
  (hC : 0 ≤ C) :
  ∥set_to_fun hT f∥ ≤ C * ∥f∥ :=
by { rw L1.set_to_fun_eq_set_to_L1, exact L1.norm_set_to_L1_le_mul_norm hT hC f, }

lemma norm_set_to_fun_le_mul_norm' (hT : dominated_fin_meas_additive μ T C) (f : α →₁[μ] E) :
  ∥set_to_fun hT f∥ ≤ max C 0 * ∥f∥ :=
by { rw L1.set_to_fun_eq_set_to_L1, exact L1.norm_set_to_L1_le_mul_norm' hT f, }

lemma norm_set_to_fun_le (hT : dominated_fin_meas_additive μ T C) (hf : integrable f μ)
  (hC : 0 ≤ C) :
  ∥set_to_fun hT f∥ ≤ C * ∥hf.to_L1 f∥ :=
by { rw set_to_fun_eq hT hf, exact L1.norm_set_to_L1_le_mul_norm hT hC _, }

lemma norm_set_to_fun_le' (hT : dominated_fin_meas_additive μ T C) (hf : integrable f μ) :
  ∥set_to_fun hT f∥ ≤ max C 0 * ∥hf.to_L1 f∥ :=
by { rw set_to_fun_eq hT hf, exact L1.norm_set_to_L1_le_mul_norm' hT _, }

/-- Lebesgue dominated convergence theorem provides sufficient conditions under which almost
  everywhere convergence of a sequence of functions implies the convergence of their image by
  `set_to_fun`.
  We could weaken the condition `bound_integrable` to require `has_finite_integral bound μ` instead
  (i.e. not requiring that `bound` is measurable), but in all applications proving integrability
  is easier. -/
theorem tendsto_set_to_fun_of_dominated_convergence (hT : dominated_fin_meas_additive μ T C)
  {fs : ℕ → α → E} {f : α → E} (bound : α → ℝ) (fs_measurable : ∀ n, ae_measurable (fs n) μ)
  (bound_integrable : integrable bound μ) (h_bound : ∀ n, ∀ᵐ a ∂μ, ∥fs n a∥ ≤ bound a)
  (h_lim : ∀ᵐ a ∂μ, tendsto (λ n, fs n a) at_top (𝓝 (f a))) :
  tendsto (λ n, set_to_fun hT (fs n)) at_top (𝓝 $ set_to_fun hT f) :=
begin
  /- `f` is a.e.-measurable, since it is the a.e.-pointwise limit of a.e.-measurable functions. -/
  have f_measurable : ae_measurable f μ := ae_measurable_of_tendsto_metric_ae fs_measurable h_lim,
  /- all functions we consider are integrable -/
  have fs_int : ∀ n, integrable (fs n) μ :=
    λ n, bound_integrable.mono' (fs_measurable n) (h_bound _),
  have f_int : integrable f μ :=
  ⟨f_measurable, has_finite_integral_of_dominated_convergence
    bound_integrable.has_finite_integral h_bound h_lim⟩,
  /- it suffices to prove the result for the corresponding L1 functions -/
  suffices : tendsto (λ n, L1.set_to_L1 hT ((fs_int n).to_L1 (fs n))) at_top
    (𝓝 (L1.set_to_L1 hT (f_int.to_L1 f))),
  { convert this,
    { ext1 n, exact set_to_fun_eq hT (fs_int n), },
    { exact set_to_fun_eq hT f_int, }, },
  /- the convergence of set_to_L1 follows from the convergence of the L1 functions -/
  refine L1.tendsto_set_to_L1 hT _ _ _,
  /- up to some rewriting, what we need to prove is `h_lim` -/
  rw tendsto_iff_norm_tendsto_zero,
  have lintegral_norm_tendsto_zero :
    tendsto (λn, ennreal.to_real $ ∫⁻ a, (ennreal.of_real ∥fs n a - f a∥) ∂μ) at_top (𝓝 0) :=
  (tendsto_to_real zero_ne_top).comp
    (tendsto_lintegral_norm_of_dominated_convergence
      fs_measurable bound_integrable.has_finite_integral h_bound h_lim),
  convert lintegral_norm_tendsto_zero,
  ext1 n,
  rw L1.norm_def,
  congr' 1,
  refine lintegral_congr_ae _,
  rw ← integrable.to_L1_sub,
  refine ((fs_int n).sub f_int).coe_fn_to_L1.mono (λ x hx, _),
  dsimp only,
  rw [hx, of_real_norm_eq_coe_nnnorm, pi.sub_apply],
end

/-- Lebesgue dominated convergence theorem for filters with a countable basis -/
lemma tendsto_set_to_fun_filter_of_dominated_convergence (hT : dominated_fin_meas_additive μ T C)
  {ι} {l : _root_.filter ι} [l.is_countably_generated]
  {fs : ι → α → E} {f : α → E} (bound : α → ℝ)
  (hfs_meas : ∀ᶠ n in l, ae_measurable (fs n) μ)
  (h_bound : ∀ᶠ n in l, ∀ᵐ a ∂μ, ∥fs n a∥ ≤ bound a)
  (bound_integrable : integrable bound μ)
  (h_lim : ∀ᵐ a ∂μ, tendsto (λ n, fs n a) l (𝓝 (f a))) :
  tendsto (λ n, set_to_fun hT (fs n)) l (𝓝 $ set_to_fun hT f) :=
begin
  rw tendsto_iff_seq_tendsto,
  intros x xl,
  have hxl : ∀ s ∈ l, ∃ a, ∀ b ≥ a, x b ∈ s, by { rwa tendsto_at_top' at xl, },
  have h : {x : ι | (λ n, ae_measurable (fs n) μ) x}
      ∩ {x : ι | (λ n, ∀ᵐ a ∂μ, ∥fs n a∥ ≤ bound a) x} ∈ l,
    from inter_mem hfs_meas h_bound,
  obtain ⟨k, h⟩ := hxl _ h,
  rw ← tendsto_add_at_top_iff_nat k,
  refine tendsto_set_to_fun_of_dominated_convergence hT bound _ bound_integrable _ _,
  { exact λ n, (h _ (self_le_add_left _ _)).1, },
  { exact λ n, (h _ (self_le_add_left _ _)).2, },
  { filter_upwards [h_lim],
    refine λ a h_lin, @tendsto.comp _ _ _ (λ n, x (n + k)) (λ n, fs n a) _ _ _ h_lin _,
    rw tendsto_add_at_top_iff_nat,
    assumption }
end

variables {X : Type*} [topological_space X] [first_countable_topology X]

lemma continuous_at_set_to_fun_of_dominated (hT : dominated_fin_meas_additive μ T C)
  {fs : X → α → E} {x₀ : X} {bound : α → ℝ} (hfs_meas : ∀ᶠ x in 𝓝 x₀, ae_measurable (fs x) μ)
  (h_bound : ∀ᶠ x in 𝓝 x₀, ∀ᵐ a ∂μ, ∥fs x a∥ ≤ bound a)
  (bound_integrable : integrable bound μ) (h_cont : ∀ᵐ a ∂μ, continuous_at (λ x, fs x a) x₀) :
  continuous_at (λ x, set_to_fun hT (fs x)) x₀ :=
tendsto_set_to_fun_filter_of_dominated_convergence hT bound ‹_› ‹_› ‹_› ‹_›

lemma continuous_set_to_fun_of_dominated (hT : dominated_fin_meas_additive μ T C)
  {fs : X → α → E} {bound : α → ℝ}
  (hfs_meas : ∀ x, ae_measurable (fs x) μ) (h_bound : ∀ x, ∀ᵐ a ∂μ, ∥fs x a∥ ≤ bound a)
  (bound_integrable : integrable bound μ) (h_cont : ∀ᵐ a ∂μ, continuous (λ x, fs x a)) :
  continuous (λ x, set_to_fun hT (fs x)) :=
continuous_iff_continuous_at.mpr (λ x₀, continuous_at_set_to_fun_of_dominated hT
  (eventually_of_forall hfs_meas) (eventually_of_forall h_bound) ‹_› $ h_cont.mono $
    λ _, continuous.continuous_at)

end function

end measure_theory


/-
Copyright (c) 2020 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Simon Hudon, Patrick Massot, Yury Kudryashov
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebra.group.hom
import Mathlib.data.equiv.mul_add
import Mathlib.data.prod
import Mathlib.PostPort

universes u_5 u_6 u_3 u_4 u_1 u_2 u_7 u_8 u_9

namespace Mathlib

/-!
# Monoid, group etc structures on `M × N`

In this file we define one-binop (`monoid`, `group` etc) structures on `M × N`. We also prove
trivial `simp` lemmas, and define the following operations on `monoid_hom`s:

* `fst M N : M × N →* M`, `snd M N : M × N →* N`: projections `prod.fst` and `prod.snd`
  as `monoid_hom`s;
* `inl M N : M →* M × N`, `inr M N : N →* M × N`: inclusions of first/second monoid
  into the product;
* `f.prod g : `M →* N × P`: sends `x` to `(f x, g x)`;
* `f.coprod g : M × N →* P`: sends `(x, y)` to `f x * g y`;
* `f.prod_map g : M × N → M' × N'`: `prod.map f g` as a `monoid_hom`,
  sends `(x, y)` to `(f x, g y)`.
-/

namespace prod

protected instance has_add {M : Type u_5} {N : Type u_6} [Add M] [Add N] : Add (M × N) :=
  { add := fun (p q : M × N) => (fst p + fst q, snd p + snd q) }

@[simp] theorem fst_add {M : Type u_5} {N : Type u_6} [Add M] [Add N] (p : M × N) (q : M × N) : fst (p + q) = fst p + fst q :=
  rfl

@[simp] theorem snd_add {M : Type u_5} {N : Type u_6} [Add M] [Add N] (p : M × N) (q : M × N) : snd (p + q) = snd p + snd q :=
  rfl

@[simp] theorem mk_add_mk {M : Type u_5} {N : Type u_6} [Add M] [Add N] (a₁ : M) (a₂ : M) (b₁ : N) (b₂ : N) : (a₁, b₁) + (a₂, b₂) = (a₁ + a₂, b₁ + b₂) :=
  rfl

protected instance has_one {M : Type u_5} {N : Type u_6} [HasOne M] [HasOne N] : HasOne (M × N) :=
  { one := (1, 1) }

@[simp] theorem fst_zero {M : Type u_5} {N : Type u_6} [HasZero M] [HasZero N] : fst 0 = 0 :=
  rfl

@[simp] theorem snd_one {M : Type u_5} {N : Type u_6} [HasOne M] [HasOne N] : snd 1 = 1 :=
  rfl

theorem zero_eq_mk {M : Type u_5} {N : Type u_6} [HasZero M] [HasZero N] : 0 = (0, 0) :=
  rfl

@[simp] theorem mk_eq_zero {M : Type u_5} {N : Type u_6} [HasZero M] [HasZero N] {x : M} {y : N} : (x, y) = 0 ↔ x = 0 ∧ y = 0 :=
  mk.inj_iff

theorem fst_mul_snd {M : Type u_5} {N : Type u_6} [monoid M] [monoid N] (p : M × N) : (fst p, 1) * (1, snd p) = p :=
  ext (mul_one (fst p)) (one_mul (snd p))

protected instance has_inv {M : Type u_5} {N : Type u_6} [has_inv M] [has_inv N] : has_inv (M × N) :=
  has_inv.mk fun (p : M × N) => (fst p⁻¹, snd p⁻¹)

@[simp] theorem fst_inv {G : Type u_3} {H : Type u_4} [has_inv G] [has_inv H] (p : G × H) : fst (p⁻¹) = (fst p⁻¹) :=
  rfl

@[simp] theorem snd_neg {G : Type u_3} {H : Type u_4} [Neg G] [Neg H] (p : G × H) : snd (-p) = -snd p :=
  rfl

@[simp] theorem inv_mk {G : Type u_3} {H : Type u_4} [has_inv G] [has_inv H] (a : G) (b : H) : (a, b)⁻¹ = (a⁻¹, b⁻¹) :=
  rfl

protected instance has_sub {M : Type u_5} {N : Type u_6} [Sub M] [Sub N] : Sub (M × N) :=
  { sub := fun (p q : M × N) => (fst p - fst q, snd p - snd q) }

@[simp] theorem fst_sub {A : Type u_1} {B : Type u_2} [add_group A] [add_group B] (a : A × B) (b : A × B) : fst (a - b) = fst a - fst b :=
  rfl

@[simp] theorem snd_sub {A : Type u_1} {B : Type u_2} [add_group A] [add_group B] (a : A × B) (b : A × B) : snd (a - b) = snd a - snd b :=
  rfl

@[simp] theorem mk_sub_mk {A : Type u_1} {B : Type u_2} [add_group A] [add_group B] (x₁ : A) (x₂ : A) (y₁ : B) (y₂ : B) : (x₁, y₁) - (x₂, y₂) = (x₁ - x₂, y₁ - y₂) :=
  rfl

protected instance semigroup {M : Type u_5} {N : Type u_6} [semigroup M] [semigroup N] : semigroup (M × N) :=
  semigroup.mk Mul.mul sorry

protected instance add_monoid {M : Type u_5} {N : Type u_6} [add_monoid M] [add_monoid N] : add_monoid (M × N) :=
  add_monoid.mk add_semigroup.add sorry 0 sorry sorry

protected instance group {G : Type u_3} {H : Type u_4} [group G] [group H] : group (G × H) :=
  group.mk monoid.mul sorry monoid.one sorry sorry has_inv.inv Div.div sorry

protected instance add_comm_semigroup {G : Type u_3} {H : Type u_4} [add_comm_semigroup G] [add_comm_semigroup H] : add_comm_semigroup (G × H) :=
  add_comm_semigroup.mk add_semigroup.add sorry sorry

protected instance left_cancel_semigroup {G : Type u_3} {H : Type u_4} [left_cancel_semigroup G] [left_cancel_semigroup H] : left_cancel_semigroup (G × H) :=
  left_cancel_semigroup.mk semigroup.mul sorry sorry

protected instance right_cancel_semigroup {G : Type u_3} {H : Type u_4} [right_cancel_semigroup G] [right_cancel_semigroup H] : right_cancel_semigroup (G × H) :=
  right_cancel_semigroup.mk semigroup.mul sorry sorry

protected instance add_comm_monoid {M : Type u_5} {N : Type u_6} [add_comm_monoid M] [add_comm_monoid N] : add_comm_monoid (M × N) :=
  add_comm_monoid.mk add_comm_semigroup.add sorry add_monoid.zero sorry sorry sorry

protected instance comm_group {G : Type u_3} {H : Type u_4} [comm_group G] [comm_group H] : comm_group (G × H) :=
  comm_group.mk comm_semigroup.mul sorry group.one sorry sorry group.inv group.div sorry sorry

end prod

namespace monoid_hom

/-- Given monoids `M`, `N`, the natural projection homomorphism from `M × N` to `M`.-/
def fst (M : Type u_5) (N : Type u_6) [monoid M] [monoid N] : M × N →* M :=
  mk prod.fst sorry sorry

/-- Given monoids `M`, `N`, the natural projection homomorphism from `M × N` to `N`.-/
def Mathlib.add_monoid_hom.snd (M : Type u_5) (N : Type u_6) [add_monoid M] [add_monoid N] : M × N →+ N :=
  add_monoid_hom.mk prod.snd sorry sorry

/-- Given monoids `M`, `N`, the natural inclusion homomorphism from `M` to `M × N`. -/
def Mathlib.add_monoid_hom.inl (M : Type u_5) (N : Type u_6) [add_monoid M] [add_monoid N] : M →+ M × N :=
  add_monoid_hom.mk (fun (x : M) => (x, 0)) sorry sorry

/-- Given monoids `M`, `N`, the natural inclusion homomorphism from `N` to `M × N`. -/
def Mathlib.add_monoid_hom.inr (M : Type u_5) (N : Type u_6) [add_monoid M] [add_monoid N] : N →+ M × N :=
  add_monoid_hom.mk (fun (y : N) => (0, y)) sorry sorry

@[simp] theorem Mathlib.add_monoid_hom.coe_fst {M : Type u_5} {N : Type u_6} [add_monoid M] [add_monoid N] : ⇑(add_monoid_hom.fst M N) = prod.fst :=
  rfl

@[simp] theorem Mathlib.add_monoid_hom.coe_snd {M : Type u_5} {N : Type u_6} [add_monoid M] [add_monoid N] : ⇑(add_monoid_hom.snd M N) = prod.snd :=
  rfl

@[simp] theorem inl_apply {M : Type u_5} {N : Type u_6} [monoid M] [monoid N] (x : M) : coe_fn (inl M N) x = (x, 1) :=
  rfl

@[simp] theorem inr_apply {M : Type u_5} {N : Type u_6} [monoid M] [monoid N] (y : N) : coe_fn (inr M N) y = (1, y) :=
  rfl

@[simp] theorem Mathlib.add_monoid_hom.fst_comp_inl {M : Type u_5} {N : Type u_6} [add_monoid M] [add_monoid N] : add_monoid_hom.comp (add_monoid_hom.fst M N) (add_monoid_hom.inl M N) = add_monoid_hom.id M :=
  rfl

@[simp] theorem Mathlib.add_monoid_hom.snd_comp_inl {M : Type u_5} {N : Type u_6} [add_monoid M] [add_monoid N] : add_monoid_hom.comp (add_monoid_hom.snd M N) (add_monoid_hom.inl M N) = 0 :=
  rfl

@[simp] theorem Mathlib.add_monoid_hom.fst_comp_inr {M : Type u_5} {N : Type u_6} [add_monoid M] [add_monoid N] : add_monoid_hom.comp (add_monoid_hom.fst M N) (add_monoid_hom.inr M N) = 0 :=
  rfl

@[simp] theorem Mathlib.add_monoid_hom.snd_comp_inr {M : Type u_5} {N : Type u_6} [add_monoid M] [add_monoid N] : add_monoid_hom.comp (add_monoid_hom.snd M N) (add_monoid_hom.inr M N) = add_monoid_hom.id N :=
  rfl

/-- Combine two `monoid_hom`s `f : M →* N`, `g : M →* P` into `f.prod g : M →* N × P`
given by `(f.prod g) x = (f x, g x)` -/
protected def prod {M : Type u_5} {N : Type u_6} {P : Type u_7} [monoid M] [monoid N] [monoid P] (f : M →* N) (g : M →* P) : M →* N × P :=
  mk (fun (x : M) => (coe_fn f x, coe_fn g x)) sorry sorry

@[simp] theorem prod_apply {M : Type u_5} {N : Type u_6} {P : Type u_7} [monoid M] [monoid N] [monoid P] (f : M →* N) (g : M →* P) (x : M) : coe_fn (monoid_hom.prod f g) x = (coe_fn f x, coe_fn g x) :=
  rfl

@[simp] theorem fst_comp_prod {M : Type u_5} {N : Type u_6} {P : Type u_7} [monoid M] [monoid N] [monoid P] (f : M →* N) (g : M →* P) : comp (fst N P) (monoid_hom.prod f g) = f :=
  ext fun (x : M) => rfl

@[simp] theorem snd_comp_prod {M : Type u_5} {N : Type u_6} {P : Type u_7} [monoid M] [monoid N] [monoid P] (f : M →* N) (g : M →* P) : comp (snd N P) (monoid_hom.prod f g) = g :=
  ext fun (x : M) => rfl

@[simp] theorem Mathlib.add_monoid_hom.prod_unique {M : Type u_5} {N : Type u_6} {P : Type u_7} [add_monoid M] [add_monoid N] [add_monoid P] (f : M →+ N × P) : add_monoid_hom.prod (add_monoid_hom.comp (add_monoid_hom.fst N P) f) (add_monoid_hom.comp (add_monoid_hom.snd N P) f) =
  f := sorry

/-- `prod.map` as a `monoid_hom`. -/
def Mathlib.add_monoid_hom.prod_map {M : Type u_5} {N : Type u_6} [add_monoid M] [add_monoid N] {M' : Type u_8} {N' : Type u_9} [add_monoid M'] [add_monoid N'] (f : M →+ M') (g : N →+ N') : M × N →+ M' × N' :=
  add_monoid_hom.prod (add_monoid_hom.comp f (add_monoid_hom.fst M N)) (add_monoid_hom.comp g (add_monoid_hom.snd M N))

theorem Mathlib.add_monoid_hom.prod_map_def {M : Type u_5} {N : Type u_6} [add_monoid M] [add_monoid N] {M' : Type u_8} {N' : Type u_9} [add_monoid M'] [add_monoid N'] (f : M →+ M') (g : N →+ N') : add_monoid_hom.prod_map f g =
  add_monoid_hom.prod (add_monoid_hom.comp f (add_monoid_hom.fst M N)) (add_monoid_hom.comp g (add_monoid_hom.snd M N)) :=
  rfl

@[simp] theorem coe_prod_map {M : Type u_5} {N : Type u_6} [monoid M] [monoid N] {M' : Type u_8} {N' : Type u_9} [monoid M'] [monoid N'] (f : M →* M') (g : N →* N') : ⇑(prod_map f g) = prod.map ⇑f ⇑g :=
  rfl

theorem prod_comp_prod_map {M : Type u_5} {N : Type u_6} {P : Type u_7} [monoid M] [monoid N] {M' : Type u_8} {N' : Type u_9} [monoid M'] [monoid N'] [monoid P] (f : P →* M) (g : P →* N) (f' : M →* M') (g' : N →* N') : comp (prod_map f' g') (monoid_hom.prod f g) = monoid_hom.prod (comp f' f) (comp g' g) :=
  rfl

/-- Coproduct of two `monoid_hom`s with the same codomain:
`f.coprod g (p : M × N) = f p.1 * g p.2`. -/
def coprod {M : Type u_5} {N : Type u_6} {P : Type u_7} [monoid M] [monoid N] [comm_monoid P] (f : M →* P) (g : N →* P) : M × N →* P :=
  comp f (fst M N) * comp g (snd M N)

@[simp] theorem coprod_apply {M : Type u_5} {N : Type u_6} {P : Type u_7} [monoid M] [monoid N] [comm_monoid P] (f : M →* P) (g : N →* P) (p : M × N) : coe_fn (coprod f g) p = coe_fn f (prod.fst p) * coe_fn g (prod.snd p) :=
  rfl

@[simp] theorem Mathlib.add_monoid_hom.coprod_comp_inl {M : Type u_5} {N : Type u_6} {P : Type u_7} [add_monoid M] [add_monoid N] [add_comm_monoid P] (f : M →+ P) (g : N →+ P) : add_monoid_hom.comp (add_monoid_hom.coprod f g) (add_monoid_hom.inl M N) = f := sorry

@[simp] theorem Mathlib.add_monoid_hom.coprod_comp_inr {M : Type u_5} {N : Type u_6} {P : Type u_7} [add_monoid M] [add_monoid N] [add_comm_monoid P] (f : M →+ P) (g : N →+ P) : add_monoid_hom.comp (add_monoid_hom.coprod f g) (add_monoid_hom.inr M N) = g := sorry

@[simp] theorem Mathlib.add_monoid_hom.coprod_unique {M : Type u_5} {N : Type u_6} {P : Type u_7} [add_monoid M] [add_monoid N] [add_comm_monoid P] (f : M × N →+ P) : add_monoid_hom.coprod (add_monoid_hom.comp f (add_monoid_hom.inl M N))
    (add_monoid_hom.comp f (add_monoid_hom.inr M N)) =
  f := sorry

@[simp] theorem coprod_inl_inr {M : Type u_1} {N : Type u_2} [comm_monoid M] [comm_monoid N] : coprod (inl M N) (inr M N) = id (M × N) :=
  coprod_unique (id (M × N))

theorem comp_coprod {M : Type u_5} {N : Type u_6} {P : Type u_7} [monoid M] [monoid N] [comm_monoid P] {Q : Type u_1} [comm_monoid Q] (h : P →* Q) (f : M →* P) (g : N →* P) : comp h (coprod f g) = coprod (comp h f) (comp h g) := sorry

end monoid_hom

namespace mul_equiv

/-- The equivalence between `M × N` and `N × M` given by swapping the components is multiplicative. -/
def prod_comm {M : Type u_5} {N : Type u_6} [monoid M] [monoid N] : M × N ≃* N × M :=
  mk (equiv.to_fun (equiv.prod_comm M N)) (equiv.inv_fun (equiv.prod_comm M N)) sorry sorry sorry

@[simp] theorem coe_prod_comm {M : Type u_5} {N : Type u_6} [monoid M] [monoid N] : ⇑prod_comm = prod.swap :=
  rfl

@[simp] theorem coe_prod_comm_symm {M : Type u_5} {N : Type u_6} [monoid M] [monoid N] : ⇑(symm prod_comm) = prod.swap :=
  rfl

/-- The monoid equivalence between units of a product of two monoids, and the product of the
    units of each monoid. -/
def prod_units {M : Type u_5} {N : Type u_6} [monoid M] [monoid N] : units (M × N) ≃* units M × units N :=
  mk (⇑(monoid_hom.prod (units.map (monoid_hom.fst M N)) (units.map (monoid_hom.snd M N))))
    (fun (u : units M × units N) =>
      units.mk (↑(prod.fst u), ↑(prod.snd u)) (↑(prod.fst u⁻¹), ↑(prod.snd u⁻¹)) sorry sorry)
    sorry sorry sorry


/-
Copyright (c) 2021 Kalle Kytölä. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kalle Kytölä
-/
import topology.algebra.module.weak_dual
import analysis.normed_space.dual
import analysis.normed_space.operator_norm

/-!
# Weak dual of normed space

Let `E` be a normed space over a field `𝕜`. This file is concerned with properties of the weak-*
topology on the dual of `E`. By the dual, we mean either of the type synonyms
`normed_space.dual 𝕜 E` or `weak_dual 𝕜 E`, depending on whether it is viewed as equipped with its
usual operator norm topology or the weak-* topology.

It is shown that the canonical mapping `normed_space.dual 𝕜 E → weak_dual 𝕜 E` is continuous, and
as a consequence the weak-* topology is coarser than the topology obtained from the operator norm
(dual norm).

The file is a stub, some TODOs below.

## Main definitions

The main definitions concern the canonical mapping `dual 𝕜 E → weak_dual 𝕜 E`.

* `normed_space.dual.to_weak_dual` and `weak_dual.to_normed_dual`: Linear equivalences from
  `dual 𝕜 E` to `weak_dual 𝕜 E` and in the converse direction.
* `normed_space.dual.continuous_linear_map_to_weak_dual`: A continuous linear mapping from
  `dual 𝕜 E` to `weak_dual 𝕜 E` (same as `normed_space.dual.to_weak_dual` but different bundled
  data).

## Main results

The first main result concerns the comparison of the operator norm topology on `dual 𝕜 E` and the
weak-* topology on (its type synonym) `weak_dual 𝕜 E`:
* `dual_norm_topology_le_weak_dual_topology`: The weak-* topology on the dual of a normed space is
  coarser (not necessarily strictly) than the operator norm topology.

TODOs:
* Add that in finite dimensions, the weak-* topology and the dual norm topology coincide.
* Add that in infinite dimensions, the weak-* topology is strictly coarser than the dual norm
  topology.
* Add Banach-Alaoglu theorem (general version maybe in `topology.algebra.module.weak_dual`).
* Add metrizability of the dual unit ball (more generally bounded subsets) of `weak_dual 𝕜 E`
  under the assumption of separability of `E`. Sequential Banach-Alaoglu theorem would then follow
  from the general one.

## Notations

No new notation is introduced.

## Implementation notes

Weak-* topology is defined generally in the file `topology.algebra.module.weak_dual`.

When `E` is a normed space, the duals `dual 𝕜 E` and `weak_dual 𝕜 E` are type synonyms with
different topology instances.

## References

* https://en.wikipedia.org/wiki/Weak_topology#Weak-*_topology

## Tags

weak-star, weak dual

-/

noncomputable theory
open filter
open_locale topological_space

section weak_star_topology_for_duals_of_normed_spaces
/-!
### Weak star topology on duals of normed spaces
In this section, we prove properties about the weak-* topology on duals of normed spaces.
We prove in particular that the canonical mapping `dual 𝕜 E → weak_dual 𝕜 E` is continuous,
i.e., that the weak-* topology is coarser (not necessarily strictly) than the topology given
by the dual-norm (i.e. the operator-norm).
-/

open normed_space

variables {𝕜 : Type*} [nondiscrete_normed_field 𝕜]
variables {E : Type*} [normed_group E] [normed_space 𝕜 E]

/-- For normed spaces `E`, there is a canonical map `dual 𝕜 E → weak_dual 𝕜 E` (the "identity"
mapping). It is a linear equivalence. -/
def normed_space.dual.to_weak_dual : dual 𝕜 E ≃ₗ[𝕜] weak_dual 𝕜 E :=
linear_equiv.refl 𝕜 (E →L[𝕜] 𝕜)

/-- For normed spaces `E`, there is a canonical map `weak_dual 𝕜 E → dual 𝕜 E` (the "identity"
mapping). It is a linear equivalence. Here it is implemented as the inverse of the linear
equivalence `normed_space.dual.to_weak_dual` in the other direction. -/
def weak_dual.to_normed_dual : weak_dual 𝕜 E ≃ₗ[𝕜] dual 𝕜 E :=
normed_space.dual.to_weak_dual.symm

@[simp] lemma weak_dual.coe_to_fun_eq_normed_coe_to_fun (x' : dual 𝕜 E) :
  (x'.to_weak_dual : E → 𝕜) = x' := rfl

namespace normed_space.dual

@[simp] lemma to_weak_dual_eq_iff (x' y' : dual 𝕜 E) :
  x'.to_weak_dual = y'.to_weak_dual ↔ x' = y' :=
to_weak_dual.injective.eq_iff

@[simp] lemma _root_.weak_dual.to_normed_dual_eq_iff (x' y' : weak_dual 𝕜 E) :
  x'.to_normed_dual = y'.to_normed_dual ↔ x' = y' :=
weak_dual.to_normed_dual.injective.eq_iff

theorem to_weak_dual_continuous :
  continuous (λ (x' : dual 𝕜 E), x'.to_weak_dual) :=
begin
  apply weak_dual.continuous_of_continuous_eval,
  intros z,
  exact (inclusion_in_double_dual 𝕜 E z).continuous,
end

/-- For a normed space `E`, according to `to_weak_dual_continuous` the "identity mapping"
`dual 𝕜 E → weak_dual 𝕜 E` is continuous. This definition implements it as a continuous linear
map. -/
def continuous_linear_map_to_weak_dual : dual 𝕜 E →L[𝕜] weak_dual 𝕜 E :=
{ cont := to_weak_dual_continuous, .. to_weak_dual, }

/-- The weak-star topology is coarser than the dual-norm topology. -/
theorem dual_norm_topology_le_weak_dual_topology :
  (by apply_instance : topological_space (dual 𝕜 E)) ≤
    (by apply_instance : topological_space (weak_dual 𝕜 E)) :=
begin
  refine continuous.le_induced _,
  apply continuous_pi_iff.mpr,
  intros z,
  exact (inclusion_in_double_dual 𝕜 E z).continuous,
end

end normed_space.dual

namespace weak_dual

lemma to_normed_dual.preimage_closed_unit_ball :
  (to_normed_dual ⁻¹' metric.closed_ball (0 : dual 𝕜 E) 1) =
    {x' : weak_dual 𝕜 E | ∥ x'.to_normed_dual ∥ ≤ 1} :=
begin
  have eq : metric.closed_ball (0 : dual 𝕜 E) 1 = {x' : dual 𝕜 E | ∥ x' ∥ ≤ 1},
  { ext x', simp only [dist_zero_right, metric.mem_closed_ball, set.mem_set_of_eq], },
  rw eq,
  exact set.preimage_set_of_eq,
end

variables (𝕜)

/-- The polar set `polar 𝕜 s` of `s : set E` seen as a subset of the dual of `E` with the
weak-star topology is `weak_dual.polar 𝕜 s`. -/
def polar (s : set E) : set (weak_dual 𝕜 E) := to_normed_dual ⁻¹' (polar 𝕜 s)

end weak_dual

end weak_star_topology_for_duals_of_normed_spaces

section polar_sets_in_weak_dual

open metric set normed_space

variables {𝕜 : Type*} [nondiscrete_normed_field 𝕜]
variables {E : Type*} [normed_group E] [normed_space 𝕜 E]

/-- The polar `polar 𝕜 s` of a set `s : E` is a closed subset when the weak star topology
is used, i.e., when `polar 𝕜 s` is interpreted as a subset of `weak_dual 𝕜 E`. -/
lemma weak_dual.is_closed_polar (s : set E) : is_closed (weak_dual.polar 𝕜 s) :=
begin
  rw [weak_dual.polar, polar_eq_Inter, preimage_Inter₂],
  apply is_closed_bInter,
  intros z hz,
  rw set.preimage_set_of_eq,
  have eq : {x' : weak_dual 𝕜 E | ∥weak_dual.to_normed_dual x' z∥ ≤ 1}
    = (λ (x' : weak_dual 𝕜 E), ∥x' z∥)⁻¹' (Iic 1) := by refl,
  rw eq,
  refine is_closed.preimage _ (is_closed_Iic),
  apply continuous.comp continuous_norm (weak_dual.eval_continuous _ _ z),
end

end polar_sets_in_weak_dual


/-
Copyright (c) 2021 Frédéric Dupuis. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Frédéric Dupuis, Heather Macbeth
-/

import algebra.ring.basic
import data.equiv.ring

/-!
# Propositional typeclasses on several ring homs

This file contains three typeclasses used in the definition of (semi)linear maps:
* `ring_hom_comp_triple σ₁₂ σ₂₃ σ₁₃`, which expresses the fact that `σ₂₃.comp σ₁₂ = σ₁₃`
* `ring_hom_inv_pair σ₁₂ σ₂₁`, which states that `σ₁₂` and `σ₂₁` are inverses of each other
* `ring_hom_surjective σ`, which states that `σ` is surjective
These typeclasses ensure that objects such as `σ₂₃.comp σ₁₂` never end up in the type of a
semilinear map; instead, the typeclass system directly finds the appropriate `ring_hom` to use.
A typical use-case is conjugate-linear maps, i.e. when `σ = complex.conj`; this system ensures that
composing two conjugate-linear maps is a linear map, and not a `conj.comp conj`-linear map.

Instances of these typeclasses mostly involving `ring_hom.id` are also provided:
* `ring_hom_inv_pair (ring_hom.id R) (ring_hom.id R)`
* `[ring_hom_inv_pair σ₁₂ σ₂₁] : ring_hom_comp_triple σ₁₂ σ₂₁ (ring_hom.id R₁)`
* `ring_hom_comp_triple (ring_hom.id R₁) σ₁₂ σ₁₂`
* `ring_hom_comp_triple σ₁₂ (ring_hom.id R₂) σ₁₂`
* `ring_hom_surjective (ring_hom.id R)`
* `[ring_hom_inv_pair σ₁ σ₂] : ring_hom_surjective σ₁`

## Implementation notes

* For the typeclass `ring_hom_inv_pair σ₁₂ σ₂₁`, `σ₂₁` is marked as an `out_param`,
  as it must typically be found via the typeclass inference system.

* Likewise, for `ring_hom_comp_triple σ₁₂ σ₂₃ σ₁₃`, `σ₁₃` is marked as an `out_param`,
  for the same reason.

## Tags

`ring_hom_comp_triple`, `ring_hom_inv_pair`, `ring_hom_surjective`
-/

variables {R₁ : Type*} {R₂ : Type*} {R₃ : Type*}
variables [semiring R₁] [semiring R₂] [semiring R₃]

/-- Class that expresses the fact that three ring homomorphisms form a composition triple. This is
used to handle composition of semilinear maps. -/
class ring_hom_comp_triple (σ₁₂ : R₁ →+* R₂) (σ₂₃ : R₂ →+* R₃)
  (σ₁₃ : out_param (R₁ →+* R₃)) : Prop :=
(comp_eq : σ₂₃.comp σ₁₂ = σ₁₃)

attribute [simp] ring_hom_comp_triple.comp_eq

variables {σ₁₂ : R₁ →+* R₂} {σ₂₃ : R₂ →+* R₃} {σ₁₃ : R₁ →+* R₃}

namespace ring_hom_comp_triple

@[simp] lemma comp_apply [ring_hom_comp_triple σ₁₂ σ₂₃ σ₁₃] {x : R₁} :
  σ₂₃ (σ₁₂ x) = σ₁₃ x :=
ring_hom.congr_fun comp_eq x

end ring_hom_comp_triple

/-- Class that expresses the fact that two ring homomorphisms are inverses of each other. This is
used to handle `symm` for semilinear equivalences. -/
class ring_hom_inv_pair (σ : R₁ →+* R₂) (σ' : out_param (R₂ →+* R₁)) : Prop :=
(comp_eq : σ'.comp σ = ring_hom.id R₁)
(comp_eq₂ : σ.comp σ' = ring_hom.id R₂)

attribute [simp] ring_hom_inv_pair.comp_eq
attribute [simp] ring_hom_inv_pair.comp_eq₂

variables {σ : R₁ →+* R₂} {σ' : R₂ →+* R₁}

namespace ring_hom_inv_pair

variables [ring_hom_inv_pair σ σ']

@[simp] lemma comp_apply_eq {x : R₁} : σ' (σ x) = x :=
by { rw [← ring_hom.comp_apply, comp_eq], simp }

@[simp] lemma comp_apply_eq₂ {x : R₂} : σ (σ' x) = x :=
by { rw [← ring_hom.comp_apply, comp_eq₂], simp }

instance ids : ring_hom_inv_pair (ring_hom.id R₁) (ring_hom.id R₁) := ⟨rfl, rfl⟩
instance triples {σ₂₁ : R₂ →+* R₁} [ring_hom_inv_pair σ₁₂ σ₂₁] :
  ring_hom_comp_triple σ₁₂ σ₂₁ (ring_hom.id R₁) :=
⟨by simp only [comp_eq]⟩

instance triples₂ {σ₂₁ : R₂ →+* R₁} [ring_hom_inv_pair σ₁₂ σ₂₁] :
  ring_hom_comp_triple σ₂₁ σ₁₂ (ring_hom.id R₂) :=
⟨by simp only [comp_eq₂]⟩

/--
Construct a `ring_hom_inv_pair` from both directions of a ring equiv.

This is not an instance, as for equivalences that are involutions, a better instance
would be `ring_hom_inv_pair e e`. Indeed, this declaration is not currently used in mathlib.

See note [reducible non-instances].
-/
@[reducible]
lemma of_ring_equiv (e : R₁ ≃+* R₂) :
  ring_hom_inv_pair (↑e : R₁ →+* R₂) ↑e.symm :=
⟨e.symm_to_ring_hom_comp_to_ring_hom, e.symm.symm_to_ring_hom_comp_to_ring_hom⟩

/--
Swap the direction of a `ring_hom_inv_pair`. This is not an instance as it would loop, and better
instances are often available and may often be preferrable to using this one. Indeed, this
declaration is not currently used in mathlib.

See note [reducible non-instances].
-/
@[reducible]
lemma symm (σ₁₂ : R₁ →+* R₂) (σ₂₁ : R₂ →+* R₁) [ring_hom_inv_pair σ₁₂ σ₂₁] :
  ring_hom_inv_pair σ₂₁ σ₁₂ :=
⟨ring_hom_inv_pair.comp_eq₂, ring_hom_inv_pair.comp_eq⟩

end ring_hom_inv_pair

namespace ring_hom_comp_triple

instance ids : ring_hom_comp_triple (ring_hom.id R₁) σ₁₂ σ₁₂ := ⟨by { ext, simp }⟩
instance right_ids : ring_hom_comp_triple σ₁₂ (ring_hom.id R₂) σ₁₂ := ⟨by { ext, simp }⟩

end ring_hom_comp_triple

/-- Class expressing the fact that a `ring_hom` is surjective. This is needed in the context
of semilinear maps, where some lemmas require this. -/
class ring_hom_surjective (σ : R₁ →+* R₂) : Prop :=
(is_surjective : function.surjective σ)

lemma ring_hom.is_surjective (σ : R₁ →+* R₂) [t : ring_hom_surjective σ] : function.surjective σ :=
t.is_surjective

namespace ring_hom_surjective

-- The linter gives a false positive, since `σ₂` is an out_param
@[priority 100, nolint dangerous_instance] instance inv_pair {σ₁ : R₁ →+* R₂} {σ₂ : R₂ →+* R₁}
  [ring_hom_inv_pair σ₁ σ₂] : ring_hom_surjective σ₁ :=
⟨λ x, ⟨σ₂ x, ring_hom_inv_pair.comp_apply_eq₂⟩⟩

instance ids : ring_hom_surjective (ring_hom.id R₁) := ⟨is_surjective⟩

/-- This cannot be an instance as there is no way to infer `σ₁₂` and `σ₂₃`. -/
lemma comp [ring_hom_comp_triple σ₁₂ σ₂₃ σ₁₃] [ring_hom_surjective σ₁₂] [ring_hom_surjective σ₂₃] :
  ring_hom_surjective σ₁₃ :=
{ is_surjective := begin
    have := σ₂₃.is_surjective.comp σ₁₂.is_surjective,
    rwa [← ring_hom.coe_comp, ring_hom_comp_triple.comp_eq] at this,
  end }

end ring_hom_surjective


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

/-
DNF transformation.
-/

import tactic.omega.clause
import tactic.omega.int.form

namespace omega
namespace int

open_locale omega.int

/-- push_neg p returns the result of normalizing ¬ p by
    pushing the outermost negation all the way down,
    until it reaches either a negation or an atom -/
@[simp] def push_neg : preform → preform
| (p ∨* q) := (push_neg p) ∧* (push_neg q)
| (p ∧* q) := (push_neg p) ∨* (push_neg q)
| (¬*p)    := p
| p        := ¬* p

lemma push_neg_equiv :
  ∀ {p : preform}, preform.equiv (push_neg p) (¬* p) :=
begin
  preform.induce `[intros v; try {refl}],
  { simp only [not_not, push_neg, preform.holds] },
  { simp only [preform.holds, push_neg, not_or_distrib, ihp v, ihq v] },
  { simp only [preform.holds, push_neg, not_and_distrib, ihp v, ihq v] }
end

/-- NNF transformation -/
def nnf : preform → preform
| (¬* p)   := push_neg (nnf p)
| (p ∨* q) := (nnf p) ∨* (nnf q)
| (p ∧* q) := (nnf p) ∧* (nnf q)
| a        := a

def is_nnf : preform → Prop
| (t =* s) := true
| (t ≤* s) := true
| ¬*(t =* s) := true
| ¬*(t ≤* s) := true
| (p ∨* q) := is_nnf p ∧ is_nnf q
| (p ∧* q) := is_nnf p ∧ is_nnf q
| _ := false

lemma is_nnf_push_neg : ∀ p : preform, is_nnf p → is_nnf (push_neg p) :=
begin
  preform.induce `[intro h1; try {trivial}],
  { cases p; try {cases h1}; trivial },
  { cases h1, constructor; [{apply ihp}, {apply ihq}]; assumption },
  { cases h1, constructor; [{apply ihp}, {apply ihq}]; assumption }
end

/-- Argument is free of negations -/
def neg_free : preform → Prop
| (t =* s) := true
| (t ≤* s) := true
| (p ∨* q) := neg_free p ∧ neg_free q
| (p ∧* q) := neg_free p ∧ neg_free q
| _        := false

lemma is_nnf_nnf : ∀ p : preform, is_nnf (nnf p) :=
begin
  preform.induce `[try {trivial}],
  { apply is_nnf_push_neg _ ih },
  { constructor; assumption },
  { constructor; assumption }
end

lemma nnf_equiv : ∀ {p : preform}, preform.equiv (nnf p) p :=
begin
  preform.induce `[intros v; try {refl}; simp only [nnf]],
  { rw push_neg_equiv,
    apply not_iff_not_of_iff, apply ih },
  { apply pred_mono_2' (ihp v) (ihq v) },
  { apply pred_mono_2' (ihp v) (ihq v) }
end

/-- Eliminate all negations from preform -/
@[simp] def neg_elim : preform → preform
| (¬* (t =* s)) := (t.add_one ≤* s) ∨* (s.add_one ≤* t)
| (¬* (t ≤* s)) := s.add_one ≤* t
| (p ∨* q) := (neg_elim p) ∨* (neg_elim q)
| (p ∧* q) := (neg_elim p) ∧* (neg_elim q)
| p        := p

lemma neg_free_neg_elim : ∀ p : preform, is_nnf p → neg_free (neg_elim p) :=
begin
  preform.induce `[intro h1, try {simp only [neg_free, neg_elim]}, try {trivial}],
  { cases p; try {cases h1}; try {trivial}, constructor; trivial },
  { cases h1, constructor; [{apply ihp}, {apply ihq}]; assumption },
  { cases h1, constructor; [{apply ihp}, {apply ihq}]; assumption }
end

lemma le_and_le_iff_eq {α : Type} [partial_order α] {a b : α} :
  (a ≤ b ∧ b ≤ a) ↔ a = b :=
begin
  constructor; intro h1,
  { cases h1, apply le_antisymm; assumption },
  { constructor; apply le_of_eq; rw h1  }
end

lemma implies_neg_elim : ∀ {p : preform}, preform.implies p (neg_elim p) :=
begin
  preform.induce `[intros v h, try {apply h}],
  { cases p with t s t s; try {apply h},
    { simp only [le_and_le_iff_eq.symm,
        not_and_distrib, not_le,
        preterm.val, preform.holds] at h,
      simp only [int.add_one_le_iff, preterm.add_one,
        preterm.val, preform.holds, neg_elim],
      rw or_comm, assumption },
    { simp only [not_le, int.add_one_le_iff,
        preterm.add_one, not_le, preterm.val,
        preform.holds, neg_elim] at *,
      assumption} },
  { simp only [neg_elim], cases h; [{left, apply ihp},
    {right, apply ihq}]; assumption },
  { apply and.imp (ihp _) (ihq _) h }
end

@[simp] def dnf_core : preform → list clause
| (p ∨* q) := (dnf_core p) ++ (dnf_core q)
| (p ∧* q) :=
  (list.product (dnf_core p) (dnf_core q)).map
  (λ pq, clause.append pq.fst pq.snd)
| (t =* s) := [([term.sub (canonize s) (canonize t)],[])]
| (t ≤* s) := [([],[term.sub (canonize s) (canonize t)])]
| (¬* _)   := []

/-- DNF transformation -/
def dnf (p : preform) : list clause :=
dnf_core $ neg_elim $ nnf p

lemma exists_clause_holds {v : nat → int} :
  ∀ {p : preform}, neg_free p → p.holds v → ∃ c ∈ (dnf_core p), clause.holds v c :=
begin
  preform.induce `[intros h1 h2],
  { apply list.exists_mem_cons_of, constructor,
    { simp only [preterm.val, preform.holds] at h2,
      rw [list.forall_mem_singleton],
      simp only [h2, omega.int.val_canonize,
        omega.term.val_sub, sub_self] },
    { apply list.forall_mem_nil } },
  { apply list.exists_mem_cons_of, constructor,
    { apply list.forall_mem_nil },
    { simp only [preterm.val, preform.holds] at h2 ,
      rw [list.forall_mem_singleton],
      simp only [val_canonize,
        preterm.val, term.val_sub],
      rw [le_sub, sub_zero], assumption } },
    { cases h1 },
    { cases h2 with h2 h2;
      [ {cases (ihp h1.left h2) with c h3},
        {cases (ihq h1.right h2) with c h3}];
      cases h3 with h3 h4;
      refine ⟨c, list.mem_append.elim_right _, h4⟩;
      [left,right]; assumption },
    { rcases (ihp h1.left h2.left) with ⟨cp, hp1, hp2⟩,
      rcases (ihq h1.right h2.right) with ⟨cq, hq1, hq2⟩,
      refine ⟨clause.append cp cq, ⟨_, clause.holds_append hp2 hq2⟩⟩,
      simp only [dnf_core, list.mem_map],
      refine ⟨(cp,cq),⟨_,rfl⟩⟩,
      rw list.mem_product,
      constructor; assumption }
end

lemma clauses_sat_dnf_core {p : preform} :
  neg_free p → p.sat → clauses.sat (dnf_core p) :=
begin
  intros h1 h2, cases h2 with v h2,
  rcases (exists_clause_holds h1 h2) with ⟨c,h3,h4⟩,
  refine ⟨c,h3,v,h4⟩
end

lemma unsat_of_clauses_unsat {p : preform} :
clauses.unsat (dnf p) → p.unsat :=
begin
  intros h1 h2, apply h1,
  apply clauses_sat_dnf_core,
  apply neg_free_neg_elim _ (is_nnf_nnf _),
  apply preform.sat_of_implies_of_sat implies_neg_elim,
  have hrw := exists_congr (@nnf_equiv p),
  apply hrw.elim_right h2
end

end int

end omega