/-
Copyright (c) 2020 Zhouhang Zhou. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Zhouhang Zhou
-/
import algebra.support

/-!
# Indicator function

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

- `indicator (s : set α) (f : α → β) (a : α)` is `f a` if `a ∈ s` and is `0` otherwise.
- `mul_indicator (s : set α) (f : α → β) (a : α)` is `f a` if `a ∈ s` and is `1` otherwise.

## Implementation note

In mathematics, an indicator function or a characteristic function is a function
used to indicate membership of an element in a set `s`,
having the value `1` for all elements of `s` and the value `0` otherwise.
But since it is usually used to restrict a function to a certain set `s`,
we let the indicator function take the value `f x` for some function `f`, instead of `1`.
If the usual indicator function is needed, just set `f` to be the constant function `λx, 1`.

The indicator function is implemented non-computably, to avoid having to pass around `decidable`
arguments. This is in contrast with the design of `pi.single` or `set.piecewise`.

## Tags
indicator, characteristic
-/

open_locale big_operators
open function

variables {α β ι M N : Type*}

namespace set

section has_one
variables [has_one M] [has_one N] {s t : set α} {f g : α → M} {a : α}

/-- `indicator s f a` is `f a` if `a ∈ s`, `0` otherwise.  -/
noncomputable def indicator {M} [has_zero M] (s : set α) (f : α → M) : α → M
| x := by haveI := classical.dec_pred (∈ s); exact if x ∈ s then f x else 0

/-- `mul_indicator s f a` is `f a` if `a ∈ s`, `1` otherwise.  -/
@[to_additive]
noncomputable def mul_indicator (s : set α) (f : α → M) : α → M
| x :=  by haveI := classical.dec_pred (∈ s); exact if x ∈ s then f x else 1

@[simp, to_additive] lemma piecewise_eq_mul_indicator [decidable_pred (∈ s)] :
  s.piecewise f 1 = s.mul_indicator f :=
funext $ λ x, @if_congr _ _ _ _ (id _) _ _ _ _ iff.rfl rfl rfl

@[to_additive] lemma mul_indicator_apply (s : set α) (f : α → M) (a : α) [decidable (a ∈ s)] :
  mul_indicator s f a = if a ∈ s then f a else 1 := by convert rfl

@[simp, to_additive] lemma mul_indicator_of_mem (h : a ∈ s) (f : α → M) :
  mul_indicator s f a = f a :=
by { letI := classical.dec (a ∈ s), exact if_pos h }

@[simp, to_additive] lemma mul_indicator_of_not_mem (h : a ∉ s) (f : α → M) :
  mul_indicator s f a = 1 :=
by { letI := classical.dec (a ∈ s), exact if_neg h }

@[to_additive] lemma mul_indicator_eq_one_or_self (s : set α) (f : α → M) (a : α) :
  mul_indicator s f a = 1 ∨ mul_indicator s f a = f a :=
begin
  by_cases h : a ∈ s,
  { exact or.inr (mul_indicator_of_mem h f) },
  { exact or.inl (mul_indicator_of_not_mem h f) }
end

@[simp, to_additive] lemma mul_indicator_apply_eq_self :
  s.mul_indicator f a = f a ↔ (a ∉ s → f a = 1) :=
by letI := classical.dec (a ∈ s); exact ite_eq_left_iff.trans (by rw [@eq_comm _ (f a)])

@[simp, to_additive] lemma mul_indicator_eq_self : s.mul_indicator f = f ↔ mul_support f ⊆ s :=
by simp only [funext_iff, subset_def, mem_mul_support, mul_indicator_apply_eq_self, not_imp_comm]

@[to_additive] lemma mul_indicator_eq_self_of_superset (h1 : s.mul_indicator f = f) (h2 : s ⊆ t) :
  t.mul_indicator f = f :=
by { rw mul_indicator_eq_self at h1 ⊢, exact subset.trans h1 h2 }

@[simp, to_additive] lemma mul_indicator_apply_eq_one :
  mul_indicator s f a = 1 ↔ (a ∈ s → f a = 1) :=
by letI := classical.dec (a ∈ s); exact ite_eq_right_iff

@[simp, to_additive] lemma mul_indicator_eq_one :
  mul_indicator s f = (λ x, 1) ↔ disjoint (mul_support f) s :=
by simp only [funext_iff, mul_indicator_apply_eq_one, set.disjoint_left, mem_mul_support,
  not_imp_not]

@[simp, to_additive] lemma mul_indicator_eq_one' :
  mul_indicator s f = 1 ↔ disjoint (mul_support f) s :=
mul_indicator_eq_one

@[to_additive] lemma mul_indicator_apply_ne_one {a : α} :
  s.mul_indicator f a ≠ 1 ↔ a ∈ s ∩ mul_support f :=
by simp only [ne.def, mul_indicator_apply_eq_one, not_imp, mem_inter_iff, mem_mul_support]

@[simp, to_additive] lemma mul_support_mul_indicator :
  function.mul_support (s.mul_indicator f) = s ∩ function.mul_support f :=
ext $ λ x, by simp [function.mem_mul_support, mul_indicator_apply_eq_one]

/-- If a multiplicative indicator function is not equal to `1` at a point, then that point is in the
set. -/
@[to_additive "If an additive indicator function is not equal to `0` at a point, then that point is
in the set."]
lemma mem_of_mul_indicator_ne_one (h : mul_indicator s f a ≠ 1) : a ∈ s :=
not_imp_comm.1 (λ hn, mul_indicator_of_not_mem hn f) h

@[to_additive] lemma eq_on_mul_indicator : eq_on (mul_indicator s f) f s :=
λ x hx, mul_indicator_of_mem hx f

@[to_additive] lemma mul_support_mul_indicator_subset : mul_support (s.mul_indicator f) ⊆ s :=
λ x hx, hx.imp_symm (λ h, mul_indicator_of_not_mem h f)

@[simp, to_additive] lemma mul_indicator_mul_support : mul_indicator (mul_support f) f = f :=
mul_indicator_eq_self.2 subset.rfl

@[simp, to_additive] lemma mul_indicator_range_comp {ι : Sort*} (f : ι → α) (g : α → M) :
  mul_indicator (range f) g ∘ f = g ∘ f :=
by letI := classical.dec_pred (∈ range f); exact piecewise_range_comp _ _ _

@[to_additive] lemma mul_indicator_congr (h : eq_on f g s) :
  mul_indicator s f = mul_indicator s g :=
funext $ λx, by { simp only [mul_indicator], split_ifs, { exact h h_1 }, refl }

@[simp, to_additive] lemma mul_indicator_univ (f : α → M) : mul_indicator (univ : set α) f = f :=
mul_indicator_eq_self.2 $ subset_univ _

@[simp, to_additive] lemma mul_indicator_empty (f : α → M) : mul_indicator (∅ : set α) f = λa, 1 :=
mul_indicator_eq_one.2 $ disjoint_empty _

@[to_additive] lemma mul_indicator_empty' (f : α → M) : mul_indicator (∅ : set α) f = 1 :=
mul_indicator_empty f

variable (M)

@[simp, to_additive] lemma mul_indicator_one (s : set α) :
  mul_indicator s (λx, (1:M)) = λx, (1:M) :=
mul_indicator_eq_one.2 $ by simp only [mul_support_one, empty_disjoint]

@[simp, to_additive] lemma mul_indicator_one' {s : set α} : s.mul_indicator (1 : α → M) = 1 :=
mul_indicator_one M s

variable {M}

@[to_additive] lemma mul_indicator_mul_indicator (s t : set α) (f : α → M) :
  mul_indicator s (mul_indicator t f) = mul_indicator (s ∩ t) f :=
funext $ λx, by { simp only [mul_indicator], split_ifs, repeat {simp * at * {contextual := tt}} }

@[simp, to_additive] lemma mul_indicator_inter_mul_support (s : set α) (f : α → M) :
  mul_indicator (s ∩ mul_support f) f = mul_indicator s f :=
by rw [← mul_indicator_mul_indicator, mul_indicator_mul_support]

@[to_additive] lemma comp_mul_indicator (h : M → β) (f : α → M) {s : set α} {x : α}
  [decidable_pred (∈ s)] :
  h (s.mul_indicator f x) = s.piecewise (h ∘ f) (const α (h 1)) x :=
by letI := classical.dec_pred (∈ s); convert s.apply_piecewise f (const α 1) (λ _, h)

@[to_additive] lemma mul_indicator_comp_right {s : set α} (f : β → α) {g : α → M} {x : β} :
  mul_indicator (f ⁻¹' s) (g ∘ f) x = mul_indicator s g (f x) :=
by { simp only [mul_indicator], split_ifs; refl }

@[to_additive] lemma mul_indicator_image {s : set α} {f : β → M} {g : α → β} (hg : injective g)
  {x : α} : mul_indicator (g '' s) f (g x) = mul_indicator s (f ∘ g) x :=
by rw [← mul_indicator_comp_right, preimage_image_eq _ hg]

@[to_additive] lemma mul_indicator_comp_of_one {g : M → N} (hg : g 1 = 1) :
  mul_indicator s (g ∘ f) = g ∘ (mul_indicator s f) :=
begin
  funext,
  simp only [mul_indicator],
  split_ifs; simp [*]
end

@[to_additive] lemma comp_mul_indicator_const (c : M) (f : M → N) (hf : f 1 = 1) :
  (λ x, f (s.mul_indicator (λ x, c) x)) = s.mul_indicator (λ x, f c) :=
(mul_indicator_comp_of_one hf).symm

@[to_additive] lemma mul_indicator_preimage (s : set α) (f : α → M) (B : set M) :
  (mul_indicator s f)⁻¹' B = s.ite (f ⁻¹' B) (1 ⁻¹' B) :=
by letI := classical.dec_pred (∈ s); exact piecewise_preimage s f 1 B

@[to_additive] lemma mul_indicator_one_preimage (s : set M) :
  t.mul_indicator 1 ⁻¹' s ∈ ({set.univ, ∅} : set (set α)) :=
begin
  classical,
  rw [mul_indicator_one', preimage_one],
  split_ifs; simp
end

@[to_additive] lemma mul_indicator_const_preimage_eq_union (U : set α) (s : set M) (a : M)
  [decidable (a ∈ s)] [decidable ((1 : M) ∈ s)] :
  U.mul_indicator (λ x, a) ⁻¹' s = (if a ∈ s then U else ∅) ∪ (if (1 : M) ∈ s then Uᶜ else ∅) :=
begin
  rw [mul_indicator_preimage, preimage_one, preimage_const],
  split_ifs; simp [← compl_eq_univ_diff]
end

@[to_additive] lemma mul_indicator_const_preimage (U : set α) (s : set M) (a : M) :
  U.mul_indicator (λ x, a) ⁻¹' s ∈ ({set.univ, U, Uᶜ, ∅} : set (set α)) :=
begin
  classical,
  rw [mul_indicator_const_preimage_eq_union],
  split_ifs; simp
end

lemma indicator_one_preimage [has_zero M] (U : set α) (s : set M) :
  U.indicator 1 ⁻¹' s ∈ ({set.univ, U, Uᶜ, ∅} : set (set α)) :=
indicator_const_preimage _ _ 1

@[to_additive] lemma mul_indicator_preimage_of_not_mem (s : set α) (f : α → M)
  {t : set M} (ht : (1:M) ∉ t) :
  (mul_indicator s f)⁻¹' t = f ⁻¹' t ∩ s :=
by simp [mul_indicator_preimage, pi.one_def, set.preimage_const_of_not_mem ht]

@[to_additive] lemma mem_range_mul_indicator {r : M} {s : set α} {f : α → M} :
  r ∈ range (mul_indicator s f) ↔ (r = 1 ∧ s ≠ univ) ∨ (r ∈ f '' s) :=
by simp [mul_indicator, ite_eq_iff, exists_or_distrib, eq_univ_iff_forall, and_comm, or_comm,
  @eq_comm _ r 1]

@[to_additive] lemma mul_indicator_rel_mul_indicator {r : M → M → Prop} (h1 : r 1 1)
  (ha : a ∈ s → r (f a) (g a)) :
  r (mul_indicator s f a) (mul_indicator s g a) :=
by { simp only [mul_indicator], split_ifs with has has, exacts [ha has, h1] }

end has_one

section monoid
variables [mul_one_class M] {s t : set α} {f g : α → M} {a : α}

@[to_additive] lemma mul_indicator_union_mul_inter_apply (f : α → M) (s t : set α) (a : α) :
  mul_indicator (s ∪ t) f a * mul_indicator (s ∩ t) f a =
    mul_indicator s f a * mul_indicator t f a :=
by by_cases hs : a ∈ s; by_cases ht : a ∈ t; simp *

@[to_additive] lemma mul_indicator_union_mul_inter (f : α → M) (s t : set α) :
  mul_indicator (s ∪ t) f * mul_indicator (s ∩ t) f = mul_indicator s f * mul_indicator t f :=
funext $ mul_indicator_union_mul_inter_apply f s t

@[to_additive] lemma mul_indicator_union_of_not_mem_inter (h : a ∉ s ∩ t) (f : α → M) :
  mul_indicator (s ∪ t) f a = mul_indicator s f a * mul_indicator t f a :=
by rw [← mul_indicator_union_mul_inter_apply f s t, mul_indicator_of_not_mem h, mul_one]

@[to_additive] lemma mul_indicator_union_of_disjoint (h : disjoint s t) (f : α → M) :
  mul_indicator (s ∪ t) f = λa, mul_indicator s f a * mul_indicator t f a :=
funext $ λa, mul_indicator_union_of_not_mem_inter (λ ha, h.le_bot ha) _

@[to_additive] lemma mul_indicator_mul (s : set α) (f g : α → M) :
  mul_indicator s (λa, f a * g a) = λa, mul_indicator s f a * mul_indicator s g a :=
by { funext, simp only [mul_indicator], split_ifs, { refl }, rw mul_one }

@[to_additive] lemma mul_indicator_mul' (s : set α) (f g : α → M) :
  mul_indicator s (f * g) = mul_indicator s f * mul_indicator s g :=
mul_indicator_mul s f g

@[simp, to_additive] lemma mul_indicator_compl_mul_self_apply (s : set α) (f : α → M) (a : α) :
  mul_indicator sᶜ f a * mul_indicator s f a = f a :=
classical.by_cases (λ ha : a ∈ s, by simp [ha]) (λ ha, by simp [ha])

@[simp, to_additive] lemma mul_indicator_compl_mul_self (s : set α) (f : α → M) :
  mul_indicator sᶜ f * mul_indicator s f = f :=
funext $ mul_indicator_compl_mul_self_apply s f

@[simp, to_additive] lemma mul_indicator_self_mul_compl_apply (s : set α) (f : α → M) (a : α) :
  mul_indicator s f a * mul_indicator sᶜ f a = f a :=
classical.by_cases (λ ha : a ∈ s, by simp [ha]) (λ ha, by simp [ha])

@[simp, to_additive]

@[to_additive] lemma mul_indicator_mul_eq_left {f g : α → M}
  (h : disjoint (mul_support f) (mul_support g)) :
  (mul_support f).mul_indicator (f * g) = f :=
begin
  refine (mul_indicator_congr $ λ x hx, _).trans mul_indicator_mul_support,
  have : g x = 1, from nmem_mul_support.1 (disjoint_left.1 h hx),
  rw [pi.mul_apply, this, mul_one]
end

@[to_additive] lemma mul_indicator_mul_eq_right {f g : α → M}
  (h : disjoint (mul_support f) (mul_support g)) :
  (mul_support g).mul_indicator (f * g) = g :=
begin
  refine (mul_indicator_congr $ λ x hx, _).trans mul_indicator_mul_support,
  have : f x = 1, from nmem_mul_support.1 (disjoint_right.1 h hx),
  rw [pi.mul_apply, this, one_mul]
end

@[to_additive] lemma mul_indicator_mul_compl_eq_piecewise
  [decidable_pred (∈ s)] (f g : α → M) :
  s.mul_indicator f * sᶜ.mul_indicator g = s.piecewise f g :=
begin
  ext x,
  by_cases h : x ∈ s,
  { rw [piecewise_eq_of_mem _ _ _ h, pi.mul_apply, set.mul_indicator_of_mem h,
      set.mul_indicator_of_not_mem (set.not_mem_compl_iff.2 h), mul_one] },
  { rw [piecewise_eq_of_not_mem _ _ _ h, pi.mul_apply, set.mul_indicator_of_not_mem h,
      set.mul_indicator_of_mem (set.mem_compl h), one_mul] },
end

/-- `set.mul_indicator` as a `monoid_hom`. -/
@[to_additive "`set.indicator` as an `add_monoid_hom`."]
noncomputable def mul_indicator_hom {α} (M) [mul_one_class M] (s : set α) : (α → M) →* (α → M) :=
{ to_fun := mul_indicator s,
  map_one' := mul_indicator_one M s,
  map_mul' := mul_indicator_mul s }

end monoid

section distrib_mul_action

variables {A : Type*} [add_monoid A] [monoid M] [distrib_mul_action M A]

lemma indicator_smul_apply (s : set α) (r : α → M) (f : α → A) (x : α) :
  indicator s (λ x, r x • f x) x = r x • indicator s f x :=
by { dunfold indicator, split_ifs, exacts [rfl, (smul_zero (r x)).symm] }

lemma indicator_smul (s : set α) (r : α → M) (f : α → A) :
  indicator s (λ (x : α), r x • f x) = λ (x : α), r x • indicator s f x :=
funext $ indicator_smul_apply s r f

lemma indicator_const_smul_apply (s : set α) (r : M) (f : α → A) (x : α) :
  indicator s (λ x, r • f x) x = r • indicator s f x :=
indicator_smul_apply s (λ x, r) f x

lemma indicator_const_smul (s : set α) (r : M) (f : α → A) :
  indicator s (λ (x : α), r • f x) = λ (x : α), r • indicator s f x :=
funext $ indicator_const_smul_apply s r f

end distrib_mul_action

section group
variables {G : Type*} [group G] {s t : set α} {f g : α → G} {a : α}

@[to_additive] lemma mul_indicator_inv' (s : set α) (f : α → G) :
  mul_indicator s (f⁻¹) = (mul_indicator s f)⁻¹ :=
(mul_indicator_hom G s).map_inv f

@[to_additive] lemma mul_indicator_inv (s : set α) (f : α → G) :
  mul_indicator s (λa, (f a)⁻¹) = λa, (mul_indicator s f a)⁻¹ :=
mul_indicator_inv' s f

@[to_additive] lemma mul_indicator_div (s : set α) (f g : α → G) :
  mul_indicator s (λ a, f a / g a) =
  λ a, mul_indicator s f a / mul_indicator s g a :=
(mul_indicator_hom G s).map_div f g

@[to_additive] lemma mul_indicator_div' (s : set α) (f g : α → G) :
  mul_indicator s (f / g) = mul_indicator s f / mul_indicator s g :=
mul_indicator_div s f g

@[to_additive indicator_compl'] lemma mul_indicator_compl (s : set α) (f : α → G) :
  mul_indicator sᶜ f = f * (mul_indicator s f)⁻¹ :=
eq_mul_inv_of_mul_eq $ s.mul_indicator_compl_mul_self f

lemma indicator_compl {G} [add_group G] (s : set α) (f : α → G) :
  indicator sᶜ f = f - indicator s f :=
by rw [sub_eq_add_neg, indicator_compl']

@[to_additive indicator_diff'] lemma mul_indicator_diff (h : s ⊆ t) (f : α → G) :
  mul_indicator (t \ s) f = mul_indicator t f * (mul_indicator s f)⁻¹ :=
eq_mul_inv_of_mul_eq $ by { rw [pi.mul_def, ←mul_indicator_union_of_disjoint, diff_union_self,
  union_eq_self_of_subset_right h], exact disjoint_sdiff_self_left }

lemma indicator_diff {G : Type*} [add_group G] {s t : set α} (h : s ⊆ t) (f : α → G) :
  indicator (t \ s) f = indicator t f - indicator s f :=
by rw [indicator_diff' h, sub_eq_add_neg]

end group

section comm_monoid

variables [comm_monoid M]

/-- Consider a product of `g i (f i)` over a `finset`.  Suppose `g` is a
function such as `pow`, which maps a second argument of `1` to
`1`. Then if `f` is replaced by the corresponding multiplicative indicator
function, the `finset` may be replaced by a possibly larger `finset`
without changing the value of the sum. -/
@[to_additive] lemma prod_mul_indicator_subset_of_eq_one [has_one N] (f : α → N)
  (g : α → N → M) {s t : finset α} (h : s ⊆ t) (hg : ∀ a, g a 1 = 1) :
  ∏ i in s, g i (f i) = ∏ i in t, g i (mul_indicator ↑s f i) :=
begin
  rw ← finset.prod_subset h _,
  { apply finset.prod_congr rfl,
    intros i hi,
    congr,
    symmetry,
    exact mul_indicator_of_mem hi _ },
  { refine λ i hi hn, _,
    convert hg i,
    exact mul_indicator_of_not_mem hn _ }
end

/-- Consider a sum of `g i (f i)` over a `finset`. Suppose `g` is a
function such as multiplication, which maps a second argument of 0 to
0.  (A typical use case would be a weighted sum of `f i * h i` or `f i
• h i`, where `f` gives the weights that are multiplied by some other
function `h`.)  Then if `f` is replaced by the corresponding indicator
function, the `finset` may be replaced by a possibly larger `finset`
without changing the value of the sum. -/
add_decl_doc set.sum_indicator_subset_of_eq_zero

/-- Taking the product of an indicator function over a possibly larger `finset` is the same as
taking the original function over the original `finset`. -/
@[to_additive "Summing an indicator function over a possibly larger `finset` is the same as summing
the original function over the original `finset`."]
lemma prod_mul_indicator_subset (f : α → M) {s t : finset α} (h : s ⊆ t) :
  ∏ i in s, f i = ∏ i in t, mul_indicator ↑s f i :=
prod_mul_indicator_subset_of_eq_one _ (λ a b, b) h (λ _, rfl)

@[to_additive] lemma _root_.finset.prod_mul_indicator_eq_prod_filter
  (s : finset ι) (f : ι → α → M) (t : ι → set α) (g : ι → α) [decidable_pred (λ i, g i ∈ t i)]:
  ∏ i in s, mul_indicator (t i) (f i) (g i) = ∏ i in s.filter (λ i, g i ∈ t i), f i (g i) :=
begin
  refine (finset.prod_filter_mul_prod_filter_not s (λ i, g i ∈ t i) _).symm.trans _,
  refine eq.trans _ (mul_one _),
  exact congr_arg2 (*)
    (finset.prod_congr rfl $ λ x hx, mul_indicator_of_mem (finset.mem_filter.1 hx).2 _)
    (finset.prod_eq_one $ λ x hx, mul_indicator_of_not_mem (finset.mem_filter.1 hx).2 _)
end

@[to_additive] lemma mul_indicator_finset_prod (I : finset ι) (s : set α) (f : ι → α → M) :
  mul_indicator s (∏ i in I, f i) = ∏ i in I, mul_indicator s (f i) :=
(mul_indicator_hom M s).map_prod _ _

@[to_additive] lemma mul_indicator_finset_bUnion {ι} (I : finset ι)
  (s : ι → set α) {f : α → M} : (∀ (i ∈ I) (j ∈ I), i ≠ j → disjoint (s i) (s j)) →
  mul_indicator (⋃ i ∈ I, s i) f = λ a, ∏ i in I, mul_indicator (s i) f a :=
begin
  classical,
  refine finset.induction_on I _ _,
  { intro h, funext, simp },
  assume a I haI ih hI,
  funext,
  rw [finset.prod_insert haI, finset.set_bUnion_insert, mul_indicator_union_of_not_mem_inter, ih _],
  { assume i hi j hj hij,
    exact hI i (finset.mem_insert_of_mem hi) j (finset.mem_insert_of_mem hj) hij },
  simp only [not_exists, exists_prop, mem_Union, mem_inter_iff, not_and],
  assume hx a' ha',
  refine disjoint_left.1 (hI a (finset.mem_insert_self _ _) a' (finset.mem_insert_of_mem ha') _) hx,
  exact (ne_of_mem_of_not_mem ha' haI).symm
end

@[to_additive] lemma mul_indicator_finset_bUnion_apply {ι} (I : finset ι)
  (s : ι → set α) {f : α → M} (h : ∀ (i ∈ I) (j ∈ I), i ≠ j → disjoint (s i) (s j)) (x : α) :
  mul_indicator (⋃ i ∈ I, s i) f x = ∏ i in I, mul_indicator (s i) f x :=
by rw set.mul_indicator_finset_bUnion I s h

end comm_monoid

section mul_zero_class

variables [mul_zero_class M] {s t : set α} {f g : α → M} {a : α}

lemma indicator_mul (s : set α) (f g : α → M) :
  indicator s (λa, f a * g a) = λa, indicator s f a * indicator s g a :=
by { funext, simp only [indicator], split_ifs, { refl }, rw mul_zero }

lemma indicator_mul_left (s : set α) (f g : α → M) :
  indicator s (λa, f a * g a) a = indicator s f a * g a :=
by { simp only [indicator], split_ifs, { refl }, rw [zero_mul] }

lemma indicator_mul_right (s : set α) (f g : α → M) :
  indicator s (λa, f a * g a) a = f a * indicator s g a :=
by { simp only [indicator], split_ifs, { refl }, rw [mul_zero] }

lemma inter_indicator_mul {t1 t2 : set α} (f g : α → M) (x : α) :
  (t1 ∩ t2).indicator (λ x, f x * g x) x = t1.indicator f x * t2.indicator g x :=
by { rw [← set.indicator_indicator], simp [indicator] }

end mul_zero_class

section mul_zero_one_class

variables [mul_zero_one_class M]

lemma inter_indicator_one {s t : set α} :
  (s ∩ t).indicator (1 : _ → M) = s.indicator 1 * t.indicator 1 :=
funext (λ _, by simpa only [← inter_indicator_mul, pi.mul_apply, pi.one_apply, one_mul])

lemma indicator_prod_one {s : set α} {t : set β} {x : α} {y : β} :
  (s ×ˢ t).indicator (1 : _ → M) (x, y) = s.indicator 1 x * t.indicator 1 y :=
by { classical, simp [indicator_apply, ←ite_and] }

variables (M) [nontrivial M]

lemma indicator_eq_zero_iff_not_mem {U : set α} {x : α} :
  indicator U 1 x = (0 : M) ↔ x ∉ U :=
by { classical, simp [indicator_apply, imp_false] }

lemma indicator_eq_one_iff_mem {U : set α} {x : α} :
  indicator U 1 x = (1 : M) ↔ x ∈ U :=
by { classical, simp [indicator_apply, imp_false] }

lemma indicator_one_inj {U V : set α} (h : indicator U (1 : α → M) = indicator V 1) : U = V :=
by { ext, simp_rw [← indicator_eq_one_iff_mem M, h] }

end mul_zero_one_class

section order

variables [has_one M] {s t : set α} {f g : α → M} {a : α} {y : M}

section
variables [has_le M]

@[to_additive] lemma mul_indicator_apply_le' (hfg : a ∈ s → f a ≤ y) (hg : a ∉ s → 1 ≤ y) :
  mul_indicator s f a ≤ y :=
begin
  by_cases ha : a ∈ s,
  { simpa [ha] using hfg ha },
  { simpa [ha] using hg ha },
end

@[to_additive] lemma mul_indicator_le' (hfg : ∀ a ∈ s, f a ≤ g a) (hg : ∀ a ∉ s, 1 ≤ g a) :
  mul_indicator s f ≤ g :=
λ a, mul_indicator_apply_le' (hfg _) (hg _)

@[to_additive] lemma le_mul_indicator_apply {y} (hfg : a ∈ s → y ≤ g a) (hf : a ∉ s → y ≤ 1) :
  y ≤ mul_indicator s g a :=
@mul_indicator_apply_le' α Mᵒᵈ ‹_› _ _ _ _ _ hfg hf

@[to_additive] lemma le_mul_indicator (hfg : ∀ a ∈ s, f a ≤ g a) (hf : ∀ a ∉ s, f a ≤ 1) :
  f ≤ mul_indicator s g :=
λ a, le_mul_indicator_apply (hfg _) (hf _)

end

variables [preorder M]

@[to_additive indicator_apply_nonneg]
lemma one_le_mul_indicator_apply (h : a ∈ s → 1 ≤ f a) : 1 ≤ mul_indicator s f a :=
le_mul_indicator_apply h (λ _, le_rfl)

@[to_additive indicator_nonneg]
lemma one_le_mul_indicator (h : ∀ a ∈ s, 1 ≤ f a) (a : α) : 1 ≤ mul_indicator s f a :=
one_le_mul_indicator_apply (h a)

@[to_additive] lemma mul_indicator_apply_le_one (h : a ∈ s → f a ≤ 1) : mul_indicator s f a ≤ 1 :=
mul_indicator_apply_le' h (λ _, le_rfl)

@[to_additive] lemma mul_indicator_le_one (h : ∀ a ∈ s, f a ≤ 1) (a : α) :
  mul_indicator s f a ≤ 1 :=
mul_indicator_apply_le_one (h a)

@[to_additive] lemma mul_indicator_le_mul_indicator (h : f a ≤ g a) :
  mul_indicator s f a ≤ mul_indicator s g a :=
mul_indicator_rel_mul_indicator le_rfl (λ _, h)

attribute [mono] mul_indicator_le_mul_indicator indicator_le_indicator

@[to_additive] lemma mul_indicator_le_mul_indicator_of_subset (h : s ⊆ t) (hf : ∀ a, 1 ≤ f a)
  (a : α) :
  mul_indicator s f a ≤ mul_indicator t f a :=
mul_indicator_apply_le' (λ ha, le_mul_indicator_apply (λ _, le_rfl) (λ hat, (hat $ h ha).elim))
  (λ ha, one_le_mul_indicator_apply (λ _, hf _))

@[to_additive] lemma mul_indicator_le_self' (hf : ∀ x ∉ s, 1 ≤ f x) : mul_indicator s f ≤ f :=
mul_indicator_le' (λ _ _, le_rfl) hf

@[to_additive] lemma mul_indicator_Union_apply {ι M} [complete_lattice M] [has_one M]
  (h1 : (⊥:M) = 1) (s : ι → set α) (f : α → M) (x : α) :
  mul_indicator (⋃ i, s i) f x = ⨆ i, mul_indicator (s i) f x :=
begin
  by_cases hx : x ∈ ⋃ i, s i,
  { rw [mul_indicator_of_mem hx],
    rw [mem_Union] at hx,
    refine le_antisymm _ (supr_le $ λ i, mul_indicator_le_self' (λ x hx, h1 ▸ bot_le) x),
    rcases hx with ⟨i, hi⟩,
    exact le_supr_of_le i (ge_of_eq $ mul_indicator_of_mem hi _) },
  { rw [mul_indicator_of_not_mem hx],
    simp only [mem_Union, not_exists] at hx,
    simp [hx, ← h1] }
end

end order

section canonically_ordered_monoid

variables [canonically_ordered_monoid M]

@[to_additive] lemma mul_indicator_le_self (s : set α) (f : α → M) :
  mul_indicator s f ≤ f :=
mul_indicator_le_self' $ λ _ _, one_le _

@[to_additive] lemma mul_indicator_apply_le {a : α} {s : set α} {f g : α → M}
  (hfg : a ∈ s → f a ≤ g a) :
  mul_indicator s f a ≤ g a :=
mul_indicator_apply_le' hfg $ λ _, one_le _

@[to_additive] lemma mul_indicator_le {s : set α} {f g : α → M} (hfg : ∀ a ∈ s, f a ≤ g a) :
  mul_indicator s f ≤ g :=
mul_indicator_le' hfg $ λ _ _, one_le _

end canonically_ordered_monoid

lemma indicator_le_indicator_nonneg {β} [linear_order β] [has_zero β] (s : set α) (f : α → β) :
  s.indicator f ≤ {x | 0 ≤ f x}.indicator f :=
begin
  intro x,
  classical,
  simp_rw indicator_apply,
  split_ifs,
  { exact le_rfl, },
  { exact (not_le.mp h_1).le, },
  { exact h_1, },
  { exact le_rfl, },
end

lemma indicator_nonpos_le_indicator {β} [linear_order β] [has_zero β] (s : set α) (f : α → β) :
  {x | f x ≤ 0}.indicator f ≤ s.indicator f :=
@indicator_le_indicator_nonneg α βᵒᵈ _ _ s f

end set

@[to_additive] lemma monoid_hom.map_mul_indicator
  {M N : Type*} [mul_one_class M] [mul_one_class N] (f : M →* N)
  (s : set α) (g : α → M) (x : α) :
  f (s.mul_indicator g x) = s.mul_indicator (f ∘ g) x :=
congr_fun (set.mul_indicator_comp_of_one f.map_one).symm x


/-
Copyright (c) 2021 Martin Zinkevich. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Martin Zinkevich, Vincent Beffara

! This file was ported from Lean 3 source module probability.integration
! leanprover-community/mathlib commit 57ac39bd365c2f80589a700f9fbb664d3a1a30c2
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.MeasureTheory.Integral.SetIntegral
import Mathbin.Probability.Independence

/-!
# Integration in Probability Theory

Integration results for independent random variables. Specifically, for two
independent random variables X and Y over the extended non-negative
reals, `E[X * Y] = E[X] * E[Y]`, and similar results.

## Implementation notes

Many lemmas in this file take two arguments of the same typeclass. It is worth remembering that lean
will always pick the later typeclass in this situation, and does not care whether the arguments are
`[]`, `{}`, or `()`. All of these use the `measurable_space` `M2` to define `μ`:

```lean
example {M1 : measurable_space Ω} [M2 : measurable_space Ω] {μ : measure Ω} : sorry := sorry
example [M1 : measurable_space Ω] {M2 : measurable_space Ω} {μ : measure Ω} : sorry := sorry
```

-/

noncomputable section

open Set MeasureTheory

open ENNReal MeasureTheory

variable {Ω : Type _} {mΩ : MeasurableSpace Ω} {μ : Measure Ω} {f g : Ω → ℝ≥0∞} {X Y : Ω → ℝ}

namespace ProbabilityTheory

/-- If a random variable `f` in `ℝ≥0∞` is independent of an event `T`, then if you restrict the
  random variable to `T`, then `E[f * indicator T c 0]=E[f] * E[indicator T c 0]`. It is useful for
  `lintegral_mul_eq_lintegral_mul_lintegral_of_independent_measurable_space`. -/
theorem lintegral_mul_indicator_eq_lintegral_mul_lintegral_indicator {Mf mΩ : MeasurableSpace Ω}
    {μ : Measure Ω} (hMf : Mf ≤ mΩ) (c : ℝ≥0∞) {T : Set Ω} (h_meas_T : MeasurableSet T)
    (h_ind : IndepSetsCat { s | measurable_set[Mf] s } {T} μ) (h_meas_f : measurable[Mf] f) :
    (∫⁻ ω, f ω * T.indicator (fun _ => c) ω ∂μ) =
      (∫⁻ ω, f ω ∂μ) * ∫⁻ ω, T.indicator (fun _ => c) ω ∂μ :=
  by
  revert f
  have h_mul_indicator : ∀ g, Measurable g → Measurable fun a => g a * T.indicator (fun x => c) a :=
    fun g h_mg => h_mg.mul (measurable_const.indicator h_meas_T)
  apply Measurable.eNNReal_induction
  · intro c' s' h_meas_s'
    simp_rw [← inter_indicator_mul]
    rw [lintegral_indicator _ (MeasurableSet.inter (hMf _ h_meas_s') h_meas_T),
      lintegral_indicator _ (hMf _ h_meas_s'), lintegral_indicator _ h_meas_T]
    simp only [measurable_const, lintegral_const, univ_inter, lintegral_const_mul,
      MeasurableSet.univ, measure.restrict_apply]
    ring_nf
    congr
    rw [mul_comm, h_ind s' T h_meas_s' (Set.mem_singleton _)]
  · intro f' g h_univ h_meas_f' h_meas_g h_ind_f' h_ind_g
    have h_measM_f' : Measurable f' := h_meas_f'.mono hMf le_rfl
    have h_measM_g : Measurable g := h_meas_g.mono hMf le_rfl
    simp_rw [Pi.add_apply, right_distrib]
    rw [lintegral_add_left (h_mul_indicator _ h_measM_f'), lintegral_add_left h_measM_f',
      right_distrib, h_ind_f', h_ind_g]
  · intro f h_meas_f h_mono_f h_ind_f
    have h_measM_f : ∀ n, Measurable (f n) := fun n => (h_meas_f n).mono hMf le_rfl
    simp_rw [ENNReal.supᵢ_mul]
    rw [lintegral_supr h_measM_f h_mono_f, lintegral_supr, ENNReal.supᵢ_mul]
    · simp_rw [← h_ind_f]
    · exact fun n => h_mul_indicator _ (h_measM_f n)
    · exact fun m n h_le a => mul_le_mul_right' (h_mono_f h_le a) _
#align probability_theory.lintegral_mul_indicator_eq_lintegral_mul_lintegral_indicator ProbabilityTheory.lintegral_mul_indicator_eq_lintegral_mul_lintegral_indicator

/-- If `f` and `g` are independent random variables with values in `ℝ≥0∞`,
   then `E[f * g] = E[f] * E[g]`. However, instead of directly using the independence
   of the random variables, it uses the independence of measurable spaces for the
   domains of `f` and `g`. This is similar to the sigma-algebra approach to
   independence. See `lintegral_mul_eq_lintegral_mul_lintegral_of_independent_fn` for
   a more common variant of the product of independent variables. -/
theorem lintegral_mul_eq_lintegral_mul_lintegral_of_independent_measurableSpace
    {Mf Mg mΩ : MeasurableSpace Ω} {μ : Measure Ω} (hMf : Mf ≤ mΩ) (hMg : Mg ≤ mΩ)
    (h_ind : IndepCat Mf Mg μ) (h_meas_f : measurable[Mf] f) (h_meas_g : measurable[Mg] g) :
    (∫⁻ ω, f ω * g ω ∂μ) = (∫⁻ ω, f ω ∂μ) * ∫⁻ ω, g ω ∂μ :=
  by
  revert g
  have h_measM_f : Measurable f := h_meas_f.mono hMf le_rfl
  apply Measurable.eNNReal_induction
  · intro c s h_s
    apply lintegral_mul_indicator_eq_lintegral_mul_lintegral_indicator hMf _ (hMg _ h_s) _ h_meas_f
    apply indep_sets_of_indep_sets_of_le_right h_ind
    rwa [singleton_subset_iff]
  · intro f' g h_univ h_measMg_f' h_measMg_g h_ind_f' h_ind_g'
    have h_measM_f' : Measurable f' := h_measMg_f'.mono hMg le_rfl
    have h_measM_g : Measurable g := h_measMg_g.mono hMg le_rfl
    simp_rw [Pi.add_apply, left_distrib]
    rw [lintegral_add_left h_measM_f', lintegral_add_left (h_measM_f.mul h_measM_f'), left_distrib,
      h_ind_f', h_ind_g']
  · intro f' h_meas_f' h_mono_f' h_ind_f'
    have h_measM_f' : ∀ n, Measurable (f' n) := fun n => (h_meas_f' n).mono hMg le_rfl
    simp_rw [ENNReal.mul_supᵢ]
    rw [lintegral_supr, lintegral_supr h_measM_f' h_mono_f', ENNReal.mul_supᵢ]
    · simp_rw [← h_ind_f']
    · exact fun n => h_measM_f.mul (h_measM_f' n)
    · exact fun n m (h_le : n ≤ m) a => mul_le_mul_left' (h_mono_f' h_le a) _
#align probability_theory.lintegral_mul_eq_lintegral_mul_lintegral_of_independent_measurable_space ProbabilityTheory.lintegral_mul_eq_lintegral_mul_lintegral_of_independent_measurableSpace

/-- If `f` and `g` are independent random variables with values in `ℝ≥0∞`,
   then `E[f * g] = E[f] * E[g]`. -/
theorem lintegral_mul_eq_lintegral_mul_lintegral_of_indepFunCat (h_meas_f : Measurable f)
    (h_meas_g : Measurable g) (h_indep_fun : IndepFunCat f g μ) :
    (∫⁻ ω, (f * g) ω ∂μ) = (∫⁻ ω, f ω ∂μ) * ∫⁻ ω, g ω ∂μ :=
  lintegral_mul_eq_lintegral_mul_lintegral_of_independent_measurableSpace
    (measurable_iff_comap_le.1 h_meas_f) (measurable_iff_comap_le.1 h_meas_g) h_indep_fun
    (Measurable.of_comap_le le_rfl) (Measurable.of_comap_le le_rfl)
#align probability_theory.lintegral_mul_eq_lintegral_mul_lintegral_of_indep_fun ProbabilityTheory.lintegral_mul_eq_lintegral_mul_lintegral_of_indepFunCat

/-- If `f` and `g` with values in `ℝ≥0∞` are independent and almost everywhere measurable,
   then `E[f * g] = E[f] * E[g]` (slightly generalizing
   `lintegral_mul_eq_lintegral_mul_lintegral_of_indep_fun`). -/
theorem lintegral_mul_eq_lintegral_mul_lintegral_of_indep_fun' (h_meas_f : AeMeasurable f μ)
    (h_meas_g : AeMeasurable g μ) (h_indep_fun : IndepFunCat f g μ) :
    (∫⁻ ω, (f * g) ω ∂μ) = (∫⁻ ω, f ω ∂μ) * ∫⁻ ω, g ω ∂μ :=
  by
  have fg_ae : f * g =ᵐ[μ] h_meas_f.mk _ * h_meas_g.mk _ := h_meas_f.ae_eq_mk.mul h_meas_g.ae_eq_mk
  rw [lintegral_congr_ae h_meas_f.ae_eq_mk, lintegral_congr_ae h_meas_g.ae_eq_mk,
    lintegral_congr_ae fg_ae]
  apply
    lintegral_mul_eq_lintegral_mul_lintegral_of_indep_fun h_meas_f.measurable_mk
      h_meas_g.measurable_mk
  exact h_indep_fun.ae_eq h_meas_f.ae_eq_mk h_meas_g.ae_eq_mk
#align probability_theory.lintegral_mul_eq_lintegral_mul_lintegral_of_indep_fun' ProbabilityTheory.lintegral_mul_eq_lintegral_mul_lintegral_of_indep_fun'

theorem lintegral_mul_eq_lintegral_mul_lintegral_of_indep_fun'' (h_meas_f : AeMeasurable f μ)
    (h_meas_g : AeMeasurable g μ) (h_indep_fun : IndepFunCat f g μ) :
    (∫⁻ ω, f ω * g ω ∂μ) = (∫⁻ ω, f ω ∂μ) * ∫⁻ ω, g ω ∂μ :=
  lintegral_mul_eq_lintegral_mul_lintegral_of_indep_fun' h_meas_f h_meas_g h_indep_fun
#align probability_theory.lintegral_mul_eq_lintegral_mul_lintegral_of_indep_fun'' ProbabilityTheory.lintegral_mul_eq_lintegral_mul_lintegral_of_indep_fun''

/-- The product of two independent, integrable, real_valued random variables is integrable. -/
theorem IndepFunCat.integrableMul {β : Type _} [MeasurableSpace β] {X Y : Ω → β}
    [NormedDivisionRing β] [BorelSpace β] (hXY : IndepFunCat X Y μ) (hX : Integrable X μ)
    (hY : Integrable Y μ) : Integrable (X * Y) μ :=
  by
  let nX : Ω → ENNReal := fun a => ‖X a‖₊
  let nY : Ω → ENNReal := fun a => ‖Y a‖₊
  have hXY' : indep_fun (fun a => ‖X a‖₊) (fun a => ‖Y a‖₊) μ :=
    hXY.comp measurable_nnnorm measurable_nnnorm
  have hXY'' : indep_fun nX nY μ :=
    hXY'.comp measurable_coe_nNReal_eNNReal measurable_coe_nNReal_eNNReal
  have hnX : AeMeasurable nX μ := hX.1.AeMeasurable.nnnorm.coe_nNReal_eNNReal
  have hnY : AeMeasurable nY μ := hY.1.AeMeasurable.nnnorm.coe_nNReal_eNNReal
  have hmul : (∫⁻ a, nX a * nY a ∂μ) = (∫⁻ a, nX a ∂μ) * ∫⁻ a, nY a ∂μ := by
    convert lintegral_mul_eq_lintegral_mul_lintegral_of_indep_fun' hnX hnY hXY''
  refine' ⟨hX.1.mul hY.1, _⟩
  simp_rw [has_finite_integral, Pi.mul_apply, nnnorm_mul, ENNReal.coe_mul, hmul]
  exact ennreal.mul_lt_top_iff.mpr (Or.inl ⟨hX.2, hY.2⟩)
#align probability_theory.indep_fun.integrable_mul ProbabilityTheory.IndepFunCat.integrableMul

/-- If the product of two independent real_valued random variables is integrable and
the second one is not almost everywhere zero, then the first one is integrable. -/
theorem IndepFunCat.integrableLeftOfIntegrableMul {β : Type _} [MeasurableSpace β] {X Y : Ω → β}
    [NormedDivisionRing β] [BorelSpace β] (hXY : IndepFunCat X Y μ) (h'XY : Integrable (X * Y) μ)
    (hX : AeStronglyMeasurable X μ) (hY : AeStronglyMeasurable Y μ) (h'Y : ¬Y =ᵐ[μ] 0) :
    Integrable X μ := by
  refine' ⟨hX, _⟩
  have I : (∫⁻ ω, ‖Y ω‖₊ ∂μ) ≠ 0 := by
    intro H
    have I : (fun ω => ↑‖Y ω‖₊) =ᵐ[μ] 0 := (lintegral_eq_zero_iff' hY.ennnorm).1 H
    apply h'Y
    filter_upwards [I]with ω hω
    simpa using hω
  apply lt_top_iff_ne_top.2 fun H => _
  have J : indep_fun (fun ω => ↑‖X ω‖₊) (fun ω => ↑‖Y ω‖₊) μ :=
    by
    have M : Measurable fun x : β => (‖x‖₊ : ℝ≥0∞) := measurable_nnnorm.coe_nnreal_ennreal
    apply indep_fun.comp hXY M M
  have A : (∫⁻ ω, ‖X ω * Y ω‖₊ ∂μ) < ∞ := h'XY.2
  simp only [nnnorm_mul, ENNReal.coe_mul] at A
  rw [lintegral_mul_eq_lintegral_mul_lintegral_of_indep_fun'' hX.ennnorm hY.ennnorm J, H] at A
  simpa [ENNReal.top_mul', I] using A
#align probability_theory.indep_fun.integrable_left_of_integrable_mul ProbabilityTheory.IndepFunCat.integrableLeftOfIntegrableMul

/-- If the product of two independent real_valued random variables is integrable and the
first one is not almost everywhere zero, then the second one is integrable. -/
theorem IndepFunCat.integrableRightOfIntegrableMul {β : Type _} [MeasurableSpace β] {X Y : Ω → β}
    [NormedDivisionRing β] [BorelSpace β] (hXY : IndepFunCat X Y μ) (h'XY : Integrable (X * Y) μ)
    (hX : AeStronglyMeasurable X μ) (hY : AeStronglyMeasurable Y μ) (h'X : ¬X =ᵐ[μ] 0) :
    Integrable Y μ := by
  refine' ⟨hY, _⟩
  have I : (∫⁻ ω, ‖X ω‖₊ ∂μ) ≠ 0 := by
    intro H
    have I : (fun ω => ↑‖X ω‖₊) =ᵐ[μ] 0 := (lintegral_eq_zero_iff' hX.ennnorm).1 H
    apply h'X
    filter_upwards [I]with ω hω
    simpa using hω
  apply lt_top_iff_ne_top.2 fun H => _
  have J : indep_fun (fun ω => ↑‖X ω‖₊) (fun ω => ↑‖Y ω‖₊) μ :=
    by
    have M : Measurable fun x : β => (‖x‖₊ : ℝ≥0∞) := measurable_nnnorm.coe_nnreal_ennreal
    apply indep_fun.comp hXY M M
  have A : (∫⁻ ω, ‖X ω * Y ω‖₊ ∂μ) < ∞ := h'XY.2
  simp only [nnnorm_mul, ENNReal.coe_mul] at A
  rw [lintegral_mul_eq_lintegral_mul_lintegral_of_indep_fun'' hX.ennnorm hY.ennnorm J, H] at A
  simpa [ENNReal.top_mul', I] using A
#align probability_theory.indep_fun.integrable_right_of_integrable_mul ProbabilityTheory.IndepFunCat.integrableRightOfIntegrableMul

/-- The (Bochner) integral of the product of two independent, nonnegative random
  variables is the product of their integrals. The proof is just plumbing around
  `lintegral_mul_eq_lintegral_mul_lintegral_of_indep_fun'`. -/
theorem IndepFunCat.integral_mul_of_nonneg (hXY : IndepFunCat X Y μ) (hXp : 0 ≤ X) (hYp : 0 ≤ Y)
    (hXm : AeMeasurable X μ) (hYm : AeMeasurable Y μ) :
    integral μ (X * Y) = integral μ X * integral μ Y :=
  by
  have h1 : AeMeasurable (fun a => ENNReal.ofReal (X a)) μ :=
    ennreal.measurable_of_real.comp_ae_measurable hXm
  have h2 : AeMeasurable (fun a => ENNReal.ofReal (Y a)) μ :=
    ennreal.measurable_of_real.comp_ae_measurable hYm
  have h3 : AeMeasurable (X * Y) μ := hXm.mul hYm
  have h4 : 0 ≤ᵐ[μ] X * Y := ae_of_all _ fun ω => mul_nonneg (hXp ω) (hYp ω)
  rw [integral_eq_lintegral_of_nonneg_ae (ae_of_all _ hXp) hXm.ae_strongly_measurable,
    integral_eq_lintegral_of_nonneg_ae (ae_of_all _ hYp) hYm.ae_strongly_measurable,
    integral_eq_lintegral_of_nonneg_ae h4 h3.ae_strongly_measurable]
  simp_rw [← ENNReal.toReal_mul, Pi.mul_apply, ENNReal.ofReal_mul (hXp _)]
  congr
  apply lintegral_mul_eq_lintegral_mul_lintegral_of_indep_fun' h1 h2
  exact hXY.comp ENNReal.measurable_ofReal ENNReal.measurable_ofReal
#align probability_theory.indep_fun.integral_mul_of_nonneg ProbabilityTheory.IndepFunCat.integral_mul_of_nonneg

/-- The (Bochner) integral of the product of two independent, integrable random
  variables is the product of their integrals. The proof is pedestrian decomposition
  into their positive and negative parts in order to apply `indep_fun.integral_mul_of_nonneg`
  four times. -/
theorem IndepFunCat.integral_mul_of_integrable (hXY : IndepFunCat X Y μ) (hX : Integrable X μ)
    (hY : Integrable Y μ) : integral μ (X * Y) = integral μ X * integral μ Y :=
  by
  let pos : ℝ → ℝ := fun x => max x 0
  let neg : ℝ → ℝ := fun x => max (-x) 0
  have posm : Measurable Pos := measurable_id'.max measurable_const
  have negm : Measurable neg := measurable_id'.neg.max measurable_const
  let Xp := Pos ∘ X
  -- `X⁺` would look better but it makes `simp_rw` below fail
  let Xm := neg ∘ X
  let Yp := Pos ∘ Y
  let Ym := neg ∘ Y
  have hXpm : X = Xp - Xm := funext fun ω => (max_zero_sub_max_neg_zero_eq_self (X ω)).symm
  have hYpm : Y = Yp - Ym := funext fun ω => (max_zero_sub_max_neg_zero_eq_self (Y ω)).symm
  have hp1 : 0 ≤ Xm := fun ω => le_max_right _ _
  have hp2 : 0 ≤ Xp := fun ω => le_max_right _ _
  have hp3 : 0 ≤ Ym := fun ω => le_max_right _ _
  have hp4 : 0 ≤ Yp := fun ω => le_max_right _ _
  have hm1 : AeMeasurable Xm μ := hX.1.AeMeasurable.neg.max aeMeasurableConst
  have hm2 : AeMeasurable Xp μ := hX.1.AeMeasurable.max aeMeasurableConst
  have hm3 : AeMeasurable Ym μ := hY.1.AeMeasurable.neg.max aeMeasurableConst
  have hm4 : AeMeasurable Yp μ := hY.1.AeMeasurable.max aeMeasurableConst
  have hv1 : integrable Xm μ := hX.neg_part
  have hv2 : integrable Xp μ := hX.pos_part
  have hv3 : integrable Ym μ := hY.neg_part
  have hv4 : integrable Yp μ := hY.pos_part
  have hi1 : indep_fun Xm Ym μ := hXY.comp negm negm
  have hi2 : indep_fun Xp Ym μ := hXY.comp posm negm
  have hi3 : indep_fun Xm Yp μ := hXY.comp negm posm
  have hi4 : indep_fun Xp Yp μ := hXY.comp posm posm
  have hl1 : integrable (Xm * Ym) μ := hi1.integrable_mul hv1 hv3
  have hl2 : integrable (Xp * Ym) μ := hi2.integrable_mul hv2 hv3
  have hl3 : integrable (Xm * Yp) μ := hi3.integrable_mul hv1 hv4
  have hl4 : integrable (Xp * Yp) μ := hi4.integrable_mul hv2 hv4
  have hl5 : integrable (Xp * Yp - Xm * Yp) μ := hl4.sub hl3
  have hl6 : integrable (Xp * Ym - Xm * Ym) μ := hl2.sub hl1
  simp_rw [hXpm, hYpm, mul_sub, sub_mul]
  rw [integral_sub' hl5 hl6, integral_sub' hl4 hl3, integral_sub' hl2 hl1, integral_sub' hv2 hv1,
    integral_sub' hv4 hv3, hi1.integral_mul_of_nonneg hp1 hp3 hm1 hm3,
    hi2.integral_mul_of_nonneg hp2 hp3 hm2 hm3, hi3.integral_mul_of_nonneg hp1 hp4 hm1 hm4,
    hi4.integral_mul_of_nonneg hp2 hp4 hm2 hm4]
  ring
#align probability_theory.indep_fun.integral_mul_of_integrable ProbabilityTheory.IndepFunCat.integral_mul_of_integrable

/-- The (Bochner) integral of the product of two independent random
  variables is the product of their integrals. -/
theorem IndepFunCat.integral_mul (hXY : IndepFunCat X Y μ) (hX : AeStronglyMeasurable X μ)
    (hY : AeStronglyMeasurable Y μ) : integral μ (X * Y) = integral μ X * integral μ Y :=
  by
  by_cases h'X : X =ᵐ[μ] 0
  · have h' : X * Y =ᵐ[μ] 0 := by
      filter_upwards [h'X]with ω hω
      simp [hω]
    simp only [integral_congr_ae h'X, integral_congr_ae h', Pi.zero_apply, integral_const,
      Algebra.id.smul_eq_mul, MulZeroClass.mul_zero, MulZeroClass.zero_mul]
  by_cases h'Y : Y =ᵐ[μ] 0
  · have h' : X * Y =ᵐ[μ] 0 := by
      filter_upwards [h'Y]with ω hω
      simp [hω]
    simp only [integral_congr_ae h'Y, integral_congr_ae h', Pi.zero_apply, integral_const,
      Algebra.id.smul_eq_mul, MulZeroClass.mul_zero, MulZeroClass.zero_mul]
  by_cases h : integrable (X * Y) μ
  · have HX : integrable X μ := hXY.integrable_left_of_integrable_mul h hX hY h'Y
    have HY : integrable Y μ := hXY.integrable_right_of_integrable_mul h hX hY h'X
    exact hXY.integral_mul_of_integrable HX HY
  · have I : ¬(integrable X μ ∧ integrable Y μ) :=
      by
      rintro ⟨HX, HY⟩
      exact h (hXY.integrable_mul HX HY)
    rw [not_and_or] at I
    cases I <;> simp [integral_undef, I, h]
#align probability_theory.indep_fun.integral_mul ProbabilityTheory.IndepFunCat.integral_mul

theorem IndepFunCat.integral_mul' (hXY : IndepFunCat X Y μ) (hX : AeStronglyMeasurable X μ)
    (hY : AeStronglyMeasurable Y μ) :
    (integral μ fun ω => X ω * Y ω) = integral μ X * integral μ Y :=
  hXY.integral_mul hX hY
#align probability_theory.indep_fun.integral_mul' ProbabilityTheory.IndepFunCat.integral_mul'

/-- Independence of functions `f` and `g` into arbitrary types is characterized by the relation
  `E[(φ ∘ f) * (ψ ∘ g)] = E[φ ∘ f] * E[ψ ∘ g]` for all measurable `φ` and `ψ` with values in `ℝ`
  satisfying appropriate integrability conditions. -/
theorem indepFunCat_iff_integral_comp_mul [IsFiniteMeasure μ] {β β' : Type _}
    {mβ : MeasurableSpace β} {mβ' : MeasurableSpace β'} {f : Ω → β} {g : Ω → β'}
    {hfm : Measurable f} {hgm : Measurable g} :
    IndepFunCat f g μ ↔
      ∀ {φ : β → ℝ} {ψ : β' → ℝ},
        Measurable φ →
          Measurable ψ →
            Integrable (φ ∘ f) μ →
              Integrable (ψ ∘ g) μ →
                integral μ (φ ∘ f * ψ ∘ g) = integral μ (φ ∘ f) * integral μ (ψ ∘ g) :=
  by
  refine' ⟨fun hfg _ _ hφ hψ => indep_fun.integral_mul_of_integrable (hfg.comp hφ hψ), _⟩
  rintro h _ _ ⟨A, hA, rfl⟩ ⟨B, hB, rfl⟩
  specialize
    h (measurable_one.indicator hA) (measurable_one.indicator hB)
      ((integrable_const 1).indicator (hfm.comp measurable_id hA))
      ((integrable_const 1).indicator (hgm.comp measurable_id hB))
  rwa [← ENNReal.toReal_eq_toReal (measure_ne_top μ _), ENNReal.toReal_mul, ←
    integral_indicator_one ((hfm hA).inter (hgm hB)), ← integral_indicator_one (hfm hA), ←
    integral_indicator_one (hgm hB), Set.inter_indicator_one]
  exact ENNReal.mul_ne_top (measure_ne_top μ _) (measure_ne_top μ _)
#align probability_theory.indep_fun_iff_integral_comp_mul ProbabilityTheory.indepFunCat_iff_integral_comp_mul

end ProbabilityTheory


namespace list
    universes u
    variables {α : Type u} {l l₁ l₂: list α} {a : α}

    @[simp] lemma rev_nil : @list.reverse α [] = [] := begin unfold reverse, unfold reverse_core end
    @[simp] lemma rev_core_nil : reverse_core [] l = l := rfl
    @[simp] lemma rev_core_cons : reverse_core (a::l₁) l = reverse_core l₁ (a::l) := rfl

    @[simp] lemma rev_core_length : (list.length $ list.reverse_core l₁ l₂) = (list.length l₁) + (list.length l₂) :=
        @well_founded.recursion
            (list α)
            _
            (measure_wf (length))
            (λ l₁, ∀ l₂, (list.length $ list.reverse_core (l₁) l₂) = (list.length (l₁)) + (list.length l₂))
            (l₁)
            (λ l₃,
                list.rec_on l₃ (by simp) (λ h l₃ i p l₂,
                begin
                    simp,
                    have p₂ : length (reverse_core l₃ (h::l₂)) = length l₃ + length (h::l₂),
                    apply p, focus {simp [measure, inv_image], rw add_comm, apply nat.lt_succ_self },
                    rw p₂, simp
                end
                )
            )
            l₂

    @[simp] lemma l_rev {t : list α} : list.length (list.reverse t) = list.length t := by simp [reverse]

end list


import interior_world.level3 --hide

/-

# Level 4: The interior is ...

-/
variables {X : Type} -- hide
variables [topological_space X] (x : X)  (A : set X) -- hide

namespace topological_space -- hide

/- Lemma
The interior of a set ...
-/
lemma interior_maximal (B : set X) (h : B ⊆ A) (hopen : is_open B):
  B ⊆ interior A :=
begin
  intros x x_in_B,
  rw interior_def',
  use B,
  exact ⟨⟨hopen, h⟩, x_in_B⟩,

end

end topological_space -- hide


/-
Copyright (c) 2020 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin, Scott Morrison, Adam Topaz
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.topology.sheaves.sheaf_of_functions
import Mathlib.topology.sheaves.stalks
import Mathlib.PostPort

universes v l

namespace Mathlib

/-!
# Functions satisfying a local predicate form a sheaf.

At this stage, in `topology/sheaves/sheaf_of_functions.lean`
we've proved that not-necessarily-continuous functions from a topological space
into some type (or type family) form a sheaf.

Why do the continuous functions form a sheaf?
The point is just that continuity is a local condition,
so one can use the lifting condition for functions to provide a candidate lift,
then verify that the lift is actually continuous by using the factorisation condition for the lift
(which guarantees that on each open set it agrees with the functions being lifted,
which were assumed to be continuous).

This file abstracts this argument to work for
any collection of dependent functions on a topological space
satisfying a "local predicate".

As an application, we check that continuity is a local predicate in this sense, and provide
* `Top.sheaf_condition.to_Top`: continuous functions into a topological space form a sheaf

A sheaf constructed in this way has a natural map `stalk_to_fiber` from the stalks
to the types in the ambient type family.

We give conditions sufficient to show that this map is injective and/or surjective.
-/

namespace Top

/--
Given a topological space `X : Top` and a type family `T : X → Type`,
a `P : prelocal_predicate T` consists of:
* a family of predicates `P.pred`, one for each `U : opens X`, of the form `(Π x : U, T x) → Prop`
* a proof that if `f : Π x : V, T x` satisfies the predicate on `V : opens X`, then
  the restriction of `f` to any open subset `U` also satisfies the predicate.
-/
structure prelocal_predicate {X : Top} (T : ↥X → Type v) where
  pred : {U : topological_space.opens ↥X} → ((x : ↥U) → T ↑x) → Prop
  res :
    ∀ {U V : topological_space.opens ↥X} (i : U ⟶ V) (f : (x : ↥V) → T ↑x),
      pred f → pred fun (x : ↥U) => f (coe_fn i x)

/--
Continuity is a "prelocal" predicate on functions to a fixed topological space `T`.
-/
@[simp] theorem continuous_prelocal_pred (X : Top) (T : Top) (U : topological_space.opens ↥X)
    (f : ↥U → ↥T) : prelocal_predicate.pred (continuous_prelocal X T) f = continuous f :=
  Eq.refl (prelocal_predicate.pred (continuous_prelocal X T) f)

/-- Satisfying the inhabited linter. -/
protected instance inhabited_prelocal_predicate (X : Top) (T : Top) :
    Inhabited (prelocal_predicate fun (x : ↥X) => ↥T) :=
  { default := continuous_prelocal X T }

/--
Given a topological space `X : Top` and a type family `T : X → Type`,
a `P : local_predicate T` consists of:
* a family of predicates `P.pred`, one for each `U : opens X`, of the form `(Π x : U, T x) → Prop`
* a proof that if `f : Π x : V, T x` satisfies the predicate on `V : opens X`, then
  the restriction of `f` to any open subset `U` also satisfies the predicate, and
* a proof that given some `f : Π x : U, T x`,
  if for every `x : U` we can find an open set `x ∈ V ≤ U`
  so that the restriction of `f` to `V` satisfies the predicate,
  then `f` itself satisfies the predicate.
-/
structure local_predicate {X : Top} (T : ↥X → Type v) extends prelocal_predicate T where
  locality :
    ∀ {U : topological_space.opens ↥X} (f : (x : ↥U) → T ↑x),
      (∀ (x : ↥U),
          ∃ (V : topological_space.opens ↥X),
            ∃ (m : subtype.val x ∈ V),
              ∃ (i : V ⟶ U),
                prelocal_predicate.pred _to_prelocal_predicate fun (x : ↥V) => f (coe_fn i x)) →
        prelocal_predicate.pred _to_prelocal_predicate f

/--
Continuity is a "local" predicate on functions to a fixed topological space `T`.
-/
def continuous_local (X : Top) (T : Top) : local_predicate fun (x : ↥X) => ↥T :=
  local_predicate.mk
    (prelocal_predicate.mk (prelocal_predicate.pred (continuous_prelocal X T)) sorry) sorry

/-- Satisfying the inhabited linter. -/
protected instance inhabited_local_predicate (X : Top) (T : Top) :
    Inhabited (local_predicate fun (x : ↥X) => ↥T) :=
  { default := continuous_local X T }

/--
Given a `P : prelocal_predicate`, we can always construct a `local_predicate`
by asking that the condition from `P` holds locally near every point.
-/
def prelocal_predicate.sheafify {X : Top} {T : ↥X → Type v} (P : prelocal_predicate T) :
    local_predicate T :=
  local_predicate.mk
    (prelocal_predicate.mk
      (fun (U : topological_space.opens ↥X) (f : (x : ↥U) → T ↑x) =>
        ∀ (x : ↥U),
          ∃ (V : topological_space.opens ↥X),
            ∃ (m : subtype.val x ∈ V),
              ∃ (i : V ⟶ U), prelocal_predicate.pred P fun (x : ↥V) => f (coe_fn i x))
      sorry)
    sorry

theorem prelocal_predicate.sheafify_of {X : Top} {T : ↥X → Type v} {P : prelocal_predicate T}
    {U : topological_space.opens ↥X} {f : (x : ↥U) → T ↑x} (h : prelocal_predicate.pred P f) :
    prelocal_predicate.pred (local_predicate.to_prelocal_predicate (prelocal_predicate.sheafify P))
        f :=
  sorry

/--
The subpresheaf of dependent functions on `X` satisfying the "pre-local" predicate `P`.
-/
def subpresheaf_to_Types {X : Top} {T : ↥X → Type v} (P : prelocal_predicate T) :
    presheaf (Type v) X :=
  category_theory.functor.mk
    (fun (U : topological_space.opens ↥Xᵒᵖ) =>
      Subtype fun (f : (x : ↥(opposite.unop U)) → T ↑x) => prelocal_predicate.pred P f)
    fun (U V : topological_space.opens ↥Xᵒᵖ) (i : U ⟶ V)
      (f : Subtype fun (f : (x : ↥(opposite.unop U)) → T ↑x) => prelocal_predicate.pred P f) =>
      { val :=
          fun (x : ↥(opposite.unop V)) =>
            subtype.val f (coe_fn (category_theory.has_hom.hom.unop i) x),
        property := sorry }

namespace subpresheaf_to_Types

/--
The natural transformation including the subpresheaf of functions satisfying a local predicate
into the presheaf of all functions.
-/
def subtype {X : Top} {T : ↥X → Type v} (P : prelocal_predicate T) :
    subpresheaf_to_Types P ⟶ presheaf_to_Types X T :=
  category_theory.nat_trans.mk
    fun (U : topological_space.opens ↥Xᵒᵖ)
      (f : category_theory.functor.obj (subpresheaf_to_Types P) U) => subtype.val f

/--
The natural transformation
from the sheaf condition diagram for functions satisfying a local predicate
to the sheaf condition diagram for arbitrary functions,
given by forgetting that the local predicate holds.
-/
def diagram_subtype {X : Top} {T : ↥X → Type v} (P : prelocal_predicate T) {ι : Type v}
    (U : ι → topological_space.opens ↥X) :
    presheaf.sheaf_condition_equalizer_products.diagram (subpresheaf_to_Types P) U ⟶
        presheaf.sheaf_condition_equalizer_products.diagram (presheaf_to_Types X T) U :=
  category_theory.nat_trans.mk
    fun (j : category_theory.limits.walking_parallel_pair) =>
      category_theory.limits.walking_parallel_pair.rec_on j
        (category_theory.limits.pi.map
          fun (i : ι)
            (f : category_theory.functor.obj (subpresheaf_to_Types P) (opposite.op (U i))) =>
            subtype.val f)
        (category_theory.limits.pi.map
          fun (p : ι × ι)
            (f :
            category_theory.functor.obj (subpresheaf_to_Types P)
              (opposite.op (U (prod.fst p) ⊓ U (prod.snd p)))) =>
            subtype.val f)

-- auxilliary lemma for `sheaf_condition` below

theorem sheaf_condition_fac {X : Top} {T : ↥X → Type v} {P : local_predicate T} {ι : Type v}
    {U : ι → topological_space.opens ↥X}
    {s :
      category_theory.limits.fork
        (presheaf.sheaf_condition_equalizer_products.left_res
          (subpresheaf_to_Types (local_predicate.to_prelocal_predicate P)) U)
        (presheaf.sheaf_condition_equalizer_products.right_res
          (subpresheaf_to_Types (local_predicate.to_prelocal_predicate P)) U)}
    (i : ι) (f : category_theory.limits.cone.X s)
    (h :
      prelocal_predicate.pred (local_predicate.to_prelocal_predicate P)
        (category_theory.limits.is_limit.lift (presheaf.to_Types X T U)
          (category_theory.functor.obj
            (category_theory.limits.cones.postcompose
              (diagram_subtype (local_predicate.to_prelocal_predicate P) U))
            s)
          f)) :
    category_theory.limits.limit.π
          (category_theory.discrete.functor
            fun (i : ι) =>
              Subtype
                fun (f : (x : ↥(opposite.unop (opposite.op (U i)))) → T ↑x) =>
                  prelocal_predicate.pred (local_predicate.to_prelocal_predicate P) f)
          i
          (presheaf.sheaf_condition_equalizer_products.res
            (subpresheaf_to_Types (local_predicate.to_prelocal_predicate P)) U
            { val :=
                category_theory.limits.is_limit.lift (presheaf.to_Types X T U)
                  (category_theory.functor.obj
                    (category_theory.limits.cones.postcompose
                      (diagram_subtype (local_predicate.to_prelocal_predicate P) U))
                    s)
                  f,
              property := h }) =
        category_theory.limits.limit.π
          (category_theory.discrete.functor
            fun (i : ι) =>
              Subtype
                fun (f : (x : ↥(opposite.unop (opposite.op (U i)))) → T ↑x) =>
                  prelocal_predicate.pred (local_predicate.to_prelocal_predicate P) f)
          i (category_theory.limits.fork.ι s f) :=
  sorry

-- auxilliary lemma for `sheaf_condition` below

theorem sheaf_condition_uniq {X : Top} {T : ↥X → Type v} {P : local_predicate T} {ι : Type v}
    {U : ι → topological_space.opens ↥X}
    {s :
      category_theory.limits.fork
        (presheaf.sheaf_condition_equalizer_products.left_res
          (subpresheaf_to_Types (local_predicate.to_prelocal_predicate P)) U)
        (presheaf.sheaf_condition_equalizer_products.right_res
          (subpresheaf_to_Types (local_predicate.to_prelocal_predicate P)) U)}
    (m :
      category_theory.limits.cone.X s ⟶
        category_theory.limits.cone.X
          (presheaf.sheaf_condition_equalizer_products.fork
            (subpresheaf_to_Types (local_predicate.to_prelocal_predicate P)) U))
    (w :
      ∀ (j : category_theory.limits.walking_parallel_pair),
        m ≫
            category_theory.nat_trans.app
              (category_theory.limits.cone.π
                (presheaf.sheaf_condition_equalizer_products.fork
                  (subpresheaf_to_Types (local_predicate.to_prelocal_predicate P)) U))
              j =
          category_theory.nat_trans.app (category_theory.limits.cone.π s) j)
    (f : category_theory.limits.cone.X s)
    (h :
      prelocal_predicate.pred (local_predicate.to_prelocal_predicate P)
        (category_theory.limits.is_limit.lift (presheaf.to_Types X T U)
          (category_theory.functor.obj
            (category_theory.limits.cones.postcompose
              (diagram_subtype (local_predicate.to_prelocal_predicate P) U))
            s)
          f)) :
    m f =
        { val :=
            category_theory.limits.is_limit.lift (presheaf.to_Types X T U)
              (category_theory.functor.obj
                (category_theory.limits.cones.postcompose
                  (diagram_subtype (local_predicate.to_prelocal_predicate P) U))
                s)
              f,
          property := h } :=
  sorry

/--
The functions satisfying a local predicate satisfy the sheaf condition.
-/
def sheaf_condition {X : Top} {T : ↥X → Type v} (P : local_predicate T) :
    presheaf.sheaf_condition (subpresheaf_to_Types (local_predicate.to_prelocal_predicate P)) :=
  sorry

end subpresheaf_to_Types

/--
The subsheaf of the sheaf of all dependently typed functions satisfying the local predicate `P`.
-/
def subsheaf_to_Types {X : Top} {T : ↥X → Type v} (P : local_predicate T) : sheaf (Type v) X :=
  sheaf.mk (subpresheaf_to_Types (local_predicate.to_prelocal_predicate P))
    (subpresheaf_to_Types.sheaf_condition P)

/--
There is a canonical map from the stalk to the original fiber, given by evaluating sections.
-/
def stalk_to_fiber {X : Top} {T : ↥X → Type v} (P : local_predicate T) (x : ↥X) :
    presheaf.stalk (sheaf.presheaf (subsheaf_to_Types P)) x ⟶ T x :=
  category_theory.limits.colimit.desc
    (category_theory.functor.obj
      (category_theory.functor.obj
        (category_theory.whiskering_left (topological_space.open_nhds xᵒᵖ)
          (topological_space.opens ↥Xᵒᵖ) (Type v))
        (category_theory.functor.op (topological_space.open_nhds.inclusion x)))
      (sheaf.presheaf (subsheaf_to_Types P)))
    (category_theory.limits.cocone.mk (T x)
      (category_theory.nat_trans.mk
        fun (U : topological_space.open_nhds xᵒᵖ)
          (f :
          category_theory.functor.obj
            (category_theory.functor.obj
              (category_theory.functor.obj
                (category_theory.whiskering_left (topological_space.open_nhds xᵒᵖ)
                  (topological_space.opens ↥Xᵒᵖ) (Type v))
                (category_theory.functor.op (topological_space.open_nhds.inclusion x)))
              (sheaf.presheaf (subsheaf_to_Types P)))
            U) =>
          subtype.val f { val := x, property := sorry }))

@[simp] theorem stalk_to_fiber_germ {X : Top} {T : ↥X → Type v} (P : local_predicate T)
    (U : topological_space.opens ↥X) (x : ↥U)
    (f : category_theory.functor.obj (sheaf.presheaf (subsheaf_to_Types P)) (opposite.op U)) :
    stalk_to_fiber P (↑x) (presheaf.germ (sheaf.presheaf (subsheaf_to_Types P)) x f) =
        subtype.val f x :=
  sorry

/--
The `stalk_to_fiber` map is surjective at `x` if
every point in the fiber `T x` has an allowed section passing through it.
-/
theorem stalk_to_fiber_surjective {X : Top} {T : ↥X → Type v} (P : local_predicate T) (x : ↥X)
    (w :
      ∀ (t : T x),
        ∃ (U : topological_space.open_nhds x),
          ∃ (f : (y : ↥(subtype.val U)) → T ↑y),
            ∃ (h : prelocal_predicate.pred (local_predicate.to_prelocal_predicate P) f),
              f { val := x, property := subtype.property U } = t) :
    function.surjective (stalk_to_fiber P x) :=
  sorry

/--
The `stalk_to_fiber` map is injective at `x` if any two allowed sections which agree at `x`
agree on some neighborhood of `x`.
-/
theorem stalk_to_fiber_injective {X : Top} {T : ↥X → Type v} (P : local_predicate T) (x : ↥X)
    (w :
      ∀ (U V : topological_space.open_nhds x) (fU : (y : ↥(subtype.val U)) → T ↑y),
        prelocal_predicate.pred (local_predicate.to_prelocal_predicate P) fU →
          ∀ (fV : (y : ↥(subtype.val V)) → T ↑y),
            prelocal_predicate.pred (local_predicate.to_prelocal_predicate P) fV →
              fU { val := x, property := subtype.property U } =
                  fV { val := x, property := subtype.property V } →
                ∃ (W : topological_space.open_nhds x),
                  ∃ (iU : W ⟶ U),
                    ∃ (iV : W ⟶ V), ∀ (w : ↥(subtype.val W)), fU (coe_fn iU w) = fV (coe_fn iV w)) :
    function.injective (stalk_to_fiber P x) :=
  sorry

/--
Some repackaging:
the presheaf of functions satisfying `continuous_prelocal` is just the same thing as
the presheaf of continuous functions.
-/
def subpresheaf_continuous_prelocal_iso_presheaf_to_Top {X : Top} (T : Top) :
    subpresheaf_to_Types (continuous_prelocal X T) ≅ presheaf_to_Top X T :=
  sorry

/--
The sheaf of continuous functions on `X` with values in a space `T`.
-/
def sheaf_to_Top {X : Top} (T : Top) : sheaf (Type v) X :=
  sheaf.mk (presheaf_to_Top X T)
    (coe_fn
      (presheaf.sheaf_condition_equiv_of_iso
        (subpresheaf_continuous_prelocal_iso_presheaf_to_Top T))
      (subpresheaf_to_Types.sheaf_condition (continuous_local X T)))

end Mathlib


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

! This file was ported from Lean 3 source module analysis.inner_product_space.two_dim
! leanprover-community/mathlib commit 46b633fd842bef9469441c0209906f6dddd2b4f5
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Analysis.InnerProductSpace.Dual
import Mathbin.Analysis.InnerProductSpace.Orientation
import Mathbin.Tactic.LinearCombination

/-!
# Oriented two-dimensional real inner product spaces

This file defines constructions specific to the geometry of an oriented two-dimensional real inner
product space `E`.

## Main declarations

* `orientation.area_form`: an antisymmetric bilinear form `E →ₗ[ℝ] E →ₗ[ℝ] ℝ` (usual notation `ω`).
  Morally, when `ω` is evaluated on two vectors, it gives the oriented area of the parallelogram
  they span. (But mathlib does not yet have a construction of oriented area, and in fact the
  construction of oriented area should pass through `ω`.)

* `orientation.right_angle_rotation`: an isometric automorphism `E ≃ₗᵢ[ℝ] E` (usual notation `J`).
  This automorphism squares to -1.  In a later file, rotations (`orientation.rotation`) are defined,
  in such a way that this automorphism is equal to rotation by 90 degrees.

* `orientation.basis_right_angle_rotation`: for a nonzero vector `x` in `E`, the basis `![x, J x]`
  for `E`.

* `orientation.kahler`: a complex-valued real-bilinear map `E →ₗ[ℝ] E →ₗ[ℝ] ℂ`. Its real part is the
  inner product and its imaginary part is `orientation.area_form`.  For vectors `x` and `y` in `E`,
  the complex number `o.kahler x y` has modulus `‖x‖ * ‖y‖`. In a later file, oriented angles
  (`orientation.oangle`) are defined, in such a way that the argument of `o.kahler x y` is the
  oriented angle from `x` to `y`.

## Main results

* `orientation.right_angle_rotation_right_angle_rotation`: the identity `J (J x) = - x`

* `orientation.nonneg_inner_and_area_form_eq_zero_iff_same_ray`: `x`, `y` are in the same ray, if
  and only if `0 ≤ ⟪x, y⟫` and `ω x y = 0`

* `orientation.kahler_mul`: the identity `o.kahler x a * o.kahler a y = ‖a‖ ^ 2 * o.kahler x y`

* `complex.area_form`, `complex.right_angle_rotation`, `complex.kahler`: the concrete
  interpretations of `area_form`, `right_angle_rotation`, `kahler` for the oriented real inner
  product space `ℂ`

* `orientation.area_form_map_complex`, `orientation.right_angle_rotation_map_complex`,
  `orientation.kahler_map_complex`: given an orientation-preserving isometry from `E` to `ℂ`,
  expressions for `area_form`, `right_angle_rotation`, `kahler` as the pullback of their concrete
  interpretations on `ℂ`

## Implementation notes

Notation `ω` for `orientation.area_form` and `J` for `orientation.right_angle_rotation` should be
defined locally in each file which uses them, since otherwise one would need a more cumbersome
notation which mentions the orientation explicitly (something like `ω[o]`).  Write

```
local notation `ω` := o.area_form
local notation `J` := o.right_angle_rotation
```

-/

noncomputable section

open RealInnerProductSpace ComplexConjugate

open FiniteDimensional

attribute [local instance] fact_finite_dimensional_of_finrank_eq_succ

variable {E : Type _} [NormedAddCommGroup E] [InnerProductSpace ℝ E] [Fact (finrank ℝ E = 2)]
  (o : Orientation ℝ E (Fin 2))

namespace Orientation

include o

/-- An antisymmetric bilinear form on an oriented real inner product space of dimension 2 (usual
notation `ω`).  When evaluated on two vectors, it gives the oriented area of the parallelogram they
span. -/
irreducible_def areaForm : E →ₗ[ℝ] E →ₗ[ℝ] ℝ :=
  by
  let z : AlternatingMap ℝ E ℝ (Fin 0) ≃ₗ[ℝ] ℝ :=
    alternating_map.const_linear_equiv_of_is_empty.symm
  let y : AlternatingMap ℝ E ℝ (Fin 1) →ₗ[ℝ] E →ₗ[ℝ] ℝ :=
    LinearMap.llcomp ℝ E (AlternatingMap ℝ E ℝ (Fin 0)) ℝ z ∘ₗ AlternatingMap.curryLeftLinearMap
  exact y ∘ₗ AlternatingMap.curryLeftLinearMap o.volume_form
#align orientation.area_form Orientation.areaForm

omit o

-- mathport name: exprω
local notation "ω" => o.areaForm

theorem areaForm_to_volumeForm (x y : E) : ω x y = o.volumeForm ![x, y] := by simp [area_form]
#align orientation.area_form_to_volume_form Orientation.areaForm_to_volumeForm

@[simp]
theorem areaForm_apply_self (x : E) : ω x x = 0 :=
  by
  rw [area_form_to_volume_form]
  refine' o.volume_form.map_eq_zero_of_eq ![x, x] _ (_ : (0 : Fin 2) ≠ 1)
  · simp
  · norm_num
#align orientation.area_form_apply_self Orientation.areaForm_apply_self

theorem areaForm_swap (x y : E) : ω x y = -ω y x :=
  by
  simp only [area_form_to_volume_form]
  convert o.volume_form.map_swap ![y, x] (_ : (0 : Fin 2) ≠ 1)
  · ext i
    fin_cases i <;> rfl
  · norm_num
#align orientation.area_form_swap Orientation.areaForm_swap

@[simp]
theorem areaForm_neg_orientation : (-o).areaForm = -o.areaForm :=
  by
  ext (x y)
  simp [area_form_to_volume_form]
#align orientation.area_form_neg_orientation Orientation.areaForm_neg_orientation

/-- Continuous linear map version of `orientation.area_form`, useful for calculus. -/
def areaForm' : E →L[ℝ] E →L[ℝ] ℝ :=
  (↑(LinearMap.toContinuousLinearMap : (E →ₗ[ℝ] ℝ) ≃ₗ[ℝ] E →L[ℝ] ℝ) ∘ₗ
      o.areaForm).toContinuousLinearMap
#align orientation.area_form' Orientation.areaForm'

@[simp]
theorem areaForm'_apply (x : E) : o.areaForm' x = (o.areaForm x).toContinuousLinearMap :=
  rfl
#align orientation.area_form'_apply Orientation.areaForm'_apply

theorem abs_areaForm_le (x y : E) : |ω x y| ≤ ‖x‖ * ‖y‖ := by
  simpa [area_form_to_volume_form, Fin.prod_univ_succ] using o.abs_volume_form_apply_le ![x, y]
#align orientation.abs_area_form_le Orientation.abs_areaForm_le

theorem areaForm_le (x y : E) : ω x y ≤ ‖x‖ * ‖y‖ := by
  simpa [area_form_to_volume_form, Fin.prod_univ_succ] using o.volume_form_apply_le ![x, y]
#align orientation.area_form_le Orientation.areaForm_le

theorem abs_areaForm_of_orthogonal {x y : E} (h : ⟪x, y⟫ = 0) : |ω x y| = ‖x‖ * ‖y‖ :=
  by
  rw [o.area_form_to_volume_form, o.abs_volume_form_apply_of_pairwise_orthogonal]
  · simp [Fin.prod_univ_succ]
  intro i j hij
  fin_cases i <;> fin_cases j
  · simpa
  · simpa using h
  · simpa [real_inner_comm] using h
  · simpa
#align orientation.abs_area_form_of_orthogonal Orientation.abs_areaForm_of_orthogonal

theorem areaForm_map {F : Type _} [NormedAddCommGroup F] [InnerProductSpace ℝ F]
    [Fact (finrank ℝ F = 2)] (φ : E ≃ₗᵢ[ℝ] F) (x y : F) :
    (Orientation.map (Fin 2) φ.toLinearEquiv o).areaForm x y = o.areaForm (φ.symm x) (φ.symm y) :=
  by
  have : φ.symm ∘ ![x, y] = ![φ.symm x, φ.symm y] :=
    by
    ext i
    fin_cases i <;> rfl
  simp [area_form_to_volume_form, volume_form_map, this]
#align orientation.area_form_map Orientation.areaForm_map

/-- The area form is invariant under pullback by a positively-oriented isometric automorphism. -/
theorem areaForm_comp_linearIsometryEquiv (φ : E ≃ₗᵢ[ℝ] E)
    (hφ : 0 < (φ.toLinearEquiv : E →ₗ[ℝ] E).det) (x y : E) :
    o.areaForm (φ x) (φ y) = o.areaForm x y :=
  by
  convert o.area_form_map φ (φ x) (φ y)
  · symm
    rwa [← o.map_eq_iff_det_pos φ.to_linear_equiv] at hφ
    rw [Fact.out (finrank ℝ E = 2), Fintype.card_fin]
  · simp
  · simp
#align orientation.area_form_comp_linear_isometry_equiv Orientation.areaForm_comp_linearIsometryEquiv

/-- Auxiliary construction for `orientation.right_angle_rotation`, rotation by 90 degrees in an
oriented real inner product space of dimension 2. -/
irreducible_def rightAngleRotationAux₁ : E →ₗ[ℝ] E :=
  let to_dual : E ≃ₗ[ℝ] E →ₗ[ℝ] ℝ :=
    (InnerProductSpace.toDual ℝ E).toLinearEquiv ≪≫ₗ LinearMap.toContinuousLinearMap.symm
  ↑to_dual.symm ∘ₗ ω
#align orientation.right_angle_rotation_aux₁ Orientation.rightAngleRotationAux₁

@[simp]
theorem inner_rightAngleRotationAux₁_left (x y : E) : ⟪o.rightAngleRotationAux₁ x, y⟫ = ω x y := by
  simp only [right_angle_rotation_aux₁, LinearEquiv.trans_symm, LinearEquiv.coe_trans,
    LinearEquiv.coe_coe, InnerProductSpace.toDual_symm_apply, eq_self_iff_true,
    LinearMap.coe_to_continuous_linear_map', LinearIsometryEquiv.coe_toLinearEquiv,
    LinearMap.comp_apply, LinearEquiv.symm_symm, LinearIsometryEquiv.toLinearEquiv_symm]
#align orientation.inner_right_angle_rotation_aux₁_left Orientation.inner_rightAngleRotationAux₁_left

@[simp]
theorem inner_rightAngleRotationAux₁_right (x y : E) : ⟪x, o.rightAngleRotationAux₁ y⟫ = -ω x y :=
  by
  rw [real_inner_comm]
  simp [o.area_form_swap y x]
#align orientation.inner_right_angle_rotation_aux₁_right Orientation.inner_rightAngleRotationAux₁_right

/-- Auxiliary construction for `orientation.right_angle_rotation`, rotation by 90 degrees in an
oriented real inner product space of dimension 2. -/
def rightAngleRotationAux₂ : E →ₗᵢ[ℝ] E :=
  { o.rightAngleRotationAux₁ with
    norm_map' := fun x => by
      dsimp
      refine' le_antisymm _ _
      · cases' eq_or_lt_of_le (norm_nonneg (o.right_angle_rotation_aux₁ x)) with h h
        · rw [← h]
          positivity
        refine' le_of_mul_le_mul_right _ h
        rw [← real_inner_self_eq_norm_mul_norm, o.inner_right_angle_rotation_aux₁_left]
        exact o.area_form_le x (o.right_angle_rotation_aux₁ x)
      · let K : Submodule ℝ E := ℝ ∙ x
        have : Nontrivial Kᗮ :=
          by
          apply @FiniteDimensional.nontrivial_of_finrank_pos ℝ
          have : finrank ℝ K ≤ Finset.card {x} :=
            by
            rw [← Set.toFinset_singleton]
            exact finrank_span_le_card ({x} : Set E)
          have : Finset.card {x} = 1 := Finset.card_singleton x
          have : finrank ℝ K + finrank ℝ Kᗮ = finrank ℝ E := K.finrank_add_finrank_orthogonal
          have : finrank ℝ E = 2 := Fact.out _
          linarith
        obtain ⟨w, hw₀⟩ : ∃ w : Kᗮ, w ≠ 0 := exists_ne 0
        have hw' : ⟪x, (w : E)⟫ = 0 := submodule.mem_orthogonal_singleton_iff_inner_right.mp w.2
        have hw : (w : E) ≠ 0 := fun h => hw₀ (submodule.coe_eq_zero.mp h)
        refine' le_of_mul_le_mul_right _ (by rwa [norm_pos_iff] : 0 < ‖(w : E)‖)
        rw [← o.abs_area_form_of_orthogonal hw']
        rw [← o.inner_right_angle_rotation_aux₁_left x w]
        exact abs_real_inner_le_norm (o.right_angle_rotation_aux₁ x) w }
#align orientation.right_angle_rotation_aux₂ Orientation.rightAngleRotationAux₂

@[simp]
theorem rightAngleRotationAux₁_rightAngleRotationAux₁ (x : E) :
    o.rightAngleRotationAux₁ (o.rightAngleRotationAux₁ x) = -x :=
  by
  apply ext_inner_left ℝ
  intro y
  have : ⟪o.right_angle_rotation_aux₁ y, o.right_angle_rotation_aux₁ x⟫ = ⟪y, x⟫ :=
    LinearIsometry.inner_map_map o.right_angle_rotation_aux₂ y x
  rw [o.inner_right_angle_rotation_aux₁_right, ← o.inner_right_angle_rotation_aux₁_left, this,
    inner_neg_right]
#align orientation.right_angle_rotation_aux₁_right_angle_rotation_aux₁ Orientation.rightAngleRotationAux₁_rightAngleRotationAux₁

/-- An isometric automorphism of an oriented real inner product space of dimension 2 (usual notation
`J`). This automorphism squares to -1.  We will define rotations in such a way that this
automorphism is equal to rotation by 90 degrees. -/
irreducible_def rightAngleRotation : E ≃ₗᵢ[ℝ] E :=
  LinearIsometryEquiv.ofLinearIsometry o.rightAngleRotationAux₂ (-o.rightAngleRotationAux₁)
    (by ext <;> simp [right_angle_rotation_aux₂]) (by ext <;> simp [right_angle_rotation_aux₂])
#align orientation.right_angle_rotation Orientation.rightAngleRotation

-- mathport name: exprJ
local notation "J" => o.rightAngleRotation

@[simp]
theorem inner_rightAngleRotation_left (x y : E) : ⟪J x, y⟫ = ω x y :=
  by
  rw [right_angle_rotation]
  exact o.inner_right_angle_rotation_aux₁_left x y
#align orientation.inner_right_angle_rotation_left Orientation.inner_rightAngleRotation_left

@[simp]
theorem inner_rightAngleRotation_right (x y : E) : ⟪x, J y⟫ = -ω x y :=
  by
  rw [right_angle_rotation]
  exact o.inner_right_angle_rotation_aux₁_right x y
#align orientation.inner_right_angle_rotation_right Orientation.inner_rightAngleRotation_right

@[simp]
theorem rightAngleRotation_rightAngleRotation (x : E) : J (J x) = -x :=
  by
  rw [right_angle_rotation]
  exact o.right_angle_rotation_aux₁_right_angle_rotation_aux₁ x
#align orientation.right_angle_rotation_right_angle_rotation Orientation.rightAngleRotation_rightAngleRotation

@[simp]
theorem rightAngleRotation_symm :
    LinearIsometryEquiv.symm J = LinearIsometryEquiv.trans J (LinearIsometryEquiv.neg ℝ) :=
  by
  rw [right_angle_rotation]
  exact LinearIsometryEquiv.toLinearIsometry_injective rfl
#align orientation.right_angle_rotation_symm Orientation.rightAngleRotation_symm

@[simp]
theorem inner_rightAngleRotation_self (x : E) : ⟪J x, x⟫ = 0 := by simp
#align orientation.inner_right_angle_rotation_self Orientation.inner_rightAngleRotation_self

theorem inner_rightAngleRotation_swap (x y : E) : ⟪x, J y⟫ = -⟪J x, y⟫ := by simp
#align orientation.inner_right_angle_rotation_swap Orientation.inner_rightAngleRotation_swap

theorem inner_rightAngleRotation_swap' (x y : E) : ⟪J x, y⟫ = -⟪x, J y⟫ := by
  simp [o.inner_right_angle_rotation_swap x y]
#align orientation.inner_right_angle_rotation_swap' Orientation.inner_rightAngleRotation_swap'

theorem inner_comp_rightAngleRotation (x y : E) : ⟪J x, J y⟫ = ⟪x, y⟫ :=
  LinearIsometryEquiv.inner_map_map J x y
#align orientation.inner_comp_right_angle_rotation Orientation.inner_comp_rightAngleRotation

@[simp]
theorem areaForm_rightAngleRotation_left (x y : E) : ω (J x) y = -⟪x, y⟫ := by
  rw [← o.inner_comp_right_angle_rotation, o.inner_right_angle_rotation_right, neg_neg]
#align orientation.area_form_right_angle_rotation_left Orientation.areaForm_rightAngleRotation_left

@[simp]
theorem areaForm_rightAngleRotation_right (x y : E) : ω x (J y) = ⟪x, y⟫ := by
  rw [← o.inner_right_angle_rotation_left, o.inner_comp_right_angle_rotation]
#align orientation.area_form_right_angle_rotation_right Orientation.areaForm_rightAngleRotation_right

@[simp]
theorem areaForm_comp_rightAngleRotation (x y : E) : ω (J x) (J y) = ω x y := by simp
#align orientation.area_form_comp_right_angle_rotation Orientation.areaForm_comp_rightAngleRotation

@[simp]
theorem rightAngleRotation_trans_rightAngleRotation :
    LinearIsometryEquiv.trans J J = LinearIsometryEquiv.neg ℝ := by ext <;> simp
#align orientation.right_angle_rotation_trans_right_angle_rotation Orientation.rightAngleRotation_trans_rightAngleRotation

theorem rightAngleRotation_neg_orientation (x : E) :
    (-o).rightAngleRotation x = -o.rightAngleRotation x :=
  by
  apply ext_inner_right ℝ
  intro y
  rw [inner_right_angle_rotation_left]
  simp
#align orientation.right_angle_rotation_neg_orientation Orientation.rightAngleRotation_neg_orientation

@[simp]
theorem rightAngleRotation_trans_neg_orientation :
    (-o).rightAngleRotation = o.rightAngleRotation.trans (LinearIsometryEquiv.neg ℝ) :=
  LinearIsometryEquiv.ext <| o.rightAngleRotation_neg_orientation
#align orientation.right_angle_rotation_trans_neg_orientation Orientation.rightAngleRotation_trans_neg_orientation

theorem rightAngleRotation_map {F : Type _} [NormedAddCommGroup F] [InnerProductSpace ℝ F]
    [Fact (finrank ℝ F = 2)] (φ : E ≃ₗᵢ[ℝ] F) (x : F) :
    (Orientation.map (Fin 2) φ.toLinearEquiv o).rightAngleRotation x =
      φ (o.rightAngleRotation (φ.symm x)) :=
  by
  apply ext_inner_right ℝ
  intro y
  rw [inner_right_angle_rotation_left]
  trans ⟪J (φ.symm x), φ.symm y⟫
  · simp [o.area_form_map]
  trans ⟪φ (J (φ.symm x)), φ (φ.symm y)⟫
  · rw [φ.inner_map_map]
  · simp
#align orientation.right_angle_rotation_map Orientation.rightAngleRotation_map

/-- `J` commutes with any positively-oriented isometric automorphism. -/
theorem linearIsometryEquiv_comp_rightAngleRotation (φ : E ≃ₗᵢ[ℝ] E)
    (hφ : 0 < (φ.toLinearEquiv : E →ₗ[ℝ] E).det) (x : E) : φ (J x) = J (φ x) :=
  by
  convert(o.right_angle_rotation_map φ (φ x)).symm
  · simp
  · symm
    rwa [← o.map_eq_iff_det_pos φ.to_linear_equiv] at hφ
    rw [Fact.out (finrank ℝ E = 2), Fintype.card_fin]
#align orientation.linear_isometry_equiv_comp_right_angle_rotation Orientation.linearIsometryEquiv_comp_rightAngleRotation

theorem rightAngleRotation_map' {F : Type _} [NormedAddCommGroup F] [InnerProductSpace ℝ F]
    [Fact (finrank ℝ F = 2)] (φ : E ≃ₗᵢ[ℝ] F) :
    (Orientation.map (Fin 2) φ.toLinearEquiv o).rightAngleRotation =
      (φ.symm.trans o.rightAngleRotation).trans φ :=
  LinearIsometryEquiv.ext <| o.rightAngleRotation_map φ
#align orientation.right_angle_rotation_map' Orientation.rightAngleRotation_map'

/-- `J` commutes with any positively-oriented isometric automorphism. -/
theorem linearIsometryEquiv_comp_right_angle_rotation' (φ : E ≃ₗᵢ[ℝ] E)
    (hφ : 0 < (φ.toLinearEquiv : E →ₗ[ℝ] E).det) : LinearIsometryEquiv.trans J φ = φ.trans J :=
  LinearIsometryEquiv.ext <| o.linearIsometryEquiv_comp_rightAngleRotation φ hφ
#align orientation.linear_isometry_equiv_comp_right_angle_rotation' Orientation.linearIsometryEquiv_comp_right_angle_rotation'

/-- For a nonzero vector `x` in an oriented two-dimensional real inner product space `E`,
`![x, J x]` forms an (orthogonal) basis for `E`. -/
def basisRightAngleRotation (x : E) (hx : x ≠ 0) : Basis (Fin 2) ℝ E :=
  @basisOfLinearIndependentOfCardEqFinrank ℝ _ _ _ _ _ _ _ ![x, J x]
    (linearIndependent_of_ne_zero_of_inner_eq_zero (fun i => by fin_cases i <;> simp [hx])
      (by
        intro i j hij
        fin_cases i <;> fin_cases j
        · simpa
        · simp
        · simp
        · simpa))
    (Fact.out (finrank ℝ E = 2)).symm
#align orientation.basis_right_angle_rotation Orientation.basisRightAngleRotation

@[simp]
theorem coe_basisRightAngleRotation (x : E) (hx : x ≠ 0) :
    ⇑(o.basisRightAngleRotation x hx) = ![x, J x] :=
  coe_basisOfLinearIndependentOfCardEqFinrank _ _
#align orientation.coe_basis_right_angle_rotation Orientation.coe_basisRightAngleRotation

/-- For vectors `a x y : E`, the identity `⟪a, x⟫ * ⟪a, y⟫ + ω a x * ω a y = ‖a‖ ^ 2 * ⟪x, y⟫`. (See
`orientation.inner_mul_inner_add_area_form_mul_area_form` for the "applied" form.)-/
theorem inner_mul_inner_add_areaForm_mul_area_form' (a x : E) :
    ⟪a, x⟫ • innerₛₗ ℝ a + ω a x • ω a = ‖a‖ ^ 2 • innerₛₗ ℝ x :=
  by
  by_cases ha : a = 0
  · simp [ha]
  apply (o.basis_right_angle_rotation a ha).ext
  intro i
  fin_cases i
  · simp only [real_inner_self_eq_norm_sq, Algebra.id.smul_eq_mul, innerₛₗ_apply,
      LinearMap.smul_apply, LinearMap.add_apply, Matrix.cons_val_zero,
      o.coe_basis_right_angle_rotation, o.area_form_apply_self, real_inner_comm]
    ring
  · simp only [real_inner_self_eq_norm_sq, Algebra.id.smul_eq_mul, innerₛₗ_apply,
      LinearMap.smul_apply, neg_inj, LinearMap.add_apply, Matrix.cons_val_one, Matrix.head_cons,
      o.coe_basis_right_angle_rotation, o.area_form_right_angle_rotation_right,
      o.area_form_apply_self, o.inner_right_angle_rotation_right]
    rw [o.area_form_swap]
    ring
#align orientation.inner_mul_inner_add_area_form_mul_area_form' Orientation.inner_mul_inner_add_areaForm_mul_area_form'

/-- For vectors `a x y : E`, the identity `⟪a, x⟫ * ⟪a, y⟫ + ω a x * ω a y = ‖a‖ ^ 2 * ⟪x, y⟫`. -/
theorem inner_mul_inner_add_areaForm_mul_areaForm (a x y : E) :
    ⟪a, x⟫ * ⟪a, y⟫ + ω a x * ω a y = ‖a‖ ^ 2 * ⟪x, y⟫ :=
  congr_arg (fun f : E →ₗ[ℝ] ℝ => f y) (o.inner_mul_inner_add_areaForm_mul_area_form' a x)
#align orientation.inner_mul_inner_add_area_form_mul_area_form Orientation.inner_mul_inner_add_areaForm_mul_areaForm

theorem inner_sq_add_areaForm_sq (a b : E) : ⟪a, b⟫ ^ 2 + ω a b ^ 2 = ‖a‖ ^ 2 * ‖b‖ ^ 2 := by
  simpa [sq, real_inner_self_eq_norm_sq] using o.inner_mul_inner_add_area_form_mul_area_form a b b
#align orientation.inner_sq_add_area_form_sq Orientation.inner_sq_add_areaForm_sq

/-- For vectors `a x y : E`, the identity `⟪a, x⟫ * ω a y - ω a x * ⟪a, y⟫ = ‖a‖ ^ 2 * ω x y`. (See
`orientation.inner_mul_area_form_sub` for the "applied" form.) -/
theorem inner_mul_areaForm_sub' (a x : E) : ⟪a, x⟫ • ω a - ω a x • innerₛₗ ℝ a = ‖a‖ ^ 2 • ω x :=
  by
  by_cases ha : a = 0
  · simp [ha]
  apply (o.basis_right_angle_rotation a ha).ext
  intro i
  fin_cases i
  · simp only [o.coe_basis_right_angle_rotation, o.area_form_apply_self, o.area_form_swap a x,
      real_inner_self_eq_norm_sq, Algebra.id.smul_eq_mul, innerₛₗ_apply, LinearMap.sub_apply,
      LinearMap.smul_apply, Matrix.cons_val_zero]
    ring
  · simp only [o.area_form_right_angle_rotation_right, o.area_form_apply_self,
      o.coe_basis_right_angle_rotation, o.inner_right_angle_rotation_right,
      real_inner_self_eq_norm_sq, real_inner_comm, Algebra.id.smul_eq_mul, innerₛₗ_apply,
      LinearMap.smul_apply, LinearMap.sub_apply, Matrix.cons_val_one, Matrix.head_cons]
    ring
#align orientation.inner_mul_area_form_sub' Orientation.inner_mul_areaForm_sub'

/-- For vectors `a x y : E`, the identity `⟪a, x⟫ * ω a y - ω a x * ⟪a, y⟫ = ‖a‖ ^ 2 * ω x y`. -/
theorem inner_mul_areaForm_sub (a x y : E) : ⟪a, x⟫ * ω a y - ω a x * ⟪a, y⟫ = ‖a‖ ^ 2 * ω x y :=
  congr_arg (fun f : E →ₗ[ℝ] ℝ => f y) (o.inner_mul_areaForm_sub' a x)
#align orientation.inner_mul_area_form_sub Orientation.inner_mul_areaForm_sub

theorem nonneg_inner_and_areaForm_eq_zero_iff_sameRay (x y : E) :
    0 ≤ ⟪x, y⟫ ∧ ω x y = 0 ↔ SameRay ℝ x y :=
  by
  by_cases hx : x = 0
  · simp [hx]
  constructor
  · let a : ℝ := (o.basis_right_angle_rotation x hx).repr y 0
    let b : ℝ := (o.basis_right_angle_rotation x hx).repr y 1
    suffices 0 ≤ a * ‖x‖ ^ 2 ∧ b * ‖x‖ ^ 2 = 0 → SameRay ℝ x (a • x + b • J x)
      by
      rw [← (o.basis_right_angle_rotation x hx).sum_repr y]
      simp only [Fin.sum_univ_succ, coe_basis_right_angle_rotation, Matrix.cons_val_zero,
        Fin.succ_zero_eq_one', Fintype.univ_of_isEmpty, Finset.sum_empty, o.area_form_apply_self,
        map_smul, map_add, map_zero, inner_smul_left, inner_smul_right, inner_add_left,
        inner_add_right, inner_zero_right, LinearMap.add_apply, Matrix.cons_val_one,
        Matrix.head_cons, Algebra.id.smul_eq_mul, o.area_form_right_angle_rotation_right,
        MulZeroClass.mul_zero, add_zero, zero_add, neg_zero, o.inner_right_angle_rotation_right,
        o.area_form_apply_self, real_inner_self_eq_norm_sq]
      exact this
    rintro ⟨ha, hb⟩
    have hx' : 0 < ‖x‖ := by simpa using hx
    have ha' : 0 ≤ a := nonneg_of_mul_nonneg_left ha (by positivity)
    have hb' : b = 0 := eq_zero_of_ne_zero_of_mul_right_eq_zero (pow_ne_zero 2 hx'.ne') hb
    simpa [hb'] using SameRay.sameRay_nonneg_smul_right x ha'
  · intro h
    obtain ⟨r, hr, rfl⟩ := h.exists_nonneg_left hx
    simp only [inner_smul_right, real_inner_self_eq_norm_sq, LinearMap.map_smulₛₗ,
      area_form_apply_self, Algebra.id.smul_eq_mul, MulZeroClass.mul_zero, eq_self_iff_true,
      and_true_iff]
    positivity
#align orientation.nonneg_inner_and_area_form_eq_zero_iff_same_ray Orientation.nonneg_inner_and_areaForm_eq_zero_iff_sameRay

/-- A complex-valued real-bilinear map on an oriented real inner product space of dimension 2. Its
real part is the inner product and its imaginary part is `orientation.area_form`.

On `ℂ` with the standard orientation, `kahler w z = conj w * z`; see `complex.kahler`. -/
def kahler : E →ₗ[ℝ] E →ₗ[ℝ] ℂ :=
  LinearMap.llcomp ℝ E ℝ ℂ Complex.ofRealClm ∘ₗ innerₛₗ ℝ +
    LinearMap.llcomp ℝ E ℝ ℂ ((LinearMap.lsmul ℝ ℂ).flip Complex.I) ∘ₗ ω
#align orientation.kahler Orientation.kahler

theorem kahler_apply_apply (x y : E) : o.kahler x y = ⟪x, y⟫ + ω x y • Complex.I :=
  rfl
#align orientation.kahler_apply_apply Orientation.kahler_apply_apply

theorem kahler_swap (x y : E) : o.kahler x y = conj (o.kahler y x) :=
  by
  simp only [kahler_apply_apply]
  rw [real_inner_comm, area_form_swap]
  simp
#align orientation.kahler_swap Orientation.kahler_swap

@[simp]
theorem kahler_apply_self (x : E) : o.kahler x x = ‖x‖ ^ 2 := by
  simp [kahler_apply_apply, real_inner_self_eq_norm_sq]
#align orientation.kahler_apply_self Orientation.kahler_apply_self

@[simp]
theorem kahler_rightAngleRotation_left (x y : E) : o.kahler (J x) y = -Complex.I * o.kahler x y :=
  by
  simp only [o.area_form_right_angle_rotation_left, o.inner_right_angle_rotation_left,
    o.kahler_apply_apply, Complex.ofReal_neg, Complex.real_smul]
  linear_combination ω x y * Complex.I_sq
#align orientation.kahler_right_angle_rotation_left Orientation.kahler_rightAngleRotation_left

@[simp]
theorem kahler_rightAngleRotation_right (x y : E) : o.kahler x (J y) = Complex.I * o.kahler x y :=
  by
  simp only [o.area_form_right_angle_rotation_right, o.inner_right_angle_rotation_right,
    o.kahler_apply_apply, Complex.ofReal_neg, Complex.real_smul]
  linear_combination -ω x y * Complex.I_sq
#align orientation.kahler_right_angle_rotation_right Orientation.kahler_rightAngleRotation_right

@[simp]
theorem kahler_comp_rightAngleRotation (x y : E) : o.kahler (J x) (J y) = o.kahler x y :=
  by
  simp only [kahler_right_angle_rotation_left, kahler_right_angle_rotation_right]
  linear_combination -o.kahler x y * Complex.I_sq
#align orientation.kahler_comp_right_angle_rotation Orientation.kahler_comp_rightAngleRotation

@[simp]
theorem kahler_neg_orientation (x y : E) : (-o).kahler x y = conj (o.kahler x y) := by
  simp [kahler_apply_apply]
#align orientation.kahler_neg_orientation Orientation.kahler_neg_orientation

theorem kahler_mul (a x y : E) : o.kahler x a * o.kahler a y = ‖a‖ ^ 2 * o.kahler x y :=
  by
  trans (↑(‖a‖ ^ 2) : ℂ) * o.kahler x y
  · ext
    · simp only [o.kahler_apply_apply, Complex.add_im, Complex.add_re, Complex.I_im, Complex.I_re,
        Complex.mul_im, Complex.mul_re, Complex.ofReal_im, Complex.ofReal_re, Complex.real_smul]
      rw [real_inner_comm a x, o.area_form_swap x a]
      linear_combination o.inner_mul_inner_add_area_form_mul_area_form a x y
    · simp only [o.kahler_apply_apply, Complex.add_im, Complex.add_re, Complex.I_im, Complex.I_re,
        Complex.mul_im, Complex.mul_re, Complex.ofReal_im, Complex.ofReal_re, Complex.real_smul]
      rw [real_inner_comm a x, o.area_form_swap x a]
      linear_combination o.inner_mul_area_form_sub a x y
  · norm_cast
#align orientation.kahler_mul Orientation.kahler_mul

theorem normSq_kahler (x y : E) : Complex.normSq (o.kahler x y) = ‖x‖ ^ 2 * ‖y‖ ^ 2 := by
  simpa [kahler_apply_apply, Complex.normSq, sq] using o.inner_sq_add_area_form_sq x y
#align orientation.norm_sq_kahler Orientation.normSq_kahler

theorem abs_kahler (x y : E) : Complex.abs (o.kahler x y) = ‖x‖ * ‖y‖ :=
  by
  rw [← sq_eq_sq, Complex.sq_abs]
  · linear_combination o.norm_sq_kahler x y
  · positivity
  · positivity
#align orientation.abs_kahler Orientation.abs_kahler

theorem norm_kahler (x y : E) : ‖o.kahler x y‖ = ‖x‖ * ‖y‖ := by simpa using o.abs_kahler x y
#align orientation.norm_kahler Orientation.norm_kahler

theorem eq_zero_or_eq_zero_of_kahler_eq_zero {x y : E} (hx : o.kahler x y = 0) : x = 0 ∨ y = 0 :=
  by
  have : ‖x‖ * ‖y‖ = 0 := by simpa [hx] using (o.norm_kahler x y).symm
  cases' eq_zero_or_eq_zero_of_mul_eq_zero this with h h
  · left
    simpa using h
  · right
    simpa using h
#align orientation.eq_zero_or_eq_zero_of_kahler_eq_zero Orientation.eq_zero_or_eq_zero_of_kahler_eq_zero

theorem kahler_eq_zero_iff (x y : E) : o.kahler x y = 0 ↔ x = 0 ∨ y = 0 :=
  by
  refine' ⟨o.eq_zero_or_eq_zero_of_kahler_eq_zero, _⟩
  rintro (rfl | rfl) <;> simp
#align orientation.kahler_eq_zero_iff Orientation.kahler_eq_zero_iff

theorem kahler_ne_zero {x y : E} (hx : x ≠ 0) (hy : y ≠ 0) : o.kahler x y ≠ 0 :=
  by
  apply mt o.eq_zero_or_eq_zero_of_kahler_eq_zero
  tauto
#align orientation.kahler_ne_zero Orientation.kahler_ne_zero

theorem kahler_ne_zero_iff (x y : E) : o.kahler x y ≠ 0 ↔ x ≠ 0 ∧ y ≠ 0 :=
  by
  refine' ⟨_, fun h => o.kahler_ne_zero h.1 h.2⟩
  contrapose
  simp only [not_and_or, Classical.not_not, kahler_apply_apply, Complex.real_smul]
  rintro (rfl | rfl) <;> simp
#align orientation.kahler_ne_zero_iff Orientation.kahler_ne_zero_iff

theorem kahler_map {F : Type _} [NormedAddCommGroup F] [InnerProductSpace ℝ F]
    [Fact (finrank ℝ F = 2)] (φ : E ≃ₗᵢ[ℝ] F) (x y : F) :
    (Orientation.map (Fin 2) φ.toLinearEquiv o).kahler x y = o.kahler (φ.symm x) (φ.symm y) := by
  simp [kahler_apply_apply, area_form_map]
#align orientation.kahler_map Orientation.kahler_map

/-- The bilinear map `kahler` is invariant under pullback by a positively-oriented isometric
automorphism. -/
theorem kahler_comp_linearIsometryEquiv (φ : E ≃ₗᵢ[ℝ] E)
    (hφ : 0 < (φ.toLinearEquiv : E →ₗ[ℝ] E).det) (x y : E) : o.kahler (φ x) (φ y) = o.kahler x y :=
  by simp [kahler_apply_apply, o.area_form_comp_linear_isometry_equiv φ hφ]
#align orientation.kahler_comp_linear_isometry_equiv Orientation.kahler_comp_linearIsometryEquiv

end Orientation

namespace Complex

attribute [local instance] Complex.finrank_real_complex_fact

@[simp]
protected theorem areaForm (w z : ℂ) : Complex.orientation.areaForm w z = (conj w * z).im :=
  by
  let o := Complex.orientation
  simp only [o.area_form_to_volume_form, o.volume_form_robust Complex.orthonormalBasisOneI rfl,
    Basis.det_apply, Matrix.det_fin_two, Basis.toMatrix_apply, to_basis_orthonormal_basis_one_I,
    Matrix.cons_val_zero, coe_basis_one_I_repr, Matrix.cons_val_one, Matrix.head_cons, mul_im,
    conj_re, conj_im]
  ring
#align complex.area_form Complex.areaForm

@[simp]
protected theorem rightAngleRotation (z : ℂ) : Complex.orientation.rightAngleRotation z = I * z :=
  by
  apply ext_inner_right ℝ
  intro w
  rw [Orientation.inner_rightAngleRotation_left]
  simp only [Complex.areaForm, Complex.inner, mul_re, mul_im, conj_re, conj_im, map_mul, conj_I,
    neg_re, neg_im, I_re, I_im]
  ring
#align complex.right_angle_rotation Complex.rightAngleRotation

@[simp]
protected theorem kahler (w z : ℂ) : Complex.orientation.kahler w z = conj w * z :=
  by
  rw [Orientation.kahler_apply_apply]
  ext1 <;> simp
#align complex.kahler Complex.kahler

end Complex

namespace Orientation

-- mathport name: exprω
local notation "ω" => o.areaForm

-- mathport name: exprJ
local notation "J" => o.rightAngleRotation

open Complex

/-- The area form on an oriented real inner product space of dimension 2 can be evaluated in terms
of a complex-number representation of the space. -/
theorem areaForm_map_complex (f : E ≃ₗᵢ[ℝ] ℂ)
    (hf : Orientation.map (Fin 2) f.toLinearEquiv o = Complex.orientation) (x y : E) :
    ω x y = (conj (f x) * f y).im :=
  by
  rw [← Complex.areaForm, ← hf, o.area_form_map]
  simp
#align orientation.area_form_map_complex Orientation.areaForm_map_complex

/-- The rotation by 90 degrees on an oriented real inner product space of dimension 2 can be
evaluated in terms of a complex-number representation of the space. -/
theorem rightAngleRotation_map_complex (f : E ≃ₗᵢ[ℝ] ℂ)
    (hf : Orientation.map (Fin 2) f.toLinearEquiv o = Complex.orientation) (x : E) :
    f (J x) = I * f x :=
  by
  rw [← Complex.rightAngleRotation, ← hf, o.right_angle_rotation_map]
  simp
#align orientation.right_angle_rotation_map_complex Orientation.rightAngleRotation_map_complex

/-- The Kahler form on an oriented real inner product space of dimension 2 can be evaluated in terms
of a complex-number representation of the space. -/
theorem kahler_map_complex (f : E ≃ₗᵢ[ℝ] ℂ)
    (hf : Orientation.map (Fin 2) f.toLinearEquiv o = Complex.orientation) (x y : E) :
    o.kahler x y = conj (f x) * f y :=
  by
  rw [← Complex.kahler, ← hf, o.kahler_map]
  simp
#align orientation.kahler_map_complex Orientation.kahler_map_complex

end Orientation


import data.int.gcd
/-1. Let a and b be coprime positive integers (recall that coprime here means gcd(a, b) = 1).
I open a fast food restaurant which sells chicken nuggets in two sizes –
you can either buy a box with a nuggets in, or a box with b nuggets in.
Prove that there is some integer N with the property that for all integers m ≥ N,
it is possible to buy exactly m nuggets. -/
variables {N m c d : ℕ}
variables {a b : ℤ }

theorem chicken_mcnugget (hp: int.gcd a b = 1) (h1: a > 0) (h2 : b >0): ∃ N, ∀ m ≥ N, m = a*c + b*d := begin
--{existsi [gcd_a a b, gcd_b a b]},
have h3: ∃ (X Y :ℤ ), X * a + Y * b=1,
let m := gcd_a a b, gcd_b a b,

apply gcd_eq_gcd_ab,

--{ existsi [gcd_a a b , gcd_b a b],
--},
let X' := X + q*b,
let Y' := Y - q*a,
have h4: X' * a + Y' * b = 1,
let Z' := -Y',
-- have h5: (Z' X': ℕ ), X' * a= 1 + Z' * b, by norm_num,
assume N = a * Z' * b,
-- appply by the lemma

end


prelude
import Leanbin.Init.Meta.Default
import Leanbin.Init.Logic
import Leanbin.Init.Data.Nat.Lemmas
import Leanbin.Init.Data.Char.Basic

namespace Char

theorem val_ofNat_eq_of_is_valid {n : Nat} : IsValidChar n → (ofNat n).val = n := by
  intro h <;> unfold of_nat <;> rw [dif_pos h]
#align char.val_of_nat_eq_of_is_valid Char.val_ofNat_eq_of_is_valid

theorem val_ofNat_eq_of_not_is_valid {n : Nat} : ¬IsValidChar n → (ofNat n).val = 0 := by
  intro h <;> unfold of_nat <;> rw [dif_neg h]
#align char.val_of_nat_eq_of_not_is_valid Char.val_ofNat_eq_of_not_is_valid

theorem ofNat_eq_of_not_is_valid {n : Nat} : ¬IsValidChar n → ofNat n = ofNat 0 := by
  intro h <;> apply eq_of_veq <;> rw [val_of_nat_eq_of_not_is_valid h] <;> rfl
#align char.of_nat_eq_of_not_is_valid Char.ofNat_eq_of_not_is_valid

theorem ofNat_ne_of_ne {n₁ n₂ : Nat} (h₁ : n₁ ≠ n₂) (h₂ : IsValidChar n₁) (h₃ : IsValidChar n₂) :
    ofNat n₁ ≠ ofNat n₂ := by
  apply ne_of_vne
  rw [val_of_nat_eq_of_is_valid h₂, val_of_nat_eq_of_is_valid h₃]
  assumption
#align char.of_nat_ne_of_ne Char.ofNat_ne_of_ne

end Char