import .topological_space
import .continuity

namespace hidden

namespace topological_space

variables {α β : Type}

open classical

local attribute [instance] classical.prop_decidable

structure disconnects (X : topological_space α) (U V: myset α): Prop :=
(neq_empty_left: U ≠ ∅) (neq_empty_right: V ≠ ∅)
(open_left: X.is_open U) (open_right: X.is_open V)
(disjoint: U ∩ V = ∅)
(cover: U ∪ V = myset.univ)

def is_disconnected (X: topological_space α): Prop :=
∃ U V: myset α, disconnects X U V

def is_connected (X : topological_space α) : Prop :=
¬is_disconnected X

theorem disconnect_clopen (X: topological_space α) (U V: myset α):
disconnects X U V →
X.is_closed U ∧ X.is_closed V :=
begin
  assume h,
  cases h with _ _ hUo hVo hUVdsj hUVcov,
  -- also a candidate for myset
  have: U.compl = V, {
    apply myset.setext,
    intro x,
    split, {
      assume hx,
      have: x ∈ U ∪ V, {
        rw hUVcov, trivial,
      },
      cases this with hU hV, {
        contradiction,
      }, {
        assumption,
      },
    }, {
      assume hx hUx,
      have: x ∈ U ∩ V, {
        split; assumption,
      },
      rw hUVdsj at this,
      from this,
    },
  },
  rw ←myset.compl_compl U,
  conv {
    congr,
    rw this,
    skip,
    rw ←this,
  },
  unfold is_closed,
  repeat {rw myset.compl_compl},
  split; assumption,
end

theorem surjective_image_connected
(X: topological_space α) (Y: topological_space β)
(f: α → β) (hfc: is_continuous X Y f) (hsurj: function.surjective f):
is_connected X → is_connected Y :=
begin
  assume hXc,
  assume himdc,
  cases himdc with U himdc,
  cases himdc with V himdc,
  have hpreUo := hfc U himdc.open_left,
  have hpreVo := hfc V himdc.open_right,
  apply hXc,
  existsi (myset.inverse_image f U),
  existsi (myset.inverse_image f V),
  split, {
    assume hpreUe,
    have hUne := himdc.neq_empty_left,
    rw ←myset.exists_iff_neq_empty at hUne,
    rw ←myset.empty_iff_eq_empty at hpreUe,
    cases hUne with y hy,
    cases hsurj y with x hx,
    apply hpreUe x,
    rw ←hx at hy,
    from hy,
  }, {
    have hVne := himdc.neq_empty_right,
    assume hpreVe,
    rw ←myset.exists_iff_neq_empty at hVne,
    rw ←myset.empty_iff_eq_empty at hpreVe,
    cases hVne with y hy,
    cases hsurj y with x hx,
    apply hpreVe x,
    rw ←hx at hy,
    from hy,
  }, {
    from hfc U himdc.open_left,
  }, {
    from hfc V himdc.open_right,
  }, {
    rw ←myset.inverse_image_intersection,
    rw himdc.disjoint,
    refl,
  }, {
    rw ←myset.inverse_image_union,
    rw himdc.cover,
    have := myset.inverse_image_of_image_of_univ (myset.function_restrict_to_image f),
    rw myset.to_image_surjective at this,
    from this.symm,
  },
end

theorem image_connected
(X: topological_space α) (Y: topological_space β)
(f: α → β) (hfc: is_continuous X Y f)
(hconn: is_connected X):
is_connected (Y.subspace_topology (myset.imageu f)) :=
begin
  apply surjective_image_connected X _ (myset.function_restrict_to_image f), {
    apply continuous_to_image,
    from hfc,
  }, {
    intro y,
    cases y.property with x hx,
    existsi x,
    apply subtype.eq,
    from hx,
  }, {
    from hconn,
  }
end

theorem discrete_disconnected
(x y: α) (hxy: x ≠ y):
is_disconnected (discrete_topology α) :=
begin
  existsi {z | z = x},
  existsi {z | z ≠ x},
  split, {
    rw ←myset.exists_iff_neq_empty,
    existsi x,
    from rfl,
  }, {
    rw ←myset.exists_iff_neq_empty,
    existsi y,
    change ¬(x = y) at hxy,
    from λ h, hxy h.symm,
  }, {
    trivial,
  }, {
    trivial,
  }, {
    apply myset.setext,
    intro z,
    split; assume h, {
      cases h, contradiction,
    }, {
      exfalso, from h,
    },
  }, {
    apply myset.setext,
    intro z,
    split; assume h, {
      trivial,
    }, {
      apply decidable.em,
    },
  },
end

theorem indiscrete_connected:
is_connected (indiscrete_topology α) :=
begin
  assume hdc,
  cases hdc with U hdc,
  cases hdc with V hdc,
  cases hdc with hUne hVne hUo hVo hUVdj hUVcov,
  cases hUo with hU hU, {
    contradiction,
  }, {
    cases hVo with hV hV, {
      contradiction,
    }, {
      rw ←myset.exists_iff_neq_empty at hUne,
      rw ←myset.exists_iff_neq_empty at hVne,
      cases hUne with x hxU,
      cases hVne with y hyU,
      rw ←myset.empty_iff_eq_empty at hUVdj,
      apply hUVdj x,
      split, {
        assumption,
      }, {
        rw hV,
        trivial,
      },
    },
  },
end

theorem empty_connected
(X: topological_space α):
is_connected (subspace_topology X ∅) :=
begin
  rw ←empty_indiscrete,
  apply indiscrete_connected,
end

theorem singleton_connected
(X: topological_space α) (x: α):
is_connected (subspace_topology X (myset.singleton x)) :=
begin
  rw ←singleton_indiscrete,
  apply indiscrete_connected,
end

-- maybe split up more to avoid cpu stress
theorem connected_iff_N_image_const
(X: topological_space α):
is_connected X ↔
∀ f: α → mynat,
  is_continuous X (discrete_topology mynat) f →
  ∀ x y: α, f x = f y :=
begin
  split, {
    assume hXc,
    intro f,
    assume hfc,
    intros x y,
    by_contradiction hfxfy,
    apply hXc,
    existsi myset.inverse_image f {n | n = f x},
    existsi myset.inverse_image f {n | n ≠ f x},
    split, {
      rw ←myset.exists_iff_neq_empty,
      existsi x,
      from rfl,
    }, {
      rw ←myset.exists_iff_neq_empty,
      existsi y,
      assume h,
      from hfxfy h.symm,
    }, {
      apply hfc,
      trivial,
    }, {
      apply hfc,
      trivial,
    }, {
      rw ←myset.inverse_image_intersection,
      apply myset.inverse_image_empty,
      apply myset.setext,
      intro z,
      split, {
        assume h,
        cases h,
        contradiction,
      }, {
        assume h,
        exfalso, from h,
      },
    }, {
      apply myset.setext,
      intro z,
      split, {
        assume h, trivial,
      }, {
        assume h,
        apply decidable.em,
      },
    },
  }, {
    assume hfNc,
    assume hXdc,
    cases hXdc with U hXdc,
    cases hXdc with V hXdc,
    suffices:
        ∃ (f : α → mynat),
          is_continuous X (discrete_topology mynat) f ∧
          ∃ (x y : α), f x ≠ f y, {
      cases this with f hf,
      cases hf with hfc hxy,
      cases hxy with x hxy,
      cases hxy with y hxy,
      apply hxy,
      apply hfNc,
      assumption,
    }, {
      existsi (λ x, if x ∈ U then (0: mynat) else 1),
      split, {
        have := disconnect_clopen _ _ _ hXdc,
        apply gluing_lemma X _ U V, {
          from this.left,
        }, {
          from this.right,
        }, {
          from hXdc.cover,
        }, {
          suffices
              hconst:
              (λ (x : subtype U), ite (↑x ∈ U) (0: mynat) 1) =
              (λ x, 0), {
            rw hconst,
            apply constant_continuous,
          }, {
            apply funext,
            intro x,
            have hxU: (↑x ∈ U) := x.property,
            rw if_pos hxU,
          },
        }, {
          suffices
            hconst:
            (λ (x : subtype V), ite (↑x ∈ U) (0: mynat) 1) =
            (λ x, 1), {
            rw hconst,
            apply constant_continuous,
          }, {
            apply funext,
            intro x,
            have hxU: (↑x ∉ U), {
              assume hxU,
              have hUVx: ↑x ∈ U ∩ V, {
                split, {
                  assumption,
                }, {
                  from x.property,
                },
              },
              rw hXdc.disjoint at hUVx,
              from hUVx,
            },
            rw if_neg hxU,
          },
        },
      }, {
        cases hXdc with hUne hVne _ _ hUVdisj,
        rw ←myset.exists_iff_neq_empty at hUne,
        rw ←myset.exists_iff_neq_empty at hVne,
        cases hUne with x hx,
        cases hVne with y hy,
        existsi x,
        existsi y,
        simp,
        rw if_pos hx,
        have hyU: y ∉ U, {
          assume hyU,
          have: y ∈ U ∩ V, {
            split; assumption,
          },
          rw hUVdisj at this,
          from this,
        },
        rw if_neg hyU,
        from mynat.zero_ne_one,
      },
    },
  },
end

-- so it gets it from the other equality for you
private lemma transitivity' {α: Type} {x y z w: α}:
y = z → x = y → z = w → x = w :=
begin
  assume hyz hxy hzw,
  transitivity z,
  transitivity y,
  all_goals {assumption},
end

-- does this even end up being faster (:
theorem s_union_of_overlapping_connected
(X: topological_space α)
(S: myset (myset α))
(hconn: ∀ U: myset α, U ∈ S →
 is_connected (subspace_topology X U))
(hlap: ∀ U V: myset α,
 U ∈ S → V ∈ S → U ∩ V ≠ ∅):
is_connected (subspace_topology X (⋃₀ S)) :=
begin
  rw connected_iff_N_image_const,
  intro f,
  assume hfc,
  intros x y,
  cases x.property with U hU,
  cases hU with hU hxU,
  cases y.property with V hV,
  cases hV with hV hxV,
  have hUVdsj := hlap U V hU hV,
  rw ←myset.exists_iff_neq_empty at hUVdsj,
  cases hUVdsj with z hz,
  have hUcon := hconn U hU,
  have hVcon := hconn V hV,
  rw connected_iff_N_image_const at hUcon,
  rw connected_iff_N_image_const at hVcon,
  transitivity f (⟨z, ⟨U, ⟨hU, hz.left⟩⟩⟩: subtype (⋃₀ S)), {
    have step1 := hUcon (λ x: subtype U, f ⟨x, ⟨U, ⟨hU, x.property⟩⟩⟩),
    let g := (λ x: subtype U, f ⟨x, ⟨U, ⟨hU, x.property⟩⟩⟩),
    have h_elp_me:
        is_continuous (subspace_topology X U) (discrete_topology mynat) g, {
      apply
          composition_continuous
            (subspace_topology X U) (X.subspace_topology (⋃₀ S))
            (discrete_topology mynat), {
        have: U ⊆ ⋃₀ S, {
          intro u,
          assume hUu,
          existsi U,
          from ⟨hU, hUu⟩,
        },
        apply s_inclusion_continuous X U (⋃₀ S) this,
      }, {
        from hfc,
      },
    },
    have step2 := step1 h_elp_me ⟨x.val, hxU⟩ ⟨z, hz.left⟩,
    apply transitivity' step2, {
      congr,
      apply subtype.eq,
      refl,
    }, {
      from rfl,
    },
  }, {
    symmetry,
    have step1 := hVcon (λ x: subtype V, f ⟨x, ⟨V, ⟨hV, x.property⟩⟩⟩),
    let g := (λ x: subtype V, f ⟨x, ⟨V, ⟨hV, x.property⟩⟩⟩),
    have h_elp_me:
        is_continuous (subspace_topology X V) (discrete_topology mynat) g, {
      apply
          composition_continuous
            (subspace_topology X V) (X.subspace_topology (⋃₀ S))
            (discrete_topology mynat), {
        have: V ⊆ ⋃₀ S, {
          intro v,
          assume hVv,
          existsi V,
          from ⟨hV, hVv⟩,
        },
        apply s_inclusion_continuous X V (⋃₀ S) this,
      }, {
        from hfc,
      },
    },
    have step2 := step1 h_elp_me ⟨y.val, hxV⟩ ⟨z, hz.right⟩,
    apply transitivity' step2, {
      congr,
      apply subtype.eq,
      refl,
    }, {
      from rfl,
    },
  },
end

theorem union_of_two_overlapping_connected
(X: topological_space α)
(U V: myset α)
(hUcn: is_connected (X.subspace_topology U))
(hVcn: is_connected (X.subspace_topology V))
(hlap: U ∩ V ≠ ∅):
is_connected (subspace_topology X (U ∪ V)) :=
begin
  rw myset.union_two_sUnion,
  apply s_union_of_overlapping_connected, {
    intro W,
    assume hW,
    cases hW; {rw hW, assumption},
  }, {
    intros W1 W2,
    assume hW1 hW2,
    rw ←myset.exists_iff_neq_empty at hlap ⊢,
    cases hlap with x hx,
    existsi x,
    cases hx with hUx hVx,
    split, {
      cases hW1; {rw hW1, assumption},
    }, {
      cases hW2; {rw hW2, assumption},
    },
  },
end

private lemma line_connected
(X: topological_space α) (Y: topological_space β)
(hYcn: is_connected Y) (x: α):
is_connected ((product_topology X Y).subspace_topology
  {xy | xy.fst = x}) :=
begin
  have: {xy : α × β | xy.fst = x}
      = myset.imageu (λ y: β, (x, y)), {
    apply myset.setext,
    intro z,
    split; assume hx, {
      existsi z.snd,
      cases z,
      apply congr,
      apply congr_arg prod.mk,
      from hx.symm,
      refl,
    }, {
      cases hx with y hy,
      rw ←hy,
      from rfl,
    },
  },
  rw this, clear this,
  apply image_connected Y _ (λ (y : β), (x, y)), {
    rw continuous_iff_components_continuous,
    split, {
      apply constant_continuous _ _ x,
    }, {
      apply identity_continuous,
    },
  }, {
    assumption,
  },
end

private lemma cross_connected
(X: topological_space α) (Y: topological_space β)
(hXcn: is_connected X) (hYcn: is_connected Y)
(x: α) (y: β):
is_connected
  ((product_topology X Y).subspace_topology
    (λ xy', xy'.fst = x ∨ xy'.snd = y)) :=
begin
  apply union_of_two_overlapping_connected, {
    apply line_connected,
    assumption,
  }, {
    have homeom := swap_homeomorphism X Y,
    have hlcn := line_connected Y X hXcn y,
    have himcn :=
        image_connected _ _ _
          (restriction_continuous _ _ _ _ homeom.g_continuous) hlcn,
    have:
        (λ a : α × β, a.snd = y)
        = myset.imageu
          (λ (x : subtype {xy : β × α | xy.fst = y}), (x.val.snd, x.val.fst)), {
      apply myset.setext,
      intro z,
      split; assume hz, {
        existsi (⟨(z.snd, z.fst), hz⟩: subtype {xy : β × α | xy.fst = y}),
        cases z,
        refl,
      }, {
        cases hz with z' hz',
        rw ←hz',
        from z'.property,
      },
    },
    rw this, clear this,
    assumption,
  }, {
    rw ←myset.exists_iff_neq_empty,
    existsi (x, y),
    split, {
      from rfl,
    }, {
      from rfl,
    },
  },
end

theorem product_connected
(X: topological_space α) (Y: topological_space β)
(hXcn: is_connected X) (hYcn: is_connected Y):
is_connected (product_topology X Y) :=
begin
  apply surjective_image_connected _ _ _ (univ_homeomorphism _).g_continuous, {
    intro a,
    existsi (⟨a, trivial⟩: subtype myset.univ),
    refl,
  }, {
    by_cases hXe: (myset.univ: myset (α × β)) = ∅, {
      rw hXe,
      apply empty_connected,
    }, {
      cases myset.exists_iff_neq_empty.mpr hXe with xy hxy,
      suffices h: ⋃₀ myset.imageu (λ y: β, (λ xy': α × β, xy'.fst = xy.fst ∨ xy'.snd = y)) = myset.univ, {
        rw ←h,
        apply s_union_of_overlapping_connected, {
          intro U,
          assume hUc,
          cases hUc with y hy,
          rw ←hy,
          apply cross_connected; assumption,
        }, {
          intros U V,
          assume hUc hVc,
          rw ←myset.exists_iff_neq_empty,
          existsi xy,
          split, {
            cases hUc with _ hUc,
            rw ←hUc,
            left,
            refl,
          }, {
            cases hVc with _ hVc,
            rw ←hVc,
            left,
            refl,
          },
        },
      }, {
        apply myset.setext,
        intro xy'',
        split; assume hx, {
          trivial,
        }, {
          existsi λ (xy' : α × β), xy'.fst = xy.fst ∨ xy'.snd = xy''.snd,
          split, {
            existsi xy''.snd,
            refl,
          }, {
            right,
            refl,
          },
        },
      },
    },
  },
end

end topological_space

end hidden


/-
Copyright 2020 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
 -/
import measure_theory.measurable_space

import measure_theory.measure_space
import measure_theory.outer_measure
import measure_theory.lebesgue_measure
import measure_theory.integration
import measure_theory.borel_space
import data.set.countable
import formal_ml.measurable_space
import formal_ml.probability_space
import formal_ml.real_random_variable
import data.complex.exponential
import formal_ml.ennreal
import formal_ml.nnreal
import formal_ml.sum
import formal_ml.exp_bound
import formal_ml.classical

namespace alt_pac

/- A first option is to define our concepts minimally. -/

def raw_error {Ω:Type*} [M:measurable_space Ω]  (μ:measure_theory.measure Ω) {β:Type*}
 (X_test:Ω → (β × bool)) (h:set β):ennreal :=
  μ (X_test ⁻¹' (h.prod {ff} ∪ (hᶜ).prod {tt}))

def raw_false_positive_rate {Ω:Type*} [M:measurable_space Ω] (μ:measure_theory.measure Ω) {β:Type*}
 (X_test:Ω → (β × bool)) (h:set β):ennreal :=
  μ (X_test ⁻¹' (h.prod {ff}))

def raw_false_negative_rate {Ω:Type*} [M:measurable_space Ω] (μ:measure_theory.measure Ω) {β:Type*}
 (X_test:Ω → (β × bool)) (h:set β):ennreal :=
  μ (X_test ⁻¹' (hᶜ.prod {tt}))

def raw_error_eq_raw_fpr_add_raw_fnr {Ω:Type*} [M:measurable_space Ω] (μ:measure_theory.measure Ω) {β:Type*}
 (X_test:Ω → (β × bool)) (h:set β):Prop := raw_error μ X_test h = raw_false_positive_rate μ X_test h + raw_false_negative_rate μ X_test h

/- However, this leads us to require additional constraints for
concepts that should always be true. -/
lemma measurable_error_eq_fpr_add_fnr {Ω:Type*} [M:measurable_space Ω] (μ:measure_theory.measure Ω) {β:Type*}
 (X_test:Ω → (β × bool)) (h:set β) [measurable_space β]:(measurable_set h) → (measurable X_test) → raw_error_eq_raw_fpr_add_raw_fnr μ X_test h := begin
  intros h_h_meas h_X_test_measurable,
  simp only [raw_error_eq_raw_fpr_add_raw_fnr, raw_false_negative_rate, raw_false_positive_rate,
        raw_error, set.preimage_union],
  rw measure_theory.measure_union,
  { rw set.disjoint_iff, apply set.disjoint_preimage, rw set.subset_def,
    intros x, intros h_contra, simp at h_contra,
    cases h_contra with h_contra_fp h_contra_fn,
    cases h_contra_fp with x' h_contra_fp,
    cases h_contra_fn with x'' h_contra_fn,
    cases h_contra_fp with h_contra_fp_pos h_contra_fp_false,
    cases h_contra_fn with h_contra_fn_neg h_contra_fn_true,
    subst h_contra_fp_false,
    simp at h_contra_fn_true,
    apply false.elim h_contra_fn_true, },
  apply measurable_elim,
  apply h_X_test_measurable,
  apply measurable_set.prod,
  apply h_h_meas,
  apply measurable_space.measurable_set_top,
  apply measurable_elim,
  apply h_X_test_measurable,
  apply measurable_set.prod,
  apply measurable_set.compl,
  apply h_h_meas,
  apply measurable_space.measurable_set_top,
end

/-Another possibility is to require measurability as error is defined. -/
def meas_error {Ω:Type*} [M:measurable_space Ω]  (μ:measure_theory.measure Ω)
[measure_theory.probability_measure μ] {β:Type*} [measurable_space β]
 (X_test:Ω → (β × bool)) (h_meas_X_test:measurable X_test) (h:set β) (h_h_meas:measurable_set h):ennreal :=
  μ (X_test ⁻¹' (h.prod {ff} ∪ (hᶜ).prod {tt}))

lemma meas_error_eq_raw_error {Ω:Type*} [M:measurable_space Ω]  (μ:measure_theory.measure Ω)
[measure_theory.probability_measure μ] {β:Type*} [measurable_space β]
 (X_test:Ω → (β × bool)) (h_meas_X_test:measurable X_test) (h:set β) (h_h_meas:measurable_set h):
 meas_error μ X_test h_meas_X_test h h_h_meas = raw_error μ X_test h := rfl

def meas_false_positive_rate
{Ω:Type*} [M:measurable_space Ω]  (μ:measure_theory.measure Ω)
[measure_theory.probability_measure μ] {β:Type*} [measurable_space β]
 (X_test:Ω → (β × bool)) (h_meas_X_test:measurable X_test) (h:set β) (h_h_meas:measurable_set h):ennreal :=
  μ (X_test ⁻¹' (h.prod {ff}))

lemma meas_false_positive_rate_eq_raw_false_positive_rate
{Ω:Type*} [M:measurable_space Ω]  (μ:measure_theory.measure Ω)
[measure_theory.probability_measure μ] {β:Type*} [measurable_space β]
 (X_test:Ω → (β × bool)) (h_meas_X_test:measurable X_test) (h:set β) (h_h_meas:measurable_set h):
  meas_false_positive_rate μ X_test h_meas_X_test h h_h_meas = raw_false_positive_rate μ X_test h := rfl

def meas_false_negative_rate {Ω:Type*} [M:measurable_space Ω]  (μ:measure_theory.measure Ω)
[measure_theory.probability_measure μ] {β:Type*} [measurable_space β]
 (X_test:Ω → (β × bool)) (h_meas_X_test:measurable X_test) (h:set β) (h_h_meas:measurable_set h):ennreal :=
  μ (X_test ⁻¹' (hᶜ.prod {tt}))

lemma meas_false_negative_rate_eq_raw_false_negative_rate
{Ω:Type*} [M:measurable_space Ω]  (μ:measure_theory.measure Ω)
[measure_theory.probability_measure μ] {β:Type*} [measurable_space β]
 (X_test:Ω → (β × bool)) (h_meas_X_test:measurable X_test) (h:set β) (h_h_meas:measurable_set h):
  meas_false_negative_rate μ X_test h_meas_X_test h h_h_meas = raw_false_negative_rate μ X_test h := rfl

def meas_error_eq_meas_fpr_add_meas_fnr {Ω:Type*} [M:measurable_space Ω]  (μ:measure_theory.measure Ω)
[measure_theory.probability_measure μ] {β:Type*} [measurable_space β]
 (X_test:Ω → (β × bool)) (h_meas_X_test:measurable X_test) (h:set β) (h_h_meas:measurable_set h):Prop :=
  meas_error μ X_test h_meas_X_test h h_h_meas = meas_false_positive_rate μ X_test h_meas_X_test h h_h_meas +
  meas_false_negative_rate μ X_test h_meas_X_test h h_h_meas

lemma meas_error_eq_meas_fpr_add_meas_fnr' {Ω:Type*} [M:measurable_space Ω]  (μ:measure_theory.measure Ω)
[measure_theory.probability_measure μ] {β:Type*} [measurable_space β]
 (X_test:Ω → (β × bool)) (h_meas_X_test:measurable X_test) (h:set β) (h_h_meas:measurable_set h):
 meas_error_eq_meas_fpr_add_meas_fnr μ X_test h_meas_X_test h h_h_meas :=
begin
  unfold meas_error_eq_meas_fpr_add_meas_fnr,
  rw meas_error_eq_raw_error,
  rw meas_false_positive_rate_eq_raw_false_positive_rate,
  rw meas_false_negative_rate_eq_raw_false_negative_rate,
  apply measurable_error_eq_fpr_add_fnr,
  apply h_h_meas,
  apply h_meas_X_test,
end

end alt_pac

/- A final approach is to embed measurability within the arguments themselves. We can add a great deal of syntactic sugar as well. -/

section error_def
variables {Ω:Type*} {p:probability_space Ω} {β:Type*} {Mβ:measurable_space β}
variables (X_test:p →ᵣ (Mβ ×ₘ bool.measurable_space)) (h:measurable_setB Mβ)
def error:nnreal :=
   Pr[(X_test.fst ∈ᵣ h ∧ X_test.snd =ᵣ ff) ∨ (X_test.fst ∉ᵣ h) ∧ (X_test.snd =ᵣ tt)]

def false_positive_rate:nnreal :=
   Pr[(X_test.fst ∈ᵣ h ∧ X_test.snd =ᵣ ff)]

def false_negative_rate:nnreal :=
   Pr[(X_test.fst ∉ᵣ h) ∧ (X_test.snd =ᵣ tt)]

lemma error_eq_fpr_add_fnr:error X_test h = false_positive_rate X_test h + false_negative_rate X_test h :=
begin
  simp only [error, false_positive_rate, false_negative_rate],
  apply Pr_disjoint_eor,
  simp,
  rw set.disjoint_iff,
  apply set.subset.trans,
  apply set.inter_subset_inter,
  apply set.inter_subset_left,
  apply set.inter_subset_left,
  rw set.inter_compl_self,
end

end error_def

/-A concept of PAC bounds separated from the method of generating a hypothesis. -/
section pac_def
variables {Ω:Type*} {p:probability_space Ω} {β:Type*} {Mβ:measurable_space β} (X_test:p →ᵣ (Mβ ×ₘ bool.measurable_space))
 {H:Type*} (R:H → measurable_setB Mβ)  [TM:top_measurable H] (A:p →ᵣ TM.to_measurable_space)

def error_fun:H → nnreal := (λ (h:H), error X_test (R h))

noncomputable def error_mf:(@top_measurable.to_measurable_space H TM) →ₘ nnreal.measurable_space :=
top_measurable_fun (λ h, error X_test (R h)) _

/- A generic variant of probably approximately correct.
  This is the probability that a hypothesis drawn from some arbitrary distribution A is
  probably approximately correct. -/
def gen_probably_approximately_correct (δ ε:nnreal):Prop := Pr[((error_mf X_test R) ∘r A) ≤ᵣ ε] ≥ 1 - δ

/- A problem is realizable if there exists a representation for a correct hypothesis. -/
def realizable:Prop := ∃ (h:H), error X_test (R h) = 0

lemma gen_probably_approximately_correct_trivial (ε:nnreal):
  gen_probably_approximately_correct X_test R A 1 ε := begin
  simp [gen_probably_approximately_correct],
end

lemma gen_probably_approximately_correct_mono (δ δ' ε:nnreal):
  (δ ≤ δ') →
  gen_probably_approximately_correct X_test R A δ ε →
  gen_probably_approximately_correct X_test R A δ' ε := begin
  simp [gen_probably_approximately_correct],
  intros h1 h2,
  apply le_trans,
  apply h2,
  apply add_le_add,
  apply le_refl _,
  apply h1,
end

end pac_def

section pac_def
variables {Ω:Type*} {p:probability_space Ω} {β:Type*} [Mβ:measurable_space β] (X_test:p →ᵣ (Mβ ×ₘ bool.measurable_space))
{D:Type*} (X_train:D → (p →ᵣ (Mβ ×ₘ bool.measurable_space)))
{H:Type*} [TM:top_measurable H]
(A:(Πₘ (d:D), Mβ ×ₘ bool.measurable_space) →ₘ TM.to_measurable_space)
 (R:H → measurable_setB Mβ)

def algorithm_rv:p →ᵣ TM.to_measurable_space := A ∘r (pi.random_variable_combine X_train)

/- A proposition that an algorithm based upon training data X_train is probably approximately
   correct. -/
def probably_approximately_correct (δ ε:nnreal):Prop :=
  gen_probably_approximately_correct X_test R (algorithm_rv X_train A) δ  ε

def example_error (h:H) (d:D):event p :=
  (((X_train d).fst ∈ᵣ (R h)) ∧ ((X_train d).snd =ᵣ ff)) ∨
  (((X_train d).fst ∉ᵣ (R h)) ∧ ((X_train d).snd =ᵣ tt))

/- Rather than consider these concepts as events, we insist that they
   hold for all instances. -/
def consistent_hypothesis (x:D→ β × bool) (h:H):Prop :=
   ∀ d:D, (x d).fst ∈ (R h).val ↔ (x d).snd

def consistent_algorithm:Prop := ∀ x:(D → (β × bool)),
  (∃ (c:H), consistent_hypothesis R x c) →
  (consistent_hypothesis R x (A.val x))

lemma probably_approximately_correct_trivial (ε:nnreal):
  probably_approximately_correct X_test X_train A R 1 ε := begin
  simp [probably_approximately_correct],
  apply gen_probably_approximately_correct_trivial,
end

lemma probably_approximately_correct_mono (δ δ' ε:nnreal):
  (δ ≤ δ') →
  probably_approximately_correct X_test X_train A R δ ε →
  probably_approximately_correct X_test X_train A R δ' ε := begin
  simp [probably_approximately_correct],
  intros h1 h2,
  apply gen_probably_approximately_correct_mono ,
  apply h1,
  apply h2,
end

end pac_def

lemma const_measurable_fun_val {Ω:Type*} {M:measurable_space Ω} (x:nnreal):
  ((↑x:(M →ₘ nnreal.measurable_space)).val) = (λ ω:Ω, x) := rfl

section pac_proof
variables {Ω:Type*} {p:probability_space Ω} {β:Type*} [Mβ:measurable_space β] (X_test:p →ᵣ (Mβ ×ₘ bool.measurable_space))
{D:Type*} (X_train:D → (p →ᵣ (Mβ ×ₘ bool.measurable_space)))
{H:Type*} [TM:top_measurable H]
(A:(Πₘ (d:D), Mβ ×ₘ bool.measurable_space) →ₘ TM.to_measurable_space)
 (R:H → measurable_setB Mβ)

lemma error_as_measurable_setB (h:H) (Y:p →ᵣ (Mβ ×ₘ bool.measurable_space)):
 ((mf_fst ∘r Y) ∈ᵣ R h∧(mf_snd ∘r Y) =ᵣ ↑ff ∨ (mf_fst ∘r Y)∉ᵣ R h∧(mf_snd ∘r Y) =ᵣ ↑tt)
 = Y ∈ᵣ ((R h).prod (measurable_setB_top {ff}) ∪ ((R h)ᶜ).prod (measurable_setB_top {tt})) :=
begin
    apply event.eq,
    simp,
    ext ω,
    simp, split; intros A1; cases A1,
    { left, apply exists.intro (Y.val ω).fst, simp [A1], rw ← A1.right, simp },
    { right, apply exists.intro (Y.val ω).fst, simp [A1], rw ← A1.right, simp },
    { left, cases A1 with x A1, rw ← A1.right, simp [A1] },
    { right, cases A1 with x A1, rw ← A1.right, simp [A1] }
end

lemma Pr_example_error_eq_error (h:H) (d:D):
(random_variable_identical X_test (X_train d)) → Pr[example_error X_train R h d] = error X_test (R h) :=
begin
  intros h1,
  simp [error],
  simp [example_error],
  rw error_as_measurable_setB R h (X_train d),
  rw error_as_measurable_setB R h X_test,
  symmetry,
  apply h1,
end

/- The average number of errors for a hypothesis in the training data,
   as a random variable. -/
noncomputable def training_error (h:H) [FD:fintype D]:p →ᵣ (borel nnreal) :=
   average_identifier (example_error X_train R h) FD

def hypothesis_consistent_event (h:H) [FD:fintype D]:
  event p := (∀ᵣ (d:D), enot (example_error X_train R h d))

lemma in_hypothesis_consistent_event_iff_consistent_hypothesis (h:H) [FD:fintype D] (ω:Ω):
   ω ∈ (hypothesis_consistent_event X_train R h) ↔ (consistent_hypothesis R (λ (d:D), (X_train d).val ω) h) :=
begin
  classical,
  simp [hypothesis_consistent_event],
  split,
  { intros h1, rw event_mem_val at h1,
    simp only [consistent_hypothesis],
    intros d,
    simp at h1,
    have h2 := h1 d,
    rw ← subtype.val_eq_coe at h2,
    simp [example_error] at h2,
    rw decidable.not_or_iff_and_not at h2,
    cases h2 with h2 h3,
    rw not_and at h2,
    rw and_comm at h3,
    rw not_and at h3,
    simp at h3,
    simp at h2,
    split, intros h4,
    simp at h4,
    apply h2,
    apply h4,
    intros h5,
    simp,
    apply h3,
    apply h5 },
  { intros h1, rw event_mem_val,
    simp only [consistent_hypothesis] at h1,
    simp,
    intros d,
    simp [example_error],
    rw ← subtype.val_eq_coe,
    simp,
    rw decidable.not_or_iff_and_not,
    have h2 := h1 d,
    simp at h2,
    split,
    { rw not_and, intros h3,
      simp, rw h2 at h3,
      apply h3 },
    { rw and_comm, rw not_and, intros h3,
      simp, rw h2, apply h3,  },
  },
end

def he_hypothesis_consistent_event (h:H) [FD:fintype D] (ε:nnreal):
  event p := (hypothesis_consistent_event X_train R h) ∧ event_const (error X_test (R h) > ε)

--set_option trace.class_instances true
def he_hypothesis_consistent_pac [encodable H] [fintype D] (δ ε:nnreal):Prop :=
  Pr[∃ᵣ (h:H), he_hypothesis_consistent_event X_test X_train R h ε] ≤ δ

/- A realizable hypothesis has probability one of being consistent, i.e.
   almost surely it is consistent. -/
lemma exists_consistent_hypothesis_of_realizable [fintype D]:realizable X_test R →
(∀ (d:D), random_variable_identical X_test (X_train d)) →
∃ (h:H), Pr[hypothesis_consistent_event X_train R h]=1 := begin
  classical,
  intros h1 h2,
  simp [realizable] at h1,
  cases h1 with h h1,
  existsi [h],
  rw ← Pr_one_minus_not_eq,

  simp [hypothesis_consistent_event],
  rw not_forall_not_eq_exists',
  have h3:Pr[∃ᵣ  (a : D), @example_error Ω p β Mβ D X_train H R h a] = 0,
  {
    rw ← le_zero_iff,
    apply le_trans,
    apply eany_fintype_bound,
    rw tsum_fintype,
    have h3_1:(λ (d:D), Pr[@example_error Ω p β Mβ D X_train H R h d]) =(λ (d:D), 0),
    { ext1 d,
      rw Pr_example_error_eq_error,
      apply h1,
      apply h2 },
    rw h3_1,
    simp,
     },
  rw h3,
  simp,
end

lemma approximately_correct_if_he_hypothesis_consistent [encodable H] [fintype D] [nonempty D]
  (ε:nnreal) (h:H):
(Pr[hypothesis_consistent_event X_train R h] = 1) →
(consistent_algorithm A R) →
(((∃ᵣ (h:H), he_hypothesis_consistent_event X_test X_train R h ε)ᶜ ∧
 hypothesis_consistent_event X_train R h
).val)
⊆
((error_mf X_test R ∘r algorithm_rv X_train A) ≤ᵣ ε).val
 :=
begin
  intros h0 h1,
  rw set.subset_def,
  intros ω h2,
  simp only [error_mf, top_measurable_fun, algorithm_rv, pi.measurable_fun, set.mem_set_of_eq, subtype.val_eq_coe,
  event_le_val_def],
  rw ← subtype.val_eq_coe,
  rw ← subtype.val_eq_coe,
  rw const_measurable_fun_val,
  simp,
  simp [he_hypothesis_consistent_event,
        const_measurable_fun] at h2,
  let h':H := A.val (λ (d : D), (X_train d).val ω),
  begin
    have B2:error X_test (R h') ≤ ε,
    { cases h2 with h2 h3,
      apply h2,
      rw ← subtype.val_eq_coe,
      rw ← event_mem_val,
      rw in_hypothesis_consistent_event_iff_consistent_hypothesis,
      simp [consistent_algorithm] at h1,
      simp [h'], apply h1,
      rw ← subtype.val_eq_coe at h3,
      rw ← event_mem_val at h3,
      rw in_hypothesis_consistent_event_iff_consistent_hypothesis at h3,
      apply h3 },
    apply B2,
  end
end

lemma consistent_pac [encodable H] [fintype D] [nonempty D] (δ ε:nnreal):(realizable X_test R) →
  (∀ (d:D), random_variable_identical X_test (X_train d)) →
  (he_hypothesis_consistent_pac X_test X_train R δ ε) → (consistent_algorithm A R) →
  (probably_approximately_correct X_test X_train A R δ ε) :=
begin
  intros h_realizable h_identical h1 h2,
  unfold probably_approximately_correct gen_probably_approximately_correct,
  unfold he_hypothesis_consistent_pac at h1,
  have h3:∃ (h:H), Pr[hypothesis_consistent_event X_train R h]=1,
  { apply exists_consistent_hypothesis_of_realizable, apply h_realizable,
    apply h_identical },
  cases h3 with c h3,

  have h4:Pr[(∃ᵣ  (h : H), he_hypothesis_consistent_event X_test X_train R h ε)ᶜ ∧ (hypothesis_consistent_event X_train R c)] ≥ 1 - δ,
  {
    have h4_1:Pr[(hypothesis_consistent_event X_train R c)ᶜ]=0,
    { rw neg_eq_not, rw ← Pr_one_minus_eq_not, rw h3, simp },
    have h4_2:Pr[(∃ᵣ  (h : H), he_hypothesis_consistent_event X_test X_train R h ε) ∨ (hypothesis_consistent_event X_train R c)ᶜ] ≤ δ,
    { apply le_trans, apply Pr_le_eor_sum,
      rw h4_1,
      rw add_zero,
      apply h1 },
    have h4_3:Pr[((∃ᵣ  (h : H), he_hypothesis_consistent_event X_test X_train R h ε) ∨ (hypothesis_consistent_event X_train R c)ᶜ)ᶜ] ≥ 1 - δ,
    { apply Pr_compl_ge_of_Pr_le, apply h4_2 },
    have h4_4:((∃ᵣ  (h : H), he_hypothesis_consistent_event X_test X_train R h ε) ∨ (hypothesis_consistent_event X_train R c)ᶜ)ᶜ = ((∃ᵣ  (h : H), he_hypothesis_consistent_event X_test X_train R h ε)ᶜ ∧ (hypothesis_consistent_event X_train R c)),
    { rw compl_eor_eq_compl_eand_compl, repeat {rw neg_eq_not}, rw enot_enot_eq_self, },
    rw ← h4_4,
    apply h4_3,
     },
  apply le_trans h4,
  apply event_prob_mono2,
  apply approximately_correct_if_he_hypothesis_consistent,
  apply h3,
  apply h2,
end

end pac_proof

section finite_pac_proof
variables {Ω:Type*} {p:probability_space Ω} {β:Type*} [Mβ:measurable_space β] (X_test:p →ᵣ (Mβ ×ₘ bool.measurable_space))
{D:Type*} (X_train:D → (p →ᵣ (Mβ ×ₘ bool.measurable_space)))
{H:Type*} [TM:top_measurable H]
(A:(Πₘ (d:D), Mβ ×ₘ bool.measurable_space) →ₘ TM.to_measurable_space)
 (R:H → measurable_setB Mβ)

def example_correct (h:H) (d:D):event p := ¬ₑ (example_error X_train R h d)

lemma example_correct_def (h:H) (d:D):example_correct X_train R h d = ¬ₑ (example_error X_train R h d) := rfl

lemma example_error_IID (h:H):
  (random_variables_IID X_train) →
  (events_IID (example_error X_train R h)) :=
begin
  intros h1,
  have h2:(example_error X_train R h) = (λ (d:D),
    (X_train d) ∈ᵣ ((R h).prod (measurable_setB_top {ff}) ∪ ((R h)ᶜ).prod (measurable_setB_top {tt}))),
  { ext1 d,
    simp [example_error],
    rw error_as_measurable_setB },
  rw h2,
  apply rv_event_IID,
  apply h1,
end
lemma example_correct_IID (h:H):
  (random_variables_IID X_train) →
  (events_IID (example_correct X_train R h)) :=
begin
  intros h1,
  have h2:events_IID (enot ∘ (example_error X_train R h)),
  { apply events_IID_not_of_events_IID,
    apply example_error_IID,
    apply h1,
  },
  apply h2,
end

lemma Pr_hypothesis_consistent_event (h:H) [FD:fintype D] [NE:nonempty D]:
  (∀ (d:D), random_variable_identical X_test (X_train d)) →
  (random_variables_IID X_train) →
  Pr[hypothesis_consistent_event X_train R h] = (1-error X_test (R h))^(fintype.card D) :=
begin
  intros h1 h2,
  simp [hypothesis_consistent_event],
  have h3:(λ (d:D), ¬ₑ example_error X_train R h d) =
    (λ (d:D), example_correct X_train R h d) := rfl,
  rw h3,
  haveI:inhabited D := classical.inhabited_of_nonempty NE,
  rw eall_fintype_eq_eall_finset, rw events_IID_pow,
  have h4:finset.univ.card = fintype.card D := rfl,
  rw h4,
  have h5:Pr[example_correct X_train R h (default D)]
    = (1 - error X_test (R h)),
  { rw example_correct_def, rw ← Pr_one_minus_eq_not,
    rw Pr_example_error_eq_error,
    apply h1 },
  rw h5,
  apply example_correct_IID, apply h2,
end

lemma Pr_he_hypothesis_consistent_event (h:H) [FD:fintype D] [NE:nonempty D] (ε:nnreal):
  (∀ (d:D), random_variable_identical X_test (X_train d)) →
  (random_variables_IID X_train) →
  Pr[he_hypothesis_consistent_event X_test X_train R h ε] ≤ (1-ε)^(fintype.card D) :=
begin
  intros h1 h2,
  simp [he_hypothesis_consistent_event],
  cases decidable.em (ε < error X_test (R h)) with h3 h3,
  { apply le_trans,
    apply Pr_eand_le_left,
    rw Pr_hypothesis_consistent_event X_test,
    apply nnreal_pow_mono,
    apply nnreal_sub_le_sub_of_le,
    apply le_of_lt h3,
    apply h1,
    apply h2 },
  { apply le_trans,
    apply Pr_eand_le_right,
    rw Pr_event_const_false,
    simp,
    apply h3 },
end

lemma Pr_he_hypothesis_consistent_event_exp (h:H) [FD:fintype D] [NE:nonempty D] (ε:nnreal):
  (∀ (d:D), random_variable_identical X_test (X_train d)) →
  (random_variables_IID X_train) →
  Pr[he_hypothesis_consistent_event X_test X_train R h ε] ≤ nnreal.exp (-ε * fintype.card D) :=
begin
  intros h1 h2,
  apply le_trans,
  apply Pr_he_hypothesis_consistent_event,
  apply h1,
  apply h2,
  apply nnreal_exp_bound2,
end

def he_hypothesis_consistent_pac_fintype [FH:fintype H] [fintype D] (δ ε:nnreal):Prop :=
  @he_hypothesis_consistent_pac _ _ _ _ X_test _ X_train _ R (fintype.encodable H) _ δ ε
--  Pr[∃ᵣ (h:H), he_hypothesis_consistent_event X_test T R h ε] ≤ δ

lemma he_hypothesis_consistent_pac_fintype_def [FH:fintype H] [fintype D] (δ ε:nnreal):
  he_hypothesis_consistent_pac_fintype X_test X_train R δ ε =
  (Pr[∃ᵣ (h:H), he_hypothesis_consistent_event X_test X_train R h ε] ≤ δ) := begin
  have h1:(∃ᵣ (h:H), he_hypothesis_consistent_event X_test X_train R h ε) =
          eany_fintype FH (λ (h:H), he_hypothesis_consistent_event X_test X_train R h ε) := rfl,
  rw h1,
  clear h1,
  simp [he_hypothesis_consistent_pac_fintype, he_hypothesis_consistent_pac],
  rw eany_encodable_notation_def,
  rw eany_eq_eany_fintype,
end

lemma he_hypothesis_consistent_pac_fintype_bound [FH:fintype H] [FD:fintype D]
  [NE:nonempty D] (ε:nnreal):
  (∀ (d:D), random_variable_identical X_test (X_train d)) →
  (random_variables_IID X_train) →
  he_hypothesis_consistent_pac_fintype X_test X_train R
    ((fintype.card H) * nnreal.exp (-ε * fintype.card D)) ε :=
begin
  intros h1 h2,
  rw he_hypothesis_consistent_pac_fintype_def,
  apply eany_fintype_bound2,
  intros h,
  apply Pr_he_hypothesis_consistent_event_exp,
  apply h1,
  apply h2,
end

lemma consistent_pac_finite [fintype H] [fintype D] [nonempty D] (δ ε:nnreal):(realizable X_test R) →
  (∀ (d:D), random_variable_identical X_test (X_train d)) →
  (he_hypothesis_consistent_pac_fintype X_test X_train R δ ε) → (consistent_algorithm A R) →
  (probably_approximately_correct X_test X_train A R δ
   ε) :=
begin
  intros h1 h2 h3 h4,
  haveI:encodable H :=fintype.encodable H,
  apply consistent_pac,
  apply h1,
  apply h2,
  apply h3,
  apply h4,
end

/--
  This is the meat of the proof of PAC bounds on a finite set of hypotheses.
  The only condition that makes it incomplete is that it requires the training data
  to be nonempty, whereas the result is trivially true if D is empty.
 -/
lemma pac_finite_bound_nonempty
  [fintype H]                                    -- hypothesis space is finite
  [fintype D]                                    -- examples are finite
  [nonempty D]                                   -- examples are nonempty
  (ε:nnreal):                                    -- approximation parameter
  (realizable X_test R) →                        -- there exists a hypothesis with zero error
  (∀ (d:D), random_variable_identical X_test (X_train d)) →
                                                 -- the training data is identically
                                                 -- distributed as the test
  (random_variables_IID X_train) →               -- the training data is IID
  (consistent_algorithm A R) →                   -- the algorithm is consistent.
  (probably_approximately_correct X_test X_train A R
  ((fintype.card H) * nnreal.exp (-ε * fintype.card D))
  ε)
  -- The algorithm has error less than ε with probability at least
  -- 1 - ((fintype.card H) * nnreal.exp (-ε * fintype.card D))
 :=
begin
  intros h1 h2 h3 h4,
  apply consistent_pac_finite,
  apply h1,
  apply h2,
  apply he_hypothesis_consistent_pac_fintype_bound,
  apply h2,
  apply h3,
  apply h4,
end

/-- A trivial result: if there are no examples, then the bound is trivial. -/
lemma pac_finite_bound_empty
  [fintype H]                                    -- hypothesis space is finite
  [fintype D]                                    -- examples are finite
  (ε:nnreal):                                    -- approximation parameter
  (realizable X_test R) →                             -- (Just to prove H is nonempty).
  (fintype.card D = 0) →
  (probably_approximately_correct X_test X_train A R
  ((fintype.card H) * nnreal.exp (-ε * fintype.card D))
  ε)
  -- The algorithm has error less than ε with probability at least
  -- 1 - ((fintype.card H) * nnreal.exp (-ε * fintype.card D))
 :=
begin
  intros h1 h2,
  cases h1 with c h1,
  have h3:nonempty H := nonempty.intro c,
  have h4:(1:nnreal) ≤ ((fintype.card H) * nnreal.exp (-ε * fintype.card D)),
  { rw h2, simp, rw nat.succ_le_iff, rw fintype.card_pos_iff, apply h3 },
  apply probably_approximately_correct_mono,
  apply h4,
  apply probably_approximately_correct_trivial,
end

end finite_pac_proof

theorem pac_finite_bound
  {Ω:Type*}                                      -- Outcome space
  {p:probability_space Ω}                        -- Probability space
  {β:Type*}                                      -- instance space
  [Mβ:measurable_space β]                        -- measurable space over instances
  (X_test:p →ᵣ (Mβ ×ₘ bool.measurable_space))    -- test data
  {D:Type*}                                      -- training data index
  (T:D → (p →ᵣ (Mβ ×ₘ bool.measurable_space)))   -- training data
  {H:Type*}                                      -- hypothesis space
  [TM:top_measurable H]                          -- measurable space over hypotheses
  (A:(Πₘ (d:D), Mβ ×ₘ bool.measurable_space) →ₘ TM.to_measurable_space)
                                                 -- algorithm
  (R:H → measurable_setB Mβ)                      -- representation schema
  [fintype H]                                    -- hypothesis space is finite
  [fintype D]                                    -- examples are finite
  (ε:nnreal):                                    -- approximation parameter
  (realizable X_test R) →                        -- there exists a hypothesis with zero error
  (∀ (d:D), random_variable_identical X_test (T d)) →
                                                 -- the training data is identical to test
  (random_variables_IID T) →                     -- the training data is IID
  (consistent_algorithm A R) →                   -- the algorithm is consistent.
  (probably_approximately_correct X_test T A R
  ((fintype.card H) * nnreal.exp (-ε * fintype.card D))
  ε)
  -- The algorithm has error less than ε with probability at least
  -- 1 - ((fintype.card H) * nnreal.exp (-ε * fintype.card D))
 :=
begin
  intros h1 h2 h3 h4,
  cases decidable.em (fintype.card D = 0),
  { apply pac_finite_bound_empty,
    apply h1,
    apply h },
  { have h5:0 < fintype.card D,
    { apply decidable.by_contradiction, intros contra,
      apply h, simp at contra, apply contra },
    rw fintype.card_pos_iff at h5,
    haveI: nonempty D := h5,
    apply pac_finite_bound_nonempty,
    repeat { assumption } },
end

/-
def generate_training_data
  {β:Type*}                                      -- instance space
  [Mβ:measurable_space β]                        -- measurable space over instances
  {H:Type*}                                      -- hypothesis space
  [TM:top_measurable H]                          -- measurable space over hypotheses
  (R:H → measurable_setB Mβ)                      -- representation schema
  {Ω:Type*}                                      -- For all
  (A:nnreal → nnreal → Π m:ℕ, (Πₘ (d:fin m), Mβ ×ₘ bool.measurable_space) →ₘ TM.to_measurable_space):
-/

#check 3

/-

Ultimately, we want to be able to talk about an algorithm that has access
to a process of independent and identical random variables EX(c, D). While
we have shown how to create the product of two measures and the product
of a finite number of measures.
From Kearns and Vazirani, An Introduction to Computational Learning Theory:

--------------------------------------------------------------------
Definition: (The PAC Model, Preliminary definition), PAC-learnable
Let C be a concept class over X. We say that C is PAC learnable if there exists
an algorithm L with the following property: for every concept c∈ C, for every
distribution D on X, and for all 0 < ε < 1/2, and 0 < δ < 1/2, if L is
given access to EX(c, D), and inputs ε and δ, then with probability at
least 1 - δ, L outputs a hypothesis concept h ∈ C satisfying error(h) ≤ ε.
This probability is taken over the random examples drawn by calls to
EX(c, D), and any internal randomization of L.

If L runs in time polynomial in (1/ε) and (1/δ), we say that C is
efficiently PAC learnable. We will sometimes refer to the input ε
as the error parameter, and the input δ as the confidence parameter.
--------------------------------------------------------------------

Alternately, we can write down a simpler variant that covers many
PAC learning algorithms.
0. For a specific concept class C (represented by R):
1. Consider ε and δ:
2. Algorithm (part 1) chooses an m given ε and δ.
3. Get a distribution of examples.
4. Algorithm (part 2) uses m, ε, and δ,
   to produce an hypothesis in h.
-/

/- TODO: delete or fix! -/
def pac_learnable_simple_algorithm
  {β:Type*}                                      -- instance space
  [Mβ:measurable_space β]                        -- measurable space over instances
  {H:Type*}                                      -- hypothesis space
  [TM:top_measurable H]                          -- measurable space over hypotheses
  (R:H → measurable_setB Mβ)                      -- representation schema
  (num_examples:nnreal → nnreal → nat)           -- number of examples as a function of the hypothesis.
  (A:nnreal → nnreal → Π m:ℕ, (Πₘ (d:fin m), Mβ ×ₘ bool.measurable_space) →ₘ TM.to_measurable_space):
                                                 -- An algorithm for choosing a hypothesis.
  Prop := ∀ (h:H)                                -- For all hypotheses,
            (ε:nnreal)                           -- For all error parameters (see constraints below),
            (δ:nnreal)                           -- For all confidence parameters (see constraints),
            {Ω:Type*}                            -- For all
            {p:probability_space Ω}
            (X:p →ᵣ Mβ), true


import topology.basic
import topology.compact_open
import data.nat.prime
import data.real.basic
import data.real.irrational
import data.complex.basic
import data.fin.basic
import geometry.euclidean.basic
import analysis.inner_product_space.pi_L2
import algebra.group.defs
import algebra.field.basic
import combinatorics.configuration
import ring_theory.polynomial.basic
import group_theory.free_group
import combinatorics.simple_graph.basic
import ring_theory.integral_closure
import data.fintype.card
import category_theory.category.basic
import ring_theory.discrete_valuation_ring
import group_theory.torsion
import linear_algebra.matrix.charpoly.basic
import algebra.order.absolute_value
import analysis.convex.basic
import topology.uniform_space.uniform_convergence_topology
import topology.sequences
import analysis.normed.group.infinite_sum
import data.nat.choose.sum
import group_theory.specific_groups.cyclic
import group_theory.order_of_element
import analysis.mean_inequalities
import analysis.normed_space.banach
import topology.algebra.continuous_monoid_hom
import linear_algebra.matrix.symmetric
import analysis.inner_product_space.spectrum
import ring_theory.class_group
import ring_theory.dedekind_domain.basic
import ring_theory.principal_ideal_domain
import model_theory.satisfiability
import probability.integration
import ring_theory.simple_module
import category_theory.preadditive.schur
import representation_theory.maschke
import topology.paracompact
import combinatorics.simple_graph.coloring
--Prove that fractional parts of the integer multiples of an irrational number form a dense subset of the unit interval in Lean mathlib

--OUTPUT 1
theorem frac_irrational_dense_in_unit_interval (x : ℝ) (hx : ¬ is_rat x) :
∃ s : set ℕ, (set_of_finite_interval (0 : ℝ) 1) = s.image ((λ y, ((x : ℝ)*y)%real) : (ℕ → ℝ)) :=
sorry

--Every real number is an upper bound of a finite set of rational numbers.
--This suggests that the least upper bound of a finite set of rational numbers is actually just a real number.

--Prove that the least upper bound of a finite set of rational numbers is indeed a real number.

--Hint: For any rational number q, x < q ∨ x = q ∨ x > q.

--Hint: You may find the set_of_finite_interval and rat_sup lemmas useful.
lemma rat_sup_of_finite_set (s : set ℚ) (hs : finite s) : set.finite_sup s = real.sup s :=
sorry

--Prove that if we have a sequence of real numbers with upper bound, then it must converge

--Hint: You may find the below lemmas useful.
lemma real.bdd_iff_exists_real_ub (s : ℕ → ℝ) :
bdd_above s ↔ ∃ c : ℝ, ∀ n : ℕ, s n ≤ c :=
sorry

lemma real.bdd_above_iff_exists_real_ub (s : ℕ → ℝ) :
bdd_above s ↔ ∃ c : ℝ, ∀ n : ℕ, s n ≤ c := sorry

--The real numbers are complete. That is, every real valued Cauchy sequence converges to a real number.

--Prove that every real valued Cauchy sequence converges to a real number.

--Hint: For any real number a, x < a ∨ x = a ∨ x > a.

--Hint: You may find the below lemmas useful.
lemma real.tendsto_of_cauchy_exists (s : ℕ → ℝ) (h : cauchy s) : ∃ l : ℝ, tendsto s (𝓝 l) := sorry

lemma real.tendsto_eq_nhds_of_cauchy (s : ℕ → ℝ) (h : cauchy s) : tendsto s (𝓝 (lim s)) := sorry

section
open real
local attribute [instance] classical.prop_decidable

--For the following exercises, fill in all the sorrys.

/-- The complete ordered field of real numbers -/
def real : Type :=
{ x : ℝ // ∀ y : ℝ, y < x ∨ y = x ∨ x < y }

lemma real.exists_lt_pos : ∃ x : real, (0 : ℝ) < x := sorry

noncomputable def real.has_lift : has_lift ℝ real := sorry

lemma real.lift_coe {x : ℝ} : (↑x : real) = sorry := sorry

lemma real.lift_coe_inj {x y : ℝ} (h : (↑x : real) = ↑y) : x = y := sorry

lemma real.lift_id {r : real} : ↑r = r := sorry

lemma real.coe_lt_of_lt : ∀ {x y : real}, x < y → (↑x : ℝ) < ↑y := sorry

lemma real.lt_coe_of_lt : ∀ {x y : real}, (↑x : ℝ) < ↑y → x < y := sorry

lemma real.lt_of_coe_lt : ∀ {x y : real}, (↑x : ℝ) < y → x < y := sorry

lemma real.coe_lt_coe : ∀ {x y : real}, x < y → (↑x : ℝ) < ↑y := sorry

lemma real.coe_lt_coe_iff : ∀ {x y : real}, (↑x : ℝ) < ↑y ↔ x < y := sorry

lemma real.coe_lt_coe_iff_lt : ∀ {x y z : real}, x < z → z < y → (↑x : ℝ) < ↑y := sorry

lemma real.coe_lt_coe_iff_le : ∀ {x y z : real}, x ≤ z → z < y → (↑x : ℝ) < ↑y := sorry

lemma real.eq_of_coe_eq_coe : ∀ {x y : real}, (↑x : ℝ) = ↑y → x = y := sorry

lemma real.eq_iff_coe_eq_coe : ∀ {x y : real}, x = y ↔ (↑x : ℝ) = ↑y := sorry

lemma real.lt_iff_coe_lt_coe : ∀ {x y : real}, x < y ↔ (↑x : ℝ) < ↑y := sorry

lemma real.le_iff_coe_le_coe : ∀ {x y : real}, x ≤ y ↔ (↑x : ℝ) ≤ ↑y := sorry

lemma real.lt_iff_lt_coe : ∀ {x y : real}, x < y ↔ ↑x < ↑y := sorry

lemma real.le_iff_le_coe : ∀ {x y : real}, x ≤ y ↔ ↑x ≤ ↑y := sorry

lemma real.lt_iff_coe_lt_of_coe_lt_coe : ∀ {x y : real}, (↑x : ℝ) < ↑y → x < y ↔ (↑x : ℝ) < ↑y := sorry

lemma real.lt_iff_of_coe_lt_coe : ∀ {x y : real}, (↑x : ℝ) < ↑y → x < y ↔ ↑x < ↑y := sorry

lemma real.coe_lt_iff_lt_of_coe_lt_coe : ∀ {x y z : real}, (↑x : ℝ) < ↑y → x < z → ↑x < ↑z := sorry

lemma real.coe_lt_iff_le_of_coe_lt_coe : ∀ {x y z : real}, (↑x : ℝ) < ↑y → x ≤ z → ↑x ≤ ↑z := sorry

lemma real.coe_lt_iff_lt_of_lt_coe : ∀ {x y z : real}, x < ↑y → x < z → ↑x < ↑z := sorry

lemma real.coe_lt_iff_le_of_lt_coe : ∀ {x y z : real}, x < ↑y → x ≤ z → ↑x ≤ ↑z := sorry

lemma real.coe_lt_iff_lt_of_coe_lt : ∀ {x y z : real}, (↑x : ℝ) < y → x < z → ↑x < ↑z := sorry

lemma real.coe_lt_iff_le_of_coe_lt : ∀ {x y z : real}, (↑x : ℝ) < y → x ≤ z → ↑x ≤ ↑z := sorry

lemma real.coe_lt_iff_lt_of_lt_coe : ∀ {x y z : real}, x < ↑y → x < z → ↑x < ↑z := sorry

lemma real.coe_lt_iff_le_of_lt_coe : ∀ {x y z : real}, x < ↑y → x ≤ z → ↑x ≤ ↑z := sorry

lemma real.lt_iff_coe_lt_of_coe_lt : ∀ {x y : real}, (↑x : ℝ) < y → x < y ↔ (↑x : ℝ) < ↑y := sorry

lemma real.lt_iff_of_coe_lt : ∀ {x y : real}, (↑x : ℝ) < y → x < y ↔ ↑x < ↑y := sorry

lemma real.coe_lt_iff_lt_of_lt_of_coe_lt_coe : ∀ {x y z : real}, x < z →

--OUTPUT 2
theorem Q2 (b:ℝ): ¬ ∃ n, b = (n:ℝ) :=
begin
  intro h, cases h with n h,
  have h1 := Q1 b n h,
  have h2 : (1:ℝ) ∈ closure ({(b * 0) % 1, b / 1, (b * 1) % 1}),
  from closure_subset (subset.refl _),
  have h3 := subset_closure_iff_subset_of_is_open (half_open_lt_one 1) h2,
  have h4 := h3 (ne_empty_of_mem h2),
  have h5 := subset_singleton_iff.2 h4,
  rw h5 at h1,
  rw nat.mod_eq_mod_nat (lt_of_lt_of_le (by linarith) h1) at h1,
  exact lt_irrefl (nat.mod_nat_lt (lt_of_lt_of_le (by linarith) h1)) h1,

--OUTPUT 3
theorem integers_of_irrational_are_dense (a : ℝ) (h : irrational a) : ∀ n : ℕ, ∃ p : ℕ, n ≤ p ∧ (p - n : ℤ) ∈ {x | x ∈ ℤ ∧ ∥x∥ < 1} :=
begin
    intros n,
    cases (nat.finite_iff_exists_nat_le (a * n) + 0.5) with m hm,
    use [m - n, nat.sub_le_sub_right_iff.mp hm, sub_mem_iff_mem_add.mp (iff.elim_right hm)],

github-repos/lean/ayush1801-Autoformalisation_benchmarks/Autoformalisation_benchmarks-51e1e942a0314a46684f2521b95b6b091c536051/proof/Zero-shot-theorem-names/Zero-Shot-prompts_temperature_0.8_max_tokens_2000_n_3/clean_files/fractional parts of the integer multiples of an irrational number form a dense subset of the unit interval.lean