import Lean.Data.Json
import Mathlib.Data.Nat.Basic
import Mathlib.Init.Algebra.Order
import Mathlib.Init.Data.Nat.Basic
import Mathlib.Init.Data.Nat.Lemmas
import Mathlib.Init.Data.Int.Basic
import Mathlib.Tactic.LibrarySearch
import Mathlib.Data.Equiv.Basic
import Mathlib.Init.Data.Int.Order
import Timelib.Date.Year
import Timelib.Date.Month
import Timelib.Date.ScalarDate
import Timelib.Date.OrdinalDate
import Timelib.Date.Ymd
import Timelib.Util

open Lean

theorem Year.lastDayFebruary_eq (y : Year) : y.lastDayFebruary = Month.february.numDays y + Year.lastDayJanuary := by
  by_cases h: y.isLeapYear <;> simp [h,  Month.numDays]

theorem Year.lastDayMarch_eq (y : Year) : y.lastDayMarch = Month.march.numDays y + y.lastDayFebruary := by
  simp [Month.numDays]; by_cases h: y.isLeapYear <;> simp [h]

theorem Year.lastDayApril_eq (y : Year) : y.lastDayApril = Month.april.numDays y + y.lastDayMarch := by
  simp [Month.numDays]; by_cases h: y.isLeapYear <;> simp [h]

theorem Year.lastDayMay_eq (y : Year) : y.lastDayMay = Month.may.numDays y + y.lastDayApril := by
  simp [Month.numDays]; by_cases h: y.isLeapYear <;> simp [h]

theorem Year.lastDayJune_eq (y : Year) : y.lastDayJune = Month.june.numDays y + y.lastDayMay := by
  simp [Month.numDays]; by_cases h: y.isLeapYear <;> simp [h]

theorem Year.lastDayJuly_eq (y : Year) : y.lastDayJuly = Month.july.numDays y + y.lastDayJune := by
  simp [Month.numDays]; by_cases h: y.isLeapYear <;> simp [h]

theorem Year.lastDayAugust_eq (y : Year) : y.lastDayAugust = Month.august.numDays y + y.lastDayJuly := by
  simp [Month.numDays]; by_cases h: y.isLeapYear <;> simp [h]

theorem Year.lastDaySeptember_eq (y : Year) : y.lastDaySeptember = Month.september.numDays y + y.lastDayAugust := by
  simp [Month.numDays]; by_cases h: y.isLeapYear <;> simp [h]

theorem Year.lastDayOctober_eq (y : Year) : y.lastDayOctober = Month.october.numDays y + y.lastDaySeptember := by
  simp [Month.numDays]; by_cases h: y.isLeapYear <;> simp [h]

theorem Year.lastDayNovember_eq (y : Year) : y.lastDayNovember = Month.november.numDays y + y.lastDayOctober := by
  simp [Month.numDays]; by_cases h: y.isLeapYear <;> simp [h]

theorem Year.lastDayDecember_eq (y : Year) : y.lastDayDecember = Month.december.numDays y + y.lastDayNovember := by
  simp [Month.numDays]; by_cases h: y.isLeapYear <;> simp [h]

/- Predicates used to show that the conversion between (days <= 365) and (Month, day) pairs
   is a bijection. There isn't really a "nice" way of doing this, because
   months have irregular numbers of days, and the number of days depends on
   whether the current year is a leap year. Lean can't handle a function
   with 365 cases, so instead we chunk them into 12 groups by month -/
@[reducible] def OrdinalDate.isJanuaryDay   (d : OrdinalDate) : Prop := d.day <= Year.lastDayJanuary
@[reducible] def OrdinalDate.isFebruaryDay  (d : OrdinalDate) : Prop := Year.lastDayJanuary < d.day ∧ d.day <= d.year.lastDayFebruary
@[reducible] def OrdinalDate.isMarchDay     (d : OrdinalDate) : Prop := d.year.lastDayFebruary < d.day ∧ d.day <= d.year.lastDayMarch
@[reducible] def OrdinalDate.isAprilDay     (d : OrdinalDate) : Prop := d.year.lastDayMarch < d.day ∧ d.day <= d.year.lastDayApril
@[reducible] def OrdinalDate.isMayDay       (d : OrdinalDate) : Prop := d.year.lastDayApril < d.day ∧ d.day <= d.year.lastDayMay
@[reducible] def OrdinalDate.isJuneDay      (d : OrdinalDate) : Prop := d.year.lastDayMay < d.day ∧ d.day <= d.year.lastDayJune
@[reducible] def OrdinalDate.isJulyDay      (d : OrdinalDate) : Prop := d.year.lastDayJune < d.day ∧ d.day <= d.year.lastDayJuly
@[reducible] def OrdinalDate.isAugustDay    (d : OrdinalDate) : Prop := d.year.lastDayJuly < d.day ∧ d.day <= d.year.lastDayAugust
@[reducible] def OrdinalDate.isSeptemberDay (d : OrdinalDate) : Prop := d.year.lastDayAugust < d.day ∧ d.day <= d.year.lastDaySeptember
@[reducible] def OrdinalDate.isOctoberDay   (d : OrdinalDate) : Prop := d.year.lastDaySeptember < d.day ∧ d.day <= d.year.lastDayOctober
@[reducible] def OrdinalDate.isNovemberDay  (d : OrdinalDate) : Prop := d.year.lastDayOctober < d.day ∧ d.day <= d.year.lastDayNovember
@[reducible] def OrdinalDate.isDecemberDay  (d : OrdinalDate) : Prop := d.year.lastDayNovember < d.day

@[reducible] def OrdinalDate.toYmd (ordinal : OrdinalDate) : Ymd :=
  if hJan: ordinal.day <= Year.lastDayJanuary
    then ⟨ordinal.year, Month.january, ordinal.day, ordinal.hGe, hJan⟩
  else if hFeb: ordinal.day <= ordinal.year.lastDayFebruary
    then ⟨ordinal.year, Month.february, ordinal.day - Year.lastDayJanuary, Nat.sub_pos_of_lt (Nat.gt_of_not_le hJan), Nat.sub_le_of_le_add (ordinal.year.lastDayFebruary_eq ▸ hFeb)⟩
  else if hMar: ordinal.day <= ordinal.year.lastDayMarch
    then ⟨ordinal.year, Month.march, ordinal.day - ordinal.year.lastDayFebruary, Nat.sub_pos_of_lt (Nat.gt_of_not_le hFeb), Nat.sub_le_of_le_add (ordinal.year.lastDayMarch_eq ▸ hMar)⟩
  else if hApr: ordinal.day <= ordinal.year.lastDayApril
    then ⟨ordinal.year, Month.april, ordinal.day - ordinal.year.lastDayMarch, Nat.sub_pos_of_lt (Nat.gt_of_not_le hMar), Nat.sub_le_of_le_add (ordinal.year.lastDayApril_eq ▸ hApr)⟩
  else if hMay: ordinal.day <= ordinal.year.lastDayMay
    then ⟨ordinal.year, Month.may, ordinal.day - ordinal.year.lastDayApril, Nat.sub_pos_of_lt (Nat.gt_of_not_le hApr), Nat.sub_le_of_le_add (ordinal.year.lastDayMay_eq ▸ hMay)⟩
  else if hJun: ordinal.day <= ordinal.year.lastDayJune
    then ⟨ordinal.year, Month.june, ordinal.day - ordinal.year.lastDayMay, Nat.sub_pos_of_lt (Nat.gt_of_not_le hMay), Nat.sub_le_of_le_add (ordinal.year.lastDayJune_eq ▸ hJun)⟩
  else if hJul: ordinal.day <= ordinal.year.lastDayJuly
    then ⟨ordinal.year, Month.july, ordinal.day - ordinal.year.lastDayJune, Nat.sub_pos_of_lt (Nat.gt_of_not_le hJun), Nat.sub_le_of_le_add (ordinal.year.lastDayJuly_eq ▸ hJul)⟩
  else if hAug: ordinal.day <= ordinal.year.lastDayAugust
    then ⟨ordinal.year, Month.august, ordinal.day - ordinal.year.lastDayJuly, Nat.sub_pos_of_lt (Nat.gt_of_not_le hJul), Nat.sub_le_of_le_add (ordinal.year.lastDayAugust_eq ▸ hAug)⟩
  else if hSep: ordinal.day <= ordinal.year.lastDaySeptember
    then ⟨ordinal.year, Month.september, ordinal.day - ordinal.year.lastDayAugust, Nat.sub_pos_of_lt (Nat.gt_of_not_le hAug), Nat.sub_le_of_le_add (ordinal.year.lastDaySeptember_eq ▸ hSep)⟩
  else if hOct: ordinal.day <= ordinal.year.lastDayOctober
    then ⟨ordinal.year, Month.october, ordinal.day - ordinal.year.lastDaySeptember, Nat.sub_pos_of_lt (Nat.gt_of_not_le hSep), Nat.sub_le_of_le_add (ordinal.year.lastDayOctober_eq ▸ hOct)⟩
  else if hNov: ordinal.day <= ordinal.year.lastDayNovember
    then ⟨ordinal.year, Month.november, ordinal.day - ordinal.year.lastDayOctober, Nat.sub_pos_of_lt (Nat.gt_of_not_le hOct), Nat.sub_le_of_le_add (ordinal.year.lastDayNovember_eq ▸ hNov)⟩
  else
    have hr : ordinal.day - Year.lastDayNovember ordinal.year ≤ Month.numDays ordinal.year 12 := by
      apply Nat.sub_le_of_le_add
      have hLe := ordinal.hLe
      simp [Year.numDaysInGregorianYear] at hLe
      simp [Month.numDays]
      refine le_trans hLe ?hLe2
      by_cases hh: ordinal.year.isLeapYear <;> simp [hh]
    ⟨ordinal.year, Month.december, ordinal.day - ordinal.year.lastDayNovember, Nat.sub_pos_of_lt (Nat.gt_of_not_le hNov), hr⟩

def Ymd.toOrdinalDate (ymd : Ymd) : OrdinalDate :=
  match ymd.month with
  | Month.january =>
    have hLe : ymd.day ≤ Year.numDaysInGregorianYear ymd.year := by
      by_cases hLeap : ymd.year.isLeapYear <;> simp_arith [hLeap, Year.numDaysInGregorianYear]
      case pos => exact Nat.le_trans (ymd.dayLe) (Nat.le_trans (ymd.month.numDays_lt_31 ymd.year) (show 31 <= 366 by decide))
      case neg => exact Nat.le_trans (ymd.dayLe) (Nat.le_trans (ymd.month.numDays_lt_31 ymd.year) (show 31 <= 365 by decide))
    ⟨ymd.year, ymd.day, ymd.dayGe, hLe⟩
  | Month.february  =>
    have hLe : ymd.day + Year.lastDayJanuary ≤ ymd.year.numDaysInGregorianYear := by
      by_cases hLeap : ymd.year.isLeapYear <;> simp_arith [hLeap, Year.numDaysInGregorianYear]
      case pos => exact Nat.le_trans (ymd.dayLe) (Nat.le_trans (ymd.month.numDays_lt_31 ymd.year) (show 31 <= 335 by decide))
      case neg => exact Nat.le_trans (ymd.dayLe) (Nat.le_trans (ymd.month.numDays_lt_31 ymd.year) (show 31 <= 334 by decide))
    ⟨ymd.year, ymd.day + Year.lastDayJanuary, Nat.le_trans ymd.dayGe (Nat.le_add_right _ _), hLe⟩
  | Month.march  =>
    have hLe : ymd.day + ymd.year.lastDayFebruary ≤ ymd.year.numDaysInGregorianYear := by
      by_cases hLeap : ymd.year.isLeapYear <;> simp_arith [Year.numDaysInGregorianYear, hLeap, Nat.le_trans (ymd.dayLe) (Nat.le_trans (ymd.month.numDays_lt_31 ymd.year) (show 31 <= 306 by decide))]
    ⟨ymd.year, ymd.day + ymd.year.lastDayFebruary, Nat.le_trans ymd.dayGe (Nat.le_add_right _ _), hLe⟩
  | Month.april  =>
    have hLe : ymd.day + ymd.year.lastDayMarch ≤ ymd.year.numDaysInGregorianYear := by
      by_cases hLeap : ymd.year.isLeapYear <;> simp_arith [Year.numDaysInGregorianYear, hLeap, Nat.le_trans (ymd.dayLe) (Nat.le_trans (ymd.month.numDays_lt_31 ymd.year) (show 31 <= 275 by decide))]
    ⟨ymd.year, ymd.day + ymd.year.lastDayMarch, Nat.le_trans ymd.dayGe (Nat.le_add_right _ _), hLe⟩
  | Month.may =>
    have hLe : ymd.day + ymd.year.lastDayApril ≤ ymd.year.numDaysInGregorianYear := by
      by_cases hLeap : ymd.year.isLeapYear <;> simp_arith [Year.numDaysInGregorianYear, hLeap, Nat.le_trans (ymd.dayLe) (Nat.le_trans (ymd.month.numDays_lt_31 ymd.year) (show 31 <= 245 by decide))]
    ⟨ymd.year, ymd.day + ymd.year.lastDayApril, Nat.le_trans ymd.dayGe (Nat.le_add_right _ _), hLe⟩
  | Month.june =>
    have hLe : ymd.day + ymd.year.lastDayMay ≤ ymd.year.numDaysInGregorianYear := by
      by_cases hLeap : ymd.year.isLeapYear <;> simp_arith [Year.numDaysInGregorianYear, hLeap, Nat.le_trans (ymd.dayLe) (Nat.le_trans (ymd.month.numDays_lt_31 ymd.year) (show 31 <= 214 by decide))]
    ⟨ymd.year, ymd.day + ymd.year.lastDayMay, Nat.le_trans ymd.dayGe (Nat.le_add_right _ _), hLe⟩
  | Month.july  =>
    have hLe : ymd.day + ymd.year.lastDayJune ≤ ymd.year.numDaysInGregorianYear := by
      by_cases hLeap : ymd.year.isLeapYear <;> simp_arith [Year.numDaysInGregorianYear, hLeap, Nat.le_trans (ymd.dayLe) (Nat.le_trans (ymd.month.numDays_lt_31 ymd.year) (show 31 <= 184 by decide))]
    ⟨ymd.year, ymd.day + ymd.year.lastDayJune, Nat.le_trans ymd.dayGe (Nat.le_add_right _ _), hLe⟩
  | Month.august  =>
    have hLe : ymd.day + ymd.year.lastDayJuly ≤ ymd.year.numDaysInGregorianYear := by
      by_cases hLeap : ymd.year.isLeapYear <;> simp_arith [Year.numDaysInGregorianYear, hLeap, Nat.le_trans (ymd.dayLe) (Nat.le_trans (ymd.month.numDays_lt_31 ymd.year) (show 31 <= 153 by decide))]
    ⟨ymd.year, ymd.day + ymd.year.lastDayJuly, Nat.le_trans ymd.dayGe (Nat.le_add_right _ _), hLe⟩
  | Month.september  =>
    have hLe : ymd.day + ymd.year.lastDayAugust ≤ ymd.year.numDaysInGregorianYear := by
      by_cases hLeap : ymd.year.isLeapYear <;> simp_arith [Year.numDaysInGregorianYear, hLeap, Nat.le_trans (ymd.dayLe) (Nat.le_trans (ymd.month.numDays_lt_31 ymd.year) (show 31 <= 122 by decide))]
    ⟨ymd.year, ymd.day + ymd.year.lastDayAugust, Nat.le_trans ymd.dayGe (Nat.le_add_right _ _), hLe⟩
  | Month.october =>
    have hLe : ymd.day + ymd.year.lastDaySeptember ≤ ymd.year.numDaysInGregorianYear := by
      by_cases hLeap : ymd.year.isLeapYear <;> simp_arith [Year.numDaysInGregorianYear, hLeap, Nat.le_trans (ymd.dayLe) (Nat.le_trans (ymd.month.numDays_lt_31 ymd.year) (show 31 <= 92 by decide))]
    ⟨ymd.year, ymd.day + ymd.year.lastDaySeptember, Nat.le_trans ymd.dayGe (Nat.le_add_right _ _), hLe⟩
  | Month.november =>
    have hLe : ymd.day + ymd.year.lastDayOctober ≤ ymd.year.numDaysInGregorianYear := by
      by_cases hLeap : ymd.year.isLeapYear <;> simp_arith [Year.numDaysInGregorianYear, hLeap, Nat.le_trans (ymd.dayLe) (Nat.le_trans (ymd.month.numDays_lt_31 ymd.year) (show 31 <= 61 by decide))]
    ⟨ymd.year, ymd.day + ymd.year.lastDayOctober, Nat.le_trans ymd.dayGe (Nat.le_add_right _ _), hLe⟩
  | Month.december =>
    have hLe : ymd.day + ymd.year.lastDayNovember ≤ ymd.year.numDaysInGregorianYear := by
    by_cases hLeap : ymd.year.isLeapYear <;> simp_arith [hLeap, Year.numDaysInGregorianYear, Month.numDays_lt_31, Nat.le_trans ymd.dayLe (ymd.month.numDays_lt_31 ymd.year)]
    ⟨ymd.year, ymd.day + ymd.year.lastDayNovember, Nat.le_trans ymd.dayGe (Nat.le_add_right _ _), hLe⟩

@[simp] theorem Ymd.toOrdinalDate_year_same (ymd : Ymd) : (ymd.toOrdinalDate).year = ymd.year := by
  simp [Ymd.toOrdinalDate]; cases ymd.month <;> simp

def ScalarDate.toOrdinalDateYearIsLastDayOfLeapYear (d : ScalarDate) : Prop :=
  let dayPred := d.day - 1
  let hundredGroupsDays := dayPred.fmod 146097
  let fourGroupDays := hundredGroupsDays % 36524
  let singlesGroupDays := fourGroupDays % 1461
  let num100YearGroups := hundredGroupsDays / 36524
  let numSingleYears : Int := singlesGroupDays / 365
  num100YearGroups = 4 ∨ numSingleYears = 4

def ScalarDate.toOrdinalDateYear (d : ScalarDate) : Year :=
  /- We substract one since the measure is 1-based. -/
  let dayPred := d.day - 1
  /- So days < 146097; the days in the 100 groups. -/
  let hundredGroupsDays := dayPred.fmod 146097
  /- days < 36524; the days in the 4 groups -/
  let fourGroupDays := hundredGroupsDays % 36524
  /- days < 1461; the days in the singles -/
  let singlesGroupDays := fourGroupDays % 1461
  /- The number of 400 groups -/
  let num400YearGroups := dayPred.fdiv 146097
  /- The number of 100 groups -/
  let num100YearGroups := hundredGroupsDays / 36524
  /- The number of 4 groups -/
  let num4YearGroups := fourGroupDays / 1461
  /- Number of single years -/
  let numSingleYears : Int := singlesGroupDays / 365
  /- Years from the 400 groups -/
  let yearsFrom400Groups := num400YearGroups * 400
  /- Years from the 100 groups -/
  let yearsFrom100Groups := num100YearGroups * 100
  /- Years from the 4 groups -/
  let yearsFrom4Groups := num4YearGroups * 4
  let isLastDayOfLeapYear := num100YearGroups = 4 ∨ numSingleYears = 4
  Year.mk (yearsFrom400Groups + yearsFrom100Groups + yearsFrom4Groups + numSingleYears + (if isLastDayOfLeapYear then 0 else 1))

theorem ScalarDate.toOrdinalDateYear_leap (d : ScalarDate) :
  d.toOrdinalDateYearIsLastDayOfLeapYear → d.toOrdinalDateYear.isLeapYear := by
  simp only [ScalarDate.toOrdinalDateYearIsLastDayOfLeapYear, Year.isLeapYear, ScalarDate.toOrdinalDateYear]
  refine Or.rec ?l ?r
  case l =>
    intro h
    have h_eq : (Int.fmod (d.day - 1) 146097) = 146096 := by
      have hle_left : 146096 ≤ Int.fmod (d.day - 1) 146097 :=
         @Int.mul_le_of_le_div 4 (b := Int.fmod (d.day - 1) 146097) 36524 pos36524 (le_of_eq h.symm)
      exact le_antisymm (Int.le_of_add_le_add_right (Int.fmod_lt _ pos146097)) hle_left
    apply Or.inr
    simp [h_eq, h4, h1, h100, hdiv']
  case r =>
    intro h
    have h_eq : (((Int.fmod (d.day - 1) 146097) % 36524) % 1461) = 1460 := by
      have hle_left : 1460 ≤ (((Int.fmod (d.day - 1) 146097) % 36524) % 1461) :=
        @Int.mul_le_of_le_div 4 ((((Int.fmod (d.day - 1) 146097) % 36524) % 1461)) 365 (pos365) (le_of_eq h.symm)
      refine le_antisymm (Int.le_of_add_le_add_right (Int.mod_lt_of_pos _ pos1461)) hle_left
    apply Or.inl
    simp [h_eq, h100, h4, h1, hdiv, hdiv']
    refine And.intro ?ll ?rr
    case ll =>
      have hleft : (Int.fdiv (d.day - 1) 146097) * 400 = ((Int.fdiv (d.day - 1) 146097) * 100) * 4 := by
        rw [mul_assoc _ 100 4]; simp [show (100 : Int) * 4 = 400 by decide]
      have hright : ((Int.fmod (d.day - 1) 146097) / 36524) * 100 = (((Int.fmod (d.day - 1) 146097) / 36524) * 25) * 4 := by
        rw [mul_assoc _ 25 4]; simp [show (25 : Int) * 4 = 100 by decide]
      simp [hleft, hright, (Int.distrib_right _ _ _).symm]
    case rr =>
      have hleft : (Int.fdiv (d.day - 1) 146097) * 400 = ((Int.fdiv (d.day - 1) 146097) * 4) * 100 := by
        rw [mul_assoc _ 4 100]; simp [show (4 : Int) * 100 = 400 by decide]
      simp [hleft, (Int.distrib_right _ _ _).symm]
      rw [add_assoc]
      rw [add_comm (((Int.fdiv (d.day - 1) 146097 * 4) + (Int.fmod (d.day - 1) 146097 / 36524)) * 100) _]
      simp
      intro hf
      have hdiv_nonneg : 0 ≤ (Int.fmod (d.day - 1) 146097) % 36524 / 1461 :=
        Int.div_nonneg (Int.mod_nonneg _ (by decide)) (le_of_lt pos1461)
      have hlt_100 : ((Int.fmod (d.day - 1) 146097) % 36524) / 1461 * 4 + 4 < 100 := by
        apply Int.add_lt_of_lt_sub_right
        rw [(show (100 : Int) - 4 = 96 by decide)]
        apply Int.mul_lt_of_lt_div pos4
        rw [(show (96 : Int) / 4 = 24 by decide)]
        apply Int.div_lt_of_lt_mul pos1461
        rw [(show (24 : Int) * 1461 = 35064 by decide)]
        apply lt_of_not_ge
        intro hf
        cases lt_or_eq_of_le hf with
        | inl hl =>
          have lt_upper : (Int.fmod (d.day - 1) 146097) % 36524 < 36524 := Int.mod_lt_of_pos _ pos36524
          have h_fac_24 :  (1461 : Int) * 24 = 35064 := by decide
          rw [<- h_fac_24] at hl hf
          have h_lt25 : ((Int.fmod (d.day - 1) 146097) % 36524) < (1461 : Int) * 25 := by
            have h_fac_25 : (1461 : Int) * 25 = 36525 := by decide
            rw [h_fac_25]; exact lt_trans lt_upper (by decide)
          have h_hard : ((Int.fmod (d.day - 1) 146097) % 36524) / 1461 = 24 := by
            exact Int.div_pigeonhole (Int.mod_nonneg _ (by decide)) (by decide) (by decide) hl h_lt25
          have h_from := ((@Int.div_mod_unique ((Int.fmod (d.day - 1) 146097) % 36524) 1461 1460 24 (show (0 : Int) < 1461 by decide)).mp (And.intro h_hard h_eq)).left
          rw [<- h_from] at lt_upper
          simp at lt_upper
          done
        | inr hr =>
          rw [<- hr] at h_eq
          have hne : Int.fmod 35064 1461 ≠ 1460 := by decide
          exact False.elim (hne h_eq)
      have hmul4 : 0 <=(Int.fmod (d.day - 1) 146097) % 36524 / 1461 * 4 :=
        Int.mul_nonneg hdiv_nonneg (le_of_lt pos4)
      have hout :
        ((Int.fmod (d.day - 1) 146097) % 36524 / 1461 * 4 + 4) % 100 =
        (Int.fmod (d.day - 1) 146097) % 36524 / 1461 * 4 + 4 := by
        refine Int.mod_eq_of_lt ?x hlt_100
        exact Int.add_right_nonneg (Int.mul_nonneg hdiv_nonneg (le_of_lt pos4)) (le_of_lt pos4)
      rw [hout] at hf
      have h_ne_zero : ((Int.fmod (d.day - 1) 146097) % 36524 / 1461 * 4) + 4 ≠ 0 := Int.add_pos_ne_zero_of_nonneg hmul4 (by decide)
      exact False.elim (h_ne_zero hf)
      done

/-
Taken from 2.21 and 2.22 in the Gregorian Calendar chapter of Calendrical Calculations.
We can switch to using `/` and `%` instead of `fmod` and `fdiv` once we've uesd `fmod`
the first time, because `fmod` with a nonnegative modulus alwayd produces a positive remainder.
-/
def ScalarDate.toOrdinalDate (d : ScalarDate) : OrdinalDate :=
  /- We substract one since the measure is 1-based. -/
  let dayPred := d.day - 1
  /- So days < 146097; the days in the 100 groups. -/
  let hundredGroupsDays := dayPred.fmod 146097
  /- days < 36524; the days in the 4 groups -/
  let fourGroupDays := hundredGroupsDays % 36524
  /- days < 1461; the days in the singles -/
  let singlesGroupDays := fourGroupDays % 1461
  /- The number of 400 groups -/
  let num400YearGroups := dayPred.fdiv 146097
  /- The number of 100 groups -/
  let num100YearGroups := hundredGroupsDays / 36524
  /- The number of 4 groups -/
  let num4YearGroups := fourGroupDays / 1461
  /- Number of single years -/
  let numSingleYears : Int := singlesGroupDays / 365
  /- Years from the 400 groups -/
  let yearsFrom400Groups := num400YearGroups * 400
  /- Years from the 100 groups -/
  let yearsFrom100Groups := num100YearGroups * 100
  /- Years from the 4 groups -/
  let yearsFrom4Groups := num4YearGroups * 4
  let isLastDayOfLeapYear := num100YearGroups = 4 ∨ numSingleYears = 4
  /- Add one iff it's not the last day of a leap year. If it is the last day of a leap year, withhold that additional year. -/
  let year := Year.mk (yearsFrom400Groups + yearsFrom100Groups + yearsFrom4Groups + numSingleYears + (if isLastDayOfLeapYear then 0 else 1))
  let day := if isLastDayOfLeapYear then 366 else ((singlesGroupDays.fmod 365) + 1)

  have h_day_ge_one : 1 <= day.toNat := by
    by_cases hleap : isLastDayOfLeapYear <;> simp [hleap]
    case neg =>
      refine toOrdinalDate_helper1 ?hle
      exact Int.le_add_of_sub_right_le (Int.mod_nonneg _ (ne_of_lt pos365).symm)
  have hle' : Int.toNat day ≤ Year.numDaysInGregorianYear year := by
    by_cases hleap : isLastDayOfLeapYear <;> simp [hleap]
    -- If it is the last day of a leap year
    case pos =>
      have h_is_leap := ScalarDate.toOrdinalDateYear_leap d hleap
      simp_all [toOrdinalDateYear, hleap, Year.numDaysInGregorianYear]
      done
    -- If it's not the last day of a leap year.
    case neg =>
      generalize hday : (((Int.fmod (d.day - 1) 146097) % 36524) % 1461) % 365  = day
      generalize hy' :
        Year.mk
          (Int.fdiv (d.day - 1) 146097 * 400
          + (Int.fmod (d.day - 1) 146097) / 36524 * 100
          + ((Int.fmod (d.day - 1) 146097) % 36524) / 1461 * 4
          + (((Int.fmod (d.day - 1) 146097) % 36524) % 1461) / 365 + 1) = y'
      have hd' : day < 365 :=
        hday ▸ (Int.mod_lt_of_pos (((Int.fmod (d.day - 1) 146097) % 36524) % 1461) pos365)
      have hpos : 0 <= day :=
        hday ▸ (Int.mod_nonneg (((Int.fmod (d.day - 1) 146097) % 36524) % 1461) (ne_of_lt pos365).symm)
      exact Int.toNat_le_of_le_of_nonneg (Int.add_nonneg hpos (by decide)) (le_trans (hd' : (day + 1) <= 365) y'.num_days_in_gregorian_year_ge_365)
      done
  ⟨year, day.toNat, h_day_ge_one, hle'⟩

/-
Derived from 2.17 in Calendrical Calculations.
-/
def OrdinalDate.toScalarDate : OrdinalDate → ScalarDate
| ⟨⟨year⟩, ordinalDay, _, _⟩ =>
  let yearPred := year - 1
  -- number of 400 groups
  let fourHundredGroups := yearPred.fdiv 400
  -- number of 100 groups; 0-3
  let oneHundredGroups := (yearPred.fmod 400) / 100
  -- number of 4 groups; 0-24
  let fourGroups := (yearPred.fmod 100) / 4
  -- number of single years; 0, 1, 2, or 3
  let singles := yearPred.fmod 4
  let daysFrom400YearGroups := fourHundredGroups * 146097
  let daysFrom100YearGroups := oneHundredGroups * 36524
  let daysFrom4YearGroups := fourGroups * 1461
  let daysFrom1YearGroups := singles * 365
  ⟨daysFrom400YearGroups + daysFrom100YearGroups + daysFrom4YearGroups + daysFrom1YearGroups + ordinalDay⟩

def Ymd.toScalarDate (ymd : Ymd) : ScalarDate := ymd.toOrdinalDate.toScalarDate

def ScalarDate.toYmd (date : ScalarDate) : Ymd := date.toOrdinalDate.toYmd

def ScalarDate.year (d : ScalarDate) : Year := d.toYmd.year

def ScalarDate.fromYmd
  (y : Year)
  (m : Month)
  (d : Nat)
  (dayGe : d >= 1 := by decide)
  (dayLe : d <= m.numDays y := by decide) : ScalarDate :=
  (Ymd.mk y m d dayGe dayLe).toScalarDate


inductive Vector (α : Type u): Nat → Type u where
| nil : Vector α 0
| cons (head : α) (tail : Vector α n) : Vector α (n+1)

theorem Nat.lt_of_add_lt_add_right {a b c : Nat} (h : a + b < c + b) : a < c := sorry

def Vector.nth : ∀{n}, Vector α n → Fin n → α
| n+1, Vector.cons x xs, ⟨  0, _⟩ => x
| n+1, Vector.cons x xs, ⟨k+1, h⟩ => xs.nth ⟨k, Nat.lt_of_add_lt_add_right h⟩


/-
Copyright (c) 2020 Robert Y. Lewis. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Robert Y. Lewis
-/

import data.rat.meta_defs
import tactic.norm_num
import data.tree
import meta.expr

/-!
# A tactic for canceling numeric denominators

This file defines tactics that cancel numeric denominators from field expressions.

As an example, we want to transform a comparison `5*(a/3 + b/4) < c/3` into the equivalent
`5*(4*a + 3*b) < 4*c`.

## Implementation notes

The tooling here was originally written for `linarith`, not intended as an interactive tactic.
The interactive version has been split off because it is sometimes convenient to use on its own.
There are likely some rough edges to it.

Improving this tactic would be a good project for someone interested in learning tactic programming.
-/

namespace cancel_factors

/-! ### Lemmas used in the procedure -/

lemma mul_subst {α} [comm_ring α] {n1 n2 k e1 e2 t1 t2 : α} (h1 : n1 * e1 = t1) (h2 : n2 * e2 = t2)
     (h3 : n1*n2 = k) : k * (e1 * e2) = t1 * t2 :=
by rw [←h3, mul_comm n1, mul_assoc n2, ←mul_assoc n1, h1, ←mul_assoc n2, mul_comm n2, mul_assoc, h2]

lemma div_subst {α} [field α] {n1 n2 k e1 e2 t1 : α} (h1 : n1 * e1 = t1) (h2 : n2 / e2 = 1)
   (h3 : n1*n2 = k) : k * (e1 / e2) = t1 :=
by rw [←h3, mul_assoc, mul_div_left_comm, h2, ←mul_assoc, h1, mul_comm, one_mul]

lemma cancel_factors_eq_div {α} [field α] {n e e' : α} (h : n*e = e') (h2 : n ≠ 0) :
  e = e' / n :=
eq_div_of_mul_eq h2 $ by rwa mul_comm at h

lemma add_subst {α} [ring α] {n e1 e2 t1 t2 : α} (h1 : n * e1 = t1) (h2 : n * e2 = t2) :
      n * (e1 + e2) = t1 + t2 := by simp [left_distrib, *]

lemma sub_subst {α} [ring α] {n e1 e2 t1 t2 : α} (h1 : n * e1 = t1) (h2 : n * e2 = t2) :
      n * (e1 - e2) = t1 - t2 := by simp [left_distrib, *, sub_eq_add_neg]

lemma neg_subst {α} [ring α] {n e t : α} (h1 : n * e = t) : n * (-e) = -t := by simp *

lemma cancel_factors_lt {α} [linear_ordered_field α] {a b ad bd a' b' gcd : α} (ha : ad*a = a')
  (hb : bd*b = b') (had : 0 < ad) (hbd : 0 < bd) (hgcd : 0 < gcd) :
  a < b = ((1/gcd)*(bd*a') < (1/gcd)*(ad*b')) :=
begin
  rw [mul_lt_mul_left, ←ha, ←hb, ←mul_assoc, ←mul_assoc, mul_comm bd, mul_lt_mul_left],
  exact mul_pos had hbd,
  exact one_div_pos.2 hgcd
end

lemma cancel_factors_le {α} [linear_ordered_field α] {a b ad bd a' b' gcd : α} (ha : ad*a = a')
  (hb : bd*b = b') (had : 0 < ad) (hbd : 0 < bd) (hgcd : 0 < gcd)  :
  a ≤ b = ((1/gcd)*(bd*a') ≤ (1/gcd)*(ad*b')) :=
begin
  rw [mul_le_mul_left, ←ha, ←hb, ←mul_assoc, ←mul_assoc, mul_comm bd, mul_le_mul_left],
  exact mul_pos had hbd,
  exact one_div_pos.2 hgcd
end

lemma cancel_factors_eq {α} [linear_ordered_field α] {a b ad bd a' b' gcd : α} (ha : ad*a = a')
  (hb : bd*b = b') (had : 0 < ad) (hbd : 0 < bd) (hgcd : 0 < gcd) :
  a = b = ((1/gcd)*(bd*a') = (1/gcd)*(ad*b')) :=
begin
  rw [←ha, ←hb, ←mul_assoc bd, ←mul_assoc ad, mul_comm bd],
  ext, split,
  { rintro rfl, refl },
  { intro h,
    simp only [←mul_assoc] at h,
    refine mul_left_cancel₀ (mul_ne_zero _ _) h,
    apply mul_ne_zero, apply div_ne_zero,
    all_goals {apply ne_of_gt; assumption <|> exact zero_lt_one}}
end

open tactic expr

/-! ### Computing cancelation factors -/

open tree

/--
`find_cancel_factor e` produces a natural number `n`, such that multiplying `e` by `n` will
be able to cancel all the numeric denominators in `e`. The returned `tree` describes how to
distribute the value `n` over products inside `e`.
-/
meta def find_cancel_factor : expr → ℕ × tree ℕ
| `(%%e1 + %%e2) :=
  let (v1, t1) := find_cancel_factor e1, (v2, t2) := find_cancel_factor e2, lcm := v1.lcm v2 in
  (lcm, node lcm t1 t2)
| `(%%e1 - %%e2) :=
  let (v1, t1) := find_cancel_factor e1, (v2, t2) := find_cancel_factor e2, lcm := v1.lcm v2 in
  (lcm, node lcm t1 t2)
| `(%%e1 * %%e2) :=
  let (v1, t1) := find_cancel_factor e1, (v2, t2) := find_cancel_factor e2, pd := v1*v2 in
  (pd, node pd t1 t2)
| `(%%e1 / %%e2) :=
  match e2.to_nonneg_rat with
  | some q := let (v1, t1) := find_cancel_factor e1, n := v1.lcm q.num.nat_abs in
    (n, node n t1 (node q.num.nat_abs tree.nil tree.nil))
  | none := (1, node 1 tree.nil tree.nil)
  end
| `(-%%e) := find_cancel_factor e
| _ := (1, node 1 tree.nil tree.nil)

/--
`mk_prod_prf n tr e` produces a proof of `n*e = e'`, where numeric denominators have been
canceled in `e'`, distributing `n` proportionally according to `tr`.
-/
meta def mk_prod_prf : ℕ → tree ℕ → expr → tactic expr
| v (node _ lhs rhs) `(%%e1 + %%e2) :=
  do v1 ← mk_prod_prf v lhs e1, v2 ← mk_prod_prf v rhs e2, mk_app ``add_subst [v1, v2]
| v (node _ lhs rhs) `(%%e1 - %%e2) :=
  do v1 ← mk_prod_prf v lhs e1, v2 ← mk_prod_prf v rhs e2, mk_app ``sub_subst [v1, v2]
| v (node n lhs@(node ln _ _) rhs) `(%%e1 * %%e2) :=
  do tp ← infer_type e1, v1 ← mk_prod_prf ln lhs e1, v2 ← mk_prod_prf (v/ln) rhs e2,
     ln' ← tp.of_nat ln, vln' ← tp.of_nat (v/ln), v' ← tp.of_nat v,
     ntp ← to_expr ``(%%ln' * %%vln' = %%v'),
     (_, npf) ← solve_aux ntp `[norm_num, done],
     mk_app ``mul_subst [v1, v2, npf]
| v (node n lhs rhs@(node rn _ _)) `(%%e1 / %%e2) :=
  do tp ← infer_type e1, v1 ← mk_prod_prf (v/rn) lhs e1,
     rn' ← tp.of_nat rn, vrn' ← tp.of_nat (v/rn), n' ← tp.of_nat n, v' ← tp.of_nat v,
     ntp ← to_expr ``(%%rn' / %%e2 = 1),
     (_, npf) ← solve_aux ntp `[norm_num, done],
     ntp2 ← to_expr ``(%%vrn' * %%n' = %%v'),
     (_, npf2) ← solve_aux ntp2 `[norm_num, done],
     mk_app ``div_subst [v1, npf, npf2]
| v t `(-%%e) := do v' ← mk_prod_prf v t e, mk_app ``neg_subst [v']
| v _ e :=
  do tp ← infer_type e,
     v' ← tp.of_nat v,
     e' ← to_expr ``(%%v' * %%e),
     mk_app `eq.refl [e']

/--
Given `e`, a term with rational division, produces a natural number `n` and a proof of `n*e = e'`,
where `e'` has no division. Assumes "well-behaved" division.
-/
meta def derive (e : expr) : tactic (ℕ × expr) :=
let (n, t) := find_cancel_factor e in
prod.mk n <$> mk_prod_prf n t e <|>
  fail!"cancel_factors.derive failed to normalize {e}. Are you sure this is well-behaved division?"

/--
Given `e`, a term with rational divison, produces a natural number `n` and a proof of `e = e' / n`,
where `e'` has no divison. Assumes "well-behaved" division.
-/
meta def derive_div (e : expr) : tactic (ℕ × expr) :=
do (n, p) ← derive e,
   tp ← infer_type e,
   n' ← tp.of_nat n, tgt ← to_expr ``(%%n' ≠ 0),
   (_, pn) ← solve_aux tgt `[norm_num, done],
   prod.mk n <$> mk_mapp ``cancel_factors_eq_div [none, none, n', none, none, p, pn]

/--
`find_comp_lemma e` arranges `e` in the form `lhs R rhs`, where `R ∈ {<, ≤, =}`, and returns
`lhs`, `rhs`, and the `cancel_factors` lemma corresponding to `R`.
-/
meta def find_comp_lemma : expr → option (expr × expr × name)
| `(%%a < %%b) := (a, b, ``cancel_factors_lt)
| `(%%a ≤ %%b) := (a, b, ``cancel_factors_le)
| `(%%a = %%b) := (a, b, ``cancel_factors_eq)
| `(%%a ≥ %%b) := (b, a, ``cancel_factors_le)
| `(%%a > %%b) := (b, a, ``cancel_factors_lt)
| _ := none

/--
`cancel_denominators_in_type h` assumes that `h` is of the form `lhs R rhs`,
where `R ∈ {<, ≤, =, ≥, >}`.
It produces an expression `h'` of the form `lhs' R rhs'` and a proof that `h = h'`.
Numeric denominators have been canceled in `lhs'` and `rhs'`.
-/
meta def cancel_denominators_in_type (h : expr) : tactic (expr × expr) :=
do some (lhs, rhs, lem) ← return $ find_comp_lemma h | fail "cannot kill factors",
   (al, lhs_p) ← derive lhs,
   (ar, rhs_p) ← derive rhs,
   let gcd := al.gcd ar,
   tp ← infer_type lhs,
   al ← tp.of_nat al,
   ar ← tp.of_nat ar,
   gcd ← tp.of_nat gcd,
   al_pos ← to_expr ``(0 < %%al),
   ar_pos ← to_expr ``(0 < %%ar),
   gcd_pos ← to_expr ``(0 < %%gcd),
   (_, al_pos) ← solve_aux al_pos `[norm_num, done],
   (_, ar_pos) ← solve_aux ar_pos `[norm_num, done],
   (_, gcd_pos) ← solve_aux gcd_pos `[norm_num, done],
   pf ← mk_app lem [lhs_p, rhs_p, al_pos, ar_pos, gcd_pos],
   pf_tp ← infer_type pf,
   return ((find_comp_lemma pf_tp).elim default (prod.fst ∘ prod.snd), pf)

end cancel_factors

/-! ### Interactive version -/

setup_tactic_parser
open tactic expr cancel_factors

/--
`cancel_denoms` attempts to remove numerals from the denominators of fractions.
It works on propositions that are field-valued inequalities.

```lean
variables {α : Type} [linear_ordered_field α] (a b c : α)

example (h : a / 5 + b / 4 < c) : 4*a + 5*b < 20*c :=
begin
  cancel_denoms at h,
  exact h
end

example (h : a > 0) : a / 5 > 0 :=
begin
  cancel_denoms,
  exact h
end
```
-/
meta def tactic.interactive.cancel_denoms (l : parse location) : tactic unit :=
do locs ← l.get_locals,
   tactic.replace_at cancel_denominators_in_type locs l.include_goal >>= guardb
     <|> fail "failed to cancel any denominators",
   tactic.interactive.norm_num [simp_arg_type.symm_expr ``(mul_assoc)] l

add_tactic_doc
{ name := "cancel_denoms",
  category := doc_category.tactic,
  decl_names := [`tactic.interactive.cancel_denoms],
  tags := ["simplification"] }


def f : Nat → Bool → Nat
  | 0,  true  => 1
  | 0,  false => 2
  | 1,  true => 3
  | 1,  _ => 4
  | x+2, true => f x true
  | x+2, b => f x (not b)

macro "urfl" : tactic => `(tactic| set_option smartUnfolding false in rfl)

theorem f_main_eq : f x b = f.match_1 (fun _ _ => Nat) x b (fun _ => 1) (fun _ => 2) (fun _ => 3) (fun _ => 4) (fun x => f x true) (fun x b => f x (not b)) := by
  split <;> (first | rfl | (conv => lhs; delta f; whnf; simp_match); try urfl)

#check @f_main_eq

def g : List Nat → List Nat → Nat
  | [],         y::ys => y
  | [],         ys    => 0
  | x1::x2::xs, ys    => g xs ys
  | x::xs,      y::ys => g xs ys + y
  | x::xs,      []    => g xs []

theorem g_main_eq (xs ys : List Nat) : g xs ys = g.match_1 (fun _ _ => Nat) xs ys (fun y ys => y) (fun _ => 0) (fun _ _ xs ys => g xs ys) (fun _ xs y ys => g xs ys + y) (fun _ xs => g xs []) := by
  split <;> (first | rfl | (conv => lhs; delta g; whnf; simp_match); try urfl)

#check @g_main_eq

def foo (xs : List Nat) : List Nat :=
  match xs with
  | []    => []
  | x::xs =>
    let y := 2 * x;
    match xs with
    | []    => []
    | x::xs => (y + x) :: foo xs

theorem foo_main_eq (xs : List Nat) : foo xs = foo.match_1 (fun _ => List Nat) xs (fun _ => []) (fun x xs => let y := 2*x; foo.match_1 (fun _ => List Nat) xs (fun _ => []) (fun x xs => (y + x)::foo xs)) := by
  split
  · rfl
  · split
    · rfl
    · rfl

#check @foo_main_eq


/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Yury Kudryashov
-/
import measure_theory.ae_measurable_sequence
import analysis.complex.basic
import analysis.normed_space.finite_dimension
import topology.G_delta
import measure_theory.arithmetic

/-!
# Borel (measurable) space

## Main definitions

* `borel α` : the least `σ`-algebra that contains all open sets;
* `class borel_space` : a space with `topological_space` and `measurable_space` structures
  such that `‹measurable_space α› = borel α`;
* `class opens_measurable_space` : a space with `topological_space` and `measurable_space`
  structures such that all open sets are measurable; equivalently, `borel α ≤ ‹measurable_space α›`.
* `borel_space` instances on `empty`, `unit`, `bool`, `nat`, `int`, `rat`;
* `measurable` and `borel_space` instances on `ℝ`, `ℝ≥0`, `ℝ≥0∞`.
* A measure is `regular` if it is finite on compact sets, inner regular and outer regular.

## Main statements

* `is_open.measurable_set`, `is_closed.measurable_set`: open and closed sets are measurable;
* `continuous.measurable` : a continuous function is measurable;
* `continuous.measurable2` : if `f : α → β` and `g : α → γ` are measurable and `op : β × γ → δ`
  is continuous, then `λ x, op (f x, g y)` is measurable;
* `measurable.add` etc : dot notation for arithmetic operations on `measurable` predicates,
  and similarly for `dist` and `edist`;
* `ae_measurable.add` : similar dot notation for almost everywhere measurable functions;
* `measurable.ennreal*` : special cases for arithmetic operations on `ℝ≥0∞`.
-/

noncomputable theory

open classical set filter measure_theory
open_locale classical big_operators topological_space nnreal ennreal

universes u v w x y
variables {α β γ γ₂ δ : Type*} {ι : Sort y} {s t u : set α}

open measurable_space topological_space

/-- `measurable_space` structure generated by `topological_space`. -/
def borel (α : Type u) [topological_space α] : measurable_space α :=
generate_from {s : set α | is_open s}

lemma borel_eq_top_of_discrete [topological_space α] [discrete_topology α] :
  borel α = ⊤ :=
top_le_iff.1 $ λ s hs, generate_measurable.basic s (is_open_discrete s)

lemma borel_eq_top_of_encodable [topological_space α] [t1_space α] [encodable α] :
  borel α = ⊤ :=
begin
  refine (top_le_iff.1 $ λ s hs, bUnion_of_singleton s ▸ _),
  apply measurable_set.bUnion s.countable_encodable,
  intros x hx,
  apply measurable_set.of_compl,
  apply generate_measurable.basic,
  exact is_closed_singleton.is_open_compl
end

lemma borel_eq_generate_from_of_subbasis {s : set (set α)}
  [t : topological_space α] [second_countable_topology α] (hs : t = generate_from s) :
  borel α = generate_from s :=
le_antisymm
  (generate_from_le $ assume u (hu : t.is_open u),
    begin
      rw [hs] at hu,
      induction hu,
      case generate_open.basic : u hu
      { exact generate_measurable.basic u hu },
      case generate_open.univ
      { exact @measurable_set.univ α (generate_from s) },
      case generate_open.inter : s₁ s₂ _ _ hs₁ hs₂
      { exact @measurable_set.inter α (generate_from s) _ _ hs₁ hs₂ },
      case generate_open.sUnion : f hf ih {
        rcases is_open_sUnion_countable f (by rwa hs) with ⟨v, hv, vf, vu⟩,
        rw ← vu,
        exact @measurable_set.sUnion α (generate_from s) _ hv
          (λ x xv, ih _ (vf xv)) }
    end)
  (generate_from_le $ assume u hu, generate_measurable.basic _ $
    show t.is_open u, by rw [hs]; exact generate_open.basic _ hu)

lemma topological_space.is_topological_basis.borel_eq_generate_from [topological_space α]
  [second_countable_topology α] {s : set (set α)} (hs : is_topological_basis s) :
  borel α = generate_from s :=
borel_eq_generate_from_of_subbasis hs.eq_generate_from

lemma is_pi_system_is_open [topological_space α] : is_pi_system (is_open : set α → Prop) :=
λ s t hs ht hst, is_open_inter hs ht

lemma borel_eq_generate_from_is_closed [topological_space α] :
  borel α = generate_from {s | is_closed s} :=
le_antisymm
  (generate_from_le $ λ t ht, @measurable_set.of_compl α _ (generate_from {s | is_closed s})
    (generate_measurable.basic _ $ is_closed_compl_iff.2 ht))
  (generate_from_le $ λ t ht, @measurable_set.of_compl α _ (borel α)
    (generate_measurable.basic _ $ is_open_compl_iff.2 ht))

section order_topology

variable (α)
variables [topological_space α] [second_countable_topology α] [linear_order α] [order_topology α]

lemma borel_eq_generate_Iio : borel α = generate_from (range Iio) :=
begin
  refine le_antisymm _ (generate_from_le _),
  { rw borel_eq_generate_from_of_subbasis (@order_topology.topology_eq_generate_intervals α _ _ _),
    letI : measurable_space α := measurable_space.generate_from (range Iio),
    have H : ∀ a : α, measurable_set (Iio a) := λ a, generate_measurable.basic _ ⟨_, rfl⟩,
    refine generate_from_le _, rintro _ ⟨a, rfl | rfl⟩; [skip, apply H],
    by_cases h : ∃ a', ∀ b, a < b ↔ a' ≤ b,
    { rcases h with ⟨a', ha'⟩,
      rw (_ : Ioi a = (Iio a')ᶜ), { exact (H _).compl },
      simp [set.ext_iff, ha'] },
    { rcases is_open_Union_countable
        (λ a' : {a' : α // a < a'}, {b | a'.1 < b})
        (λ a', is_open_lt' _) with ⟨v, ⟨hv⟩, vu⟩,
      simp [set.ext_iff] at vu,
      have : Ioi a = ⋃ x : v, (Iio x.1.1)ᶜ,
      { simp [set.ext_iff],
        refine λ x, ⟨λ ax, _, λ ⟨a', ⟨h, av⟩, ax⟩, lt_of_lt_of_le h ax⟩,
        rcases (vu x).2 _ with ⟨a', h₁, h₂⟩,
        { exact ⟨a', h₁, le_of_lt h₂⟩ },
        refine not_imp_comm.1 (λ h, _) h,
        exact ⟨x, λ b, ⟨λ ab, le_of_not_lt (λ h', h ⟨b, ab, h'⟩),
          lt_of_lt_of_le ax⟩⟩ },
      rw this, resetI,
      apply measurable_set.Union,
      exact λ _, (H _).compl } },
  { rw forall_range_iff,
    intro a,
    exact generate_measurable.basic _ is_open_Iio }
end

lemma borel_eq_generate_Ioi : borel α = generate_from (range Ioi) :=
@borel_eq_generate_Iio (order_dual α) _ (by apply_instance : second_countable_topology α) _ _

end order_topology

lemma borel_comap {f : α → β} {t : topological_space β} :
  @borel α (t.induced f) = (@borel β t).comap f :=
comap_generate_from.symm

lemma continuous.borel_measurable [topological_space α] [topological_space β]
  {f : α → β} (hf : continuous f) :
  @measurable α β (borel α) (borel β) f :=
measurable.of_le_map $ generate_from_le $
  λ s hs, generate_measurable.basic (f ⁻¹' s) (hs.preimage hf)

/-- A space with `measurable_space` and `topological_space` structures such that
all open sets are measurable. -/
class opens_measurable_space (α : Type*) [topological_space α] [h : measurable_space α] : Prop :=
(borel_le : borel α ≤ h)

/-- A space with `measurable_space` and `topological_space` structures such that
the `σ`-algebra of measurable sets is exactly the `σ`-algebra generated by open sets. -/
class borel_space (α : Type*) [topological_space α] [measurable_space α] : Prop :=
(measurable_eq : ‹measurable_space α› = borel α)

/-- In a `borel_space` all open sets are measurable. -/
@[priority 100]
instance borel_space.opens_measurable {α : Type*} [topological_space α] [measurable_space α]
  [borel_space α] : opens_measurable_space α :=
⟨ge_of_eq $ borel_space.measurable_eq⟩

instance subtype.borel_space {α : Type*} [topological_space α] [measurable_space α]
  [hα : borel_space α] (s : set α) :
  borel_space s :=
⟨by { rw [hα.1, subtype.measurable_space, ← borel_comap], refl }⟩

instance subtype.opens_measurable_space {α : Type*} [topological_space α] [measurable_space α]
  [h : opens_measurable_space α] (s : set α) :
  opens_measurable_space s :=
⟨by { rw [borel_comap], exact comap_mono h.1 }⟩

section
variables [topological_space α] [measurable_space α] [opens_measurable_space α]
   [topological_space β] [measurable_space β] [opens_measurable_space β]
   [topological_space γ] [measurable_space γ] [borel_space γ]
   [topological_space γ₂] [measurable_space γ₂] [borel_space γ₂]
   [measurable_space δ]

lemma is_open.measurable_set (h : is_open s) : measurable_set s :=
opens_measurable_space.borel_le _ $ generate_measurable.basic _ h

lemma measurable_set_interior : measurable_set (interior s) := is_open_interior.measurable_set

lemma is_Gδ.measurable_set (h : is_Gδ s) : measurable_set s :=
begin
  rcases h with ⟨S, hSo, hSc, rfl⟩,
  exact measurable_set.sInter hSc (λ t ht, (hSo t ht).measurable_set)
end

lemma measurable_set_of_continuous_at {β} [emetric_space β] (f : α → β) :
  measurable_set {x | continuous_at f x} :=
(is_Gδ_set_of_continuous_at f).measurable_set

lemma is_closed.measurable_set (h : is_closed s) : measurable_set s :=
h.is_open_compl.measurable_set.of_compl

lemma is_compact.measurable_set [t2_space α] (h : is_compact s) : measurable_set s :=
h.is_closed.measurable_set

lemma measurable_set_closure : measurable_set (closure s) :=
is_closed_closure.measurable_set

lemma measurable_of_is_open {f : δ → γ} (hf : ∀ s, is_open s → measurable_set (f ⁻¹' s)) :
  measurable f :=
by { rw [‹borel_space γ›.measurable_eq], exact measurable_generate_from hf }

lemma measurable_of_is_closed {f : δ → γ} (hf : ∀ s, is_closed s → measurable_set (f ⁻¹' s)) :
  measurable f :=
begin
  apply measurable_of_is_open, intros s hs,
  rw [← measurable_set.compl_iff, ← preimage_compl], apply hf, rw [is_closed_compl_iff], exact hs
end

lemma measurable_of_is_closed' {f : δ → γ}
  (hf : ∀ s, is_closed s → s.nonempty → s ≠ univ → measurable_set (f ⁻¹' s)) : measurable f :=
begin
  apply measurable_of_is_closed, intros s hs,
  cases eq_empty_or_nonempty s with h1 h1, { simp [h1] },
  by_cases h2 : s = univ, { simp [h2] },
  exact hf s hs h1 h2
end

instance nhds_is_measurably_generated (a : α) : (𝓝 a).is_measurably_generated :=
begin
  rw [nhds, infi_subtype'],
  refine @filter.infi_is_measurably_generated _ _ _ _ (λ i, _),
  exact i.2.2.measurable_set.principal_is_measurably_generated
end

/-- If `s` is a measurable set, then `𝓝[s] a` is a measurably generated filter for
each `a`. This cannot be an `instance` because it depends on a non-instance `hs : measurable_set s`.
-/
lemma measurable_set.nhds_within_is_measurably_generated {s : set α} (hs : measurable_set s)
  (a : α) :
  (𝓝[s] a).is_measurably_generated :=
by haveI := hs.principal_is_measurably_generated; exact filter.inf_is_measurably_generated _ _

@[priority 100] -- see Note [lower instance priority]
instance opens_measurable_space.to_measurable_singleton_class [t1_space α] :
  measurable_singleton_class α :=
⟨λ x, is_closed_singleton.measurable_set⟩

instance pi.opens_measurable_space {ι : Type*} {π : ι → Type*} [fintype ι]
  [t' : Π i, topological_space (π i)]
  [Π i, measurable_space (π i)] [∀ i, second_countable_topology (π i)]
  [∀ i, opens_measurable_space (π i)] :
  opens_measurable_space (Π i, π i) :=
begin
  constructor,
  have : Pi.topological_space =
    generate_from {t | ∃(s:Πa, set (π a)) (i : finset ι), (∀a∈i, s a ∈ countable_basis (π a)) ∧
      t = pi ↑i s},
  { rw [funext (λ a, @eq_generate_from_countable_basis (π a) _ _), pi_generate_from_eq] },
  rw [borel_eq_generate_from_of_subbasis this],
  apply generate_from_le,
  rintros _ ⟨s, i, hi, rfl⟩,
  refine measurable_set.pi i.countable_to_set (λ a ha, is_open.measurable_set _),
  rw [eq_generate_from_countable_basis (π a)],
  exact generate_open.basic _ (hi a ha)
end

instance prod.opens_measurable_space [second_countable_topology α] [second_countable_topology β] :
  opens_measurable_space (α × β) :=
begin
  constructor,
  rw [((is_basis_countable_basis α).prod (is_basis_countable_basis β)).borel_eq_generate_from],
  apply generate_from_le,
  rintros _ ⟨u, v, hu, hv, rfl⟩,
  exact (is_open_of_mem_countable_basis hu).measurable_set.prod
    (is_open_of_mem_countable_basis hv).measurable_set
end

section preorder
variables [preorder α] [order_closed_topology α] {a b : α}

@[simp] lemma measurable_set_Ici : measurable_set (Ici a) := is_closed_Ici.measurable_set
@[simp] lemma measurable_set_Iic : measurable_set (Iic a) := is_closed_Iic.measurable_set
@[simp] lemma measurable_set_Icc : measurable_set (Icc a b) := is_closed_Icc.measurable_set

instance nhds_within_Ici_is_measurably_generated :
  (𝓝[Ici b] a).is_measurably_generated :=
measurable_set_Ici.nhds_within_is_measurably_generated _

instance nhds_within_Iic_is_measurably_generated :
  (𝓝[Iic b] a).is_measurably_generated :=
measurable_set_Iic.nhds_within_is_measurably_generated _

instance at_top_is_measurably_generated : (filter.at_top : filter α).is_measurably_generated :=
@filter.infi_is_measurably_generated _ _ _ _ $
  λ a, (measurable_set_Ici : measurable_set (Ici a)).principal_is_measurably_generated

instance at_bot_is_measurably_generated : (filter.at_bot : filter α).is_measurably_generated :=
@filter.infi_is_measurably_generated _ _ _ _ $
  λ a, (measurable_set_Iic : measurable_set (Iic a)).principal_is_measurably_generated

end preorder

section partial_order
variables [partial_order α] [order_closed_topology α] [second_countable_topology α]
  {a b : α}

lemma measurable_set_le' : measurable_set {p : α × α | p.1 ≤ p.2} :=
order_closed_topology.is_closed_le'.measurable_set

lemma measurable_set_le {f g : δ → α} (hf : measurable f) (hg : measurable g) :
  measurable_set {a | f a ≤ g a} :=
hf.prod_mk hg measurable_set_le'

end partial_order

section linear_order
variables [linear_order α] [order_closed_topology α] {a b : α}

@[simp] lemma measurable_set_Iio : measurable_set (Iio a) := is_open_Iio.measurable_set
@[simp] lemma measurable_set_Ioi : measurable_set (Ioi a) := is_open_Ioi.measurable_set
@[simp] lemma measurable_set_Ioo : measurable_set (Ioo a b) := is_open_Ioo.measurable_set

@[simp] lemma measurable_set_Ioc : measurable_set (Ioc a b) :=
measurable_set_Ioi.inter measurable_set_Iic

@[simp] lemma measurable_set_Ico : measurable_set (Ico a b) :=
measurable_set_Ici.inter measurable_set_Iio

instance nhds_within_Ioi_is_measurably_generated :
  (𝓝[Ioi b] a).is_measurably_generated :=
measurable_set_Ioi.nhds_within_is_measurably_generated _

instance nhds_within_Iio_is_measurably_generated :
  (𝓝[Iio b] a).is_measurably_generated :=
measurable_set_Iio.nhds_within_is_measurably_generated _

variables [second_countable_topology α]

lemma measurable_set_lt' : measurable_set {p : α × α | p.1 < p.2} :=
(is_open_lt continuous_fst continuous_snd).measurable_set

lemma measurable_set_lt {f g : δ → α} (hf : measurable f) (hg : measurable g) :
  measurable_set {a | f a < g a} :=
hf.prod_mk hg measurable_set_lt'

end linear_order

section linear_order

variables [linear_order α] [order_closed_topology α]

lemma measurable_set_interval {a b : α} : measurable_set (interval a b) :=
measurable_set_Icc

variables [second_countable_topology α]

lemma measurable.max {f g : δ → α} (hf : measurable f) (hg : measurable g) :
  measurable (λ a, max (f a) (g a)) :=
hf.piecewise (measurable_set_le hg hf) hg

lemma ae_measurable.max {f g : δ → α} {μ : measure δ}
  (hf : ae_measurable f μ) (hg : ae_measurable g μ) : ae_measurable (λ a, max (f a) (g a)) μ :=
⟨λ a, max (hf.mk f a) (hg.mk g a), hf.measurable_mk.max hg.measurable_mk,
  eventually_eq.comp₂ hf.ae_eq_mk _ hg.ae_eq_mk⟩

lemma measurable.min {f g : δ → α} (hf : measurable f) (hg : measurable g) :
  measurable (λ a, min (f a) (g a)) :=
hf.piecewise (measurable_set_le hf hg) hg

lemma ae_measurable.min {f g : δ → α} {μ : measure δ}
  (hf : ae_measurable f μ) (hg : ae_measurable g μ) : ae_measurable (λ a, min (f a) (g a)) μ :=
⟨λ a, min (hf.mk f a) (hg.mk g a), hf.measurable_mk.min hg.measurable_mk,
  eventually_eq.comp₂ hf.ae_eq_mk _ hg.ae_eq_mk⟩

end linear_order

/-- A continuous function from an `opens_measurable_space` to a `borel_space`
is measurable. -/
lemma continuous.measurable {f : α → γ} (hf : continuous f) :
  measurable f :=
hf.borel_measurable.mono opens_measurable_space.borel_le
  (le_of_eq $ borel_space.measurable_eq)

/-- A continuous function from an `opens_measurable_space` to a `borel_space`
is ae-measurable. -/
lemma continuous.ae_measurable {f : α → γ} (h : continuous f) (μ : measure α) : ae_measurable f μ :=
h.measurable.ae_measurable

lemma closed_embedding.measurable {f : α → γ} (hf : closed_embedding f) :
  measurable f :=
hf.continuous.measurable

@[priority 100, to_additive]
instance has_continuous_mul.has_measurable_mul [has_mul γ] [has_continuous_mul γ] :
  has_measurable_mul γ :=
{ measurable_const_mul := λ c, (continuous_const.mul continuous_id).measurable,
  measurable_mul_const := λ c, (continuous_id.mul continuous_const).measurable }

@[priority 100]
instance has_continuous_sub.has_measurable_sub [has_sub γ] [has_continuous_sub γ] :
  has_measurable_sub γ :=
{ measurable_const_sub := λ c, (continuous_const.sub continuous_id).measurable,
  measurable_sub_const := λ c, (continuous_id.sub continuous_const).measurable }

@[priority 100, to_additive]
instance topological_group.has_measurable_inv [group γ] [topological_group γ] :
  has_measurable_inv γ :=
⟨continuous_inv.measurable⟩

@[priority 100]
instance has_continuous_smul.has_measurable_smul {M α} [topological_space M]
  [topological_space α] [measurable_space M] [measurable_space α]
  [opens_measurable_space M] [borel_space α] [has_scalar M α] [has_continuous_smul M α] :
  has_measurable_smul M α :=
⟨λ c, (continuous_const.smul continuous_id).measurable,
  λ y, (continuous_id.smul continuous_const).measurable⟩

section homeomorph

/-- A homeomorphism between two Borel spaces is a measurable equivalence.-/
def homeomorph.to_measurable_equiv (h : γ ≃ₜ γ₂) : γ ≃ᵐ γ₂ :=
{ measurable_to_fun := h.continuous_to_fun.measurable,
  measurable_inv_fun := h.continuous_inv_fun.measurable,
  .. h }

@[simp]
lemma homeomorph.to_measurable_equiv_coe (h : γ ≃ₜ γ₂) : (h.to_measurable_equiv : γ → γ₂) = h :=
rfl

@[simp] lemma homeomorph.to_measurable_equiv_symm_coe (h : γ ≃ₜ γ₂) :
  (h.to_measurable_equiv.symm : γ₂ → γ) = h.symm :=
rfl

lemma homeomorph.measurable (h : α ≃ₜ γ) : measurable h :=
h.continuous.measurable

end homeomorph

lemma measurable_of_continuous_on_compl_singleton [t1_space α] {f : α → γ} (a : α)
  (hf : continuous_on f {a}ᶜ) :
  measurable f :=
measurable_of_measurable_on_compl_singleton a
  (continuous_on_iff_continuous_restrict.1 hf).measurable

lemma continuous.measurable2 [second_countable_topology α] [second_countable_topology β]
  {f : δ → α} {g : δ → β} {c : α → β → γ}
  (h : continuous (λ p : α × β, c p.1 p.2)) (hf : measurable f) (hg : measurable g) :
  measurable (λ a, c (f a) (g a)) :=
h.measurable.comp (hf.prod_mk hg)

lemma continuous.ae_measurable2 [second_countable_topology α] [second_countable_topology β]
  {f : δ → α} {g : δ → β} {c : α → β → γ} {μ : measure δ}
  (h : continuous (λ p : α × β, c p.1 p.2)) (hf : ae_measurable f μ) (hg : ae_measurable g μ) :
  ae_measurable (λ a, c (f a) (g a)) μ :=
h.measurable.comp_ae_measurable (hf.prod_mk hg)

@[priority 100]
instance has_continuous_inv'.has_measurable_inv [group_with_zero γ] [t1_space γ]
  [has_continuous_inv' γ] :
  has_measurable_inv γ :=
⟨measurable_of_continuous_on_compl_singleton 0 continuous_on_inv'⟩

@[priority 100, to_additive]
instance has_continuous_mul.has_measurable_mul₂ [second_countable_topology γ] [has_mul γ]
  [has_continuous_mul γ] : has_measurable_mul₂ γ :=
⟨continuous_mul.measurable⟩

@[priority 100]
instance has_continuous_sub.has_measurable_sub₂ [second_countable_topology γ] [has_sub γ]
  [has_continuous_sub γ] : has_measurable_sub₂ γ :=
⟨continuous_sub.measurable⟩

@[priority 100]
instance has_continuous_smul.has_measurable_smul₂ {M α} [topological_space M]
  [second_countable_topology M] [measurable_space M] [opens_measurable_space M]
  [topological_space α] [second_countable_topology α] [measurable_space α]
  [borel_space α] [has_scalar M α] [has_continuous_smul M α] :
  has_measurable_smul₂ M α :=
⟨continuous_smul.measurable⟩

end

section borel_space
variables [topological_space α] [measurable_space α] [borel_space α]
  [topological_space β] [measurable_space β] [borel_space β]
  [topological_space γ] [measurable_space γ] [borel_space γ]
  [measurable_space δ]

lemma pi_le_borel_pi {ι : Type*} {π : ι → Type*} [Π i, topological_space (π i)]
  [Π i, measurable_space (π i)] [∀ i, borel_space (π i)] :
  measurable_space.pi ≤ borel (Π i, π i) :=
begin
  have : ‹Π i, measurable_space (π i)› = λ i, borel (π i) :=
    funext (λ i, borel_space.measurable_eq),
  rw [this],
  exact supr_le (λ i, comap_le_iff_le_map.2 $ (continuous_apply i).borel_measurable)
end

lemma prod_le_borel_prod : prod.measurable_space ≤ borel (α × β) :=
begin
  rw [‹borel_space α›.measurable_eq, ‹borel_space β›.measurable_eq],
  refine sup_le _ _,
  { exact comap_le_iff_le_map.mpr continuous_fst.borel_measurable },
  { exact comap_le_iff_le_map.mpr continuous_snd.borel_measurable }
end

instance pi.borel_space {ι : Type*} {π : ι → Type*} [fintype ι]
  [t' : Π i, topological_space (π i)]
  [Π i, measurable_space (π i)] [∀ i, second_countable_topology (π i)]
  [∀ i, borel_space (π i)] :
  borel_space (Π i, π i) :=
⟨le_antisymm pi_le_borel_pi opens_measurable_space.borel_le⟩

instance prod.borel_space [second_countable_topology α] [second_countable_topology β] :
  borel_space (α × β) :=
⟨le_antisymm prod_le_borel_prod opens_measurable_space.borel_le⟩

lemma closed_embedding.measurable_inv_fun [n : nonempty β] {g : β → γ} (hg : closed_embedding g) :
  measurable (function.inv_fun g) :=
begin
  refine measurable_of_is_closed (λ s hs, _),
  by_cases h : classical.choice n ∈ s,
  { rw preimage_inv_fun_of_mem hg.to_embedding.inj h,
    exact (hg.closed_iff_image_closed.mp hs).measurable_set.union
      hg.closed_range.measurable_set.compl },
  { rw preimage_inv_fun_of_not_mem hg.to_embedding.inj h,
    exact (hg.closed_iff_image_closed.mp hs).measurable_set }
end

lemma measurable_comp_iff_of_closed_embedding {f : δ → β} (g : β → γ) (hg : closed_embedding g) :
  measurable (g ∘ f) ↔ measurable f :=
begin
  refine ⟨λ hf, _, λ hf, hg.measurable.comp hf⟩,
  apply measurable_of_is_closed, intros s hs,
  convert hf (hg.is_closed_map s hs).measurable_set,
  rw [@preimage_comp _ _ _ f g, preimage_image_eq _ hg.to_embedding.inj]
end

lemma ae_measurable_comp_iff_of_closed_embedding {f : δ → β} {μ : measure δ}
  (g : β → γ) (hg : closed_embedding g) : ae_measurable (g ∘ f) μ ↔ ae_measurable f μ :=
begin
  by_cases h : nonempty β,
  { resetI,
    refine ⟨λ hf, _, λ hf, hg.measurable.comp_ae_measurable hf⟩,
    convert hg.measurable_inv_fun.comp_ae_measurable hf,
    ext x,
    exact (function.left_inverse_inv_fun hg.to_embedding.inj (f x)).symm },
  { have H : ¬ nonempty δ, by { contrapose! h, exact nonempty.map f h },
    simp [(measurable_of_not_nonempty H (g ∘ f)).ae_measurable,
          (measurable_of_not_nonempty H f).ae_measurable] }
end

lemma ae_measurable_comp_right_iff_of_closed_embedding {g : α → β} {μ : measure α}
  {f : β → δ} (hg : closed_embedding g) :
  ae_measurable (f ∘ g) μ ↔ ae_measurable f (measure.map g μ) :=
begin
  refine ⟨λ h, _, λ h, h.comp_measurable hg.measurable⟩,
  by_cases hα : nonempty α,
  swap, { simp [measure.eq_zero_of_not_nonempty hα μ] },
  resetI,
  refine ⟨(h.mk _) ∘ (function.inv_fun g), h.measurable_mk.comp hg.measurable_inv_fun, _⟩,
  have : μ = measure.map (function.inv_fun g) (measure.map g μ),
    by rw [measure.map_map hg.measurable_inv_fun hg.measurable,
           (function.left_inverse_inv_fun hg.to_embedding.inj).comp_eq_id, measure.map_id],
  rw this at h,
  filter_upwards [ae_of_ae_map hg.measurable_inv_fun h.ae_eq_mk,
    ae_map_mem_range g hg.closed_range.measurable_set μ],
  assume x hx₁ hx₂,
  convert hx₁,
  exact ((function.left_inverse_inv_fun hg.to_embedding.inj).right_inv_on_range hx₂).symm,
end

section linear_order

variables [linear_order α] [order_topology α] [second_countable_topology α]

lemma measurable_of_Iio {f : δ → α} (hf : ∀ x, measurable_set (f ⁻¹' Iio x)) : measurable f :=
begin
  convert measurable_generate_from _,
  exact borel_space.measurable_eq.trans (borel_eq_generate_Iio _),
  rintro _ ⟨x, rfl⟩, exact hf x
end

lemma measurable_of_Ioi {f : δ → α} (hf : ∀ x, measurable_set (f ⁻¹' Ioi x)) : measurable f :=
begin
  convert measurable_generate_from _,
  exact borel_space.measurable_eq.trans (borel_eq_generate_Ioi _),
  rintro _ ⟨x, rfl⟩, exact hf x
end

lemma measurable_of_Iic {f : δ → α} (hf : ∀ x, measurable_set (f ⁻¹' Iic x)) : measurable f :=
begin
  apply measurable_of_Ioi,
  simp_rw [← compl_Iic, preimage_compl, measurable_set.compl_iff],
  assumption
end

lemma measurable_of_Ici {f : δ → α} (hf : ∀ x, measurable_set (f ⁻¹' Ici x)) : measurable f :=
begin
  apply measurable_of_Iio,
  simp_rw [← compl_Ici, preimage_compl, measurable_set.compl_iff],
  assumption
end

lemma measurable.is_lub {ι} [encodable ι] {f : ι → δ → α} {g : δ → α} (hf : ∀ i, measurable (f i))
  (hg : ∀ b, is_lub {a | ∃ i, f i b = a} (g b)) :
  measurable g :=
begin
  change ∀ b, is_lub (range $ λ i, f i b) (g b) at hg,
  rw [‹borel_space α›.measurable_eq, borel_eq_generate_Ioi α],
  apply measurable_generate_from,
  rintro _ ⟨a, rfl⟩,
  simp_rw [set.preimage, mem_Ioi, lt_is_lub_iff (hg _), exists_range_iff, set_of_exists],
  exact measurable_set.Union (λ i, hf i (is_open_lt' _).measurable_set)
end

private lemma ae_measurable.is_lub_of_nonempty {ι} (hι : nonempty ι)
  {μ : measure δ} [encodable ι] {f : ι → δ → α} {g : δ → α}
  (hf : ∀ i, ae_measurable (f i) μ) (hg : ∀ᵐ b ∂μ, is_lub {a | ∃ i, f i b = a} (g b)) :
  ae_measurable g μ :=
begin
  let p : δ → (ι → α) → Prop := λ x f', is_lub {a | ∃ i, f' i = a} (g x),
  let g_seq := λ x, ite (x ∈ ae_seq_set hf p) (g x) (⟨g x⟩ : nonempty α).some,
  have hg_seq : ∀ b, is_lub {a | ∃ i, ae_seq hf p i b = a} (g_seq b),
  { intro b,
    haveI hα : nonempty α := nonempty.map g ⟨b⟩,
    simp only [ae_seq, g_seq],
    split_ifs,
    { have h_set_eq : {a : α | ∃ (i : ι), (hf i).mk (f i) b = a} = {a : α | ∃ (i : ι), f i b = a},
      { ext x,
        simp_rw [set.mem_set_of_eq, ae_seq.mk_eq_fun_of_mem_ae_seq_set hf h], },
      rw h_set_eq,
      exact ae_seq.fun_prop_of_mem_ae_seq_set hf h, },
    { have h_singleton : {a : α | ∃ (i : ι), hα.some = a} = {hα.some},
      { ext1 x,
        exact ⟨λ hx, hx.some_spec.symm, λ hx, ⟨hι.some, hx.symm⟩⟩, },
      rw h_singleton,
      exact is_lub_singleton, }, },
  refine ⟨g_seq, measurable.is_lub (ae_seq.measurable hf p) hg_seq, _⟩,
  exact (ite_ae_eq_of_measure_compl_zero g (λ x, (⟨g x⟩ : nonempty α).some) (ae_seq_set hf p)
    (ae_seq.measure_compl_ae_seq_set_eq_zero hf hg)).symm,
end

lemma ae_measurable.is_lub {ι} {μ : measure δ} [encodable ι] {f : ι → δ → α} {g : δ → α}
  (hf : ∀ i, ae_measurable (f i) μ) (hg : ∀ᵐ b ∂μ, is_lub {a | ∃ i, f i b = a} (g b)) :
  ae_measurable g μ :=
begin
  by_cases hμ : μ = 0, { rw hμ, exact ae_measurable_zero_measure },
  haveI : μ.ae.ne_bot := by simpa [ne_bot_iff],
  by_cases hι : nonempty ι, { exact ae_measurable.is_lub_of_nonempty hι hf hg, },
  suffices : ∃ x, g =ᵐ[μ] λ y, g x,
  by { exact ⟨(λ y, g this.some), measurable_const, this.some_spec⟩, },
  have h_empty : ∀ x, {a : α | ∃ (i : ι), f i x = a} = ∅,
  { intro x,
    ext1 y,
    rw [set.mem_set_of_eq, set.mem_empty_eq, iff_false],
    exact λ hi, hι (nonempty_of_exists hi), },
  simp_rw h_empty at hg,
  exact ⟨hg.exists.some, hg.mono (λ y hy, is_lub.unique hy hg.exists.some_spec)⟩,
end

lemma measurable.is_glb {ι} [encodable ι] {f : ι → δ → α} {g : δ → α} (hf : ∀ i, measurable (f i))
  (hg : ∀ b, is_glb {a | ∃ i, f i b = a} (g b)) :
  measurable g :=
begin
  change ∀ b, is_glb (range $ λ i, f i b) (g b) at hg,
  rw [‹borel_space α›.measurable_eq, borel_eq_generate_Iio α],
  apply measurable_generate_from,
  rintro _ ⟨a, rfl⟩,
  simp_rw [set.preimage, mem_Iio, is_glb_lt_iff (hg _), exists_range_iff, set_of_exists],
  exact measurable_set.Union (λ i, hf i (is_open_gt' _).measurable_set)
end

private lemma ae_measurable.is_glb_of_nonempty {ι} (hι : nonempty ι)
  {μ : measure δ} [encodable ι] {f : ι → δ → α} {g : δ → α}
  (hf : ∀ i, ae_measurable (f i) μ) (hg : ∀ᵐ b ∂μ, is_glb {a | ∃ i, f i b = a} (g b)) :
  ae_measurable g μ :=
begin
  let p : δ → (ι → α) → Prop := λ x f', is_glb {a | ∃ i, f' i = a} (g x),
  let g_seq := λ x, ite (x ∈ ae_seq_set hf p) (g x) (⟨g x⟩ : nonempty α).some,
  have hg_seq : ∀ b, is_glb {a | ∃ i, ae_seq hf p i b = a} (g_seq b),
  { intro b,
    haveI hα : nonempty α := nonempty.map g ⟨b⟩,
    simp only [ae_seq, g_seq],
    split_ifs,
    { have h_set_eq : {a : α | ∃ (i : ι), (hf i).mk (f i) b = a} = {a : α | ∃ (i : ι), f i b = a},
      { ext x,
        simp_rw [set.mem_set_of_eq, ae_seq.mk_eq_fun_of_mem_ae_seq_set hf h], },
      rw h_set_eq,
      exact ae_seq.fun_prop_of_mem_ae_seq_set hf h, },
    { have h_singleton : {a : α | ∃ (i : ι), hα.some = a} = {hα.some},
      { ext1 x,
        exact ⟨λ hx, hx.some_spec.symm, λ hx, ⟨hι.some, hx.symm⟩⟩, },
      rw h_singleton,
      exact is_glb_singleton, }, },
  refine ⟨g_seq, measurable.is_glb (ae_seq.measurable hf p) hg_seq, _⟩,
  exact (ite_ae_eq_of_measure_compl_zero g (λ x, (⟨g x⟩ : nonempty α).some) (ae_seq_set hf p)
    (ae_seq.measure_compl_ae_seq_set_eq_zero hf hg)).symm,
end

lemma ae_measurable.is_glb {ι} {μ : measure δ} [encodable ι] {f : ι → δ → α} {g : δ → α}
  (hf : ∀ i, ae_measurable (f i) μ) (hg : ∀ᵐ b ∂μ, is_glb {a | ∃ i, f i b = a} (g b)) :
  ae_measurable g μ :=
begin
  by_cases hμ : μ = 0, { rw hμ, exact ae_measurable_zero_measure },
  haveI : μ.ae.ne_bot := by simpa [ne_bot_iff],
  by_cases hι : nonempty ι, { exact ae_measurable.is_glb_of_nonempty hι hf hg, },
  suffices : ∃ x, g =ᵐ[μ] λ y, g x,
  by { exact ⟨(λ y, g this.some), measurable_const, this.some_spec⟩, },
  have h_empty : ∀ x, {a : α | ∃ (i : ι), f i x = a} = ∅,
  { intro x,
    ext1 y,
    rw [set.mem_set_of_eq, set.mem_empty_eq, iff_false],
    exact λ hi, hι (nonempty_of_exists hi), },
  simp_rw h_empty at hg,
  exact ⟨hg.exists.some, hg.mono (λ y hy, is_glb.unique hy hg.exists.some_spec)⟩,
end

end linear_order

lemma measurable.supr_Prop {α} [measurable_space α] [complete_lattice α]
  (p : Prop) {f : δ → α} (hf : measurable f) :
  measurable (λ b, ⨆ h : p, f b) :=
classical.by_cases
  (assume h : p, begin convert hf, funext, exact supr_pos h end)
  (assume h : ¬p, begin convert measurable_const, funext, exact supr_neg h end)

lemma measurable.infi_Prop {α} [measurable_space α] [complete_lattice α]
  (p : Prop) {f : δ → α} (hf : measurable f) :
  measurable (λ b, ⨅ h : p, f b) :=
classical.by_cases
  (assume h : p, begin convert hf, funext, exact infi_pos h end )
  (assume h : ¬p, begin convert measurable_const, funext, exact infi_neg h end)

section complete_linear_order

variables [complete_linear_order α] [order_topology α] [second_countable_topology α]

lemma measurable_supr {ι} [encodable ι] {f : ι → δ → α} (hf : ∀ i, measurable (f i)) :
  measurable (λ b, ⨆ i, f i b) :=
measurable.is_lub hf $ λ b, is_lub_supr

lemma ae_measurable_supr {ι} {μ : measure δ} [encodable ι] {f : ι → δ → α}
  (hf : ∀ i, ae_measurable (f i) μ) :
  ae_measurable (λ b, ⨆ i, f i b) μ :=
ae_measurable.is_lub hf $ (ae_of_all μ (λ b, is_lub_supr))

lemma measurable_infi {ι} [encodable ι] {f : ι → δ → α} (hf : ∀ i, measurable (f i)) :
  measurable (λ b, ⨅ i, f i b) :=
measurable.is_glb hf $ λ b, is_glb_infi

lemma ae_measurable_infi {ι} {μ : measure δ} [encodable ι] {f : ι → δ → α}
  (hf : ∀ i, ae_measurable (f i) μ) :
  ae_measurable (λ b, ⨅ i, f i b) μ :=
ae_measurable.is_glb hf $ (ae_of_all μ (λ b, is_glb_infi))

lemma measurable_bsupr {ι} (s : set ι) {f : ι → δ → α} (hs : countable s)
  (hf : ∀ i, measurable (f i)) : measurable (λ b, ⨆ i ∈ s, f i b) :=
by { haveI : encodable s := hs.to_encodable, simp only [supr_subtype'],
     exact measurable_supr (λ i, hf i) }

lemma ae_measurable_bsupr {ι} {μ : measure δ} (s : set ι) {f : ι → δ → α} (hs : countable s)
  (hf : ∀ i, ae_measurable (f i) μ) : ae_measurable (λ b, ⨆ i ∈ s, f i b) μ :=
begin
  haveI : encodable s := hs.to_encodable,
  simp only [supr_subtype'],
  exact ae_measurable_supr (λ i, hf i),
end

lemma measurable_binfi {ι} (s : set ι) {f : ι → δ → α} (hs : countable s)
  (hf : ∀ i, measurable (f i)) : measurable (λ b, ⨅ i ∈ s, f i b) :=
by { haveI : encodable s := hs.to_encodable, simp only [infi_subtype'],
     exact measurable_infi (λ i, hf i) }

lemma ae_measurable_binfi {ι} {μ : measure δ} (s : set ι) {f : ι → δ → α} (hs : countable s)
  (hf : ∀ i, ae_measurable (f i) μ) : ae_measurable (λ b, ⨅ i ∈ s, f i b) μ :=
begin
  haveI : encodable s := hs.to_encodable,
  simp only [infi_subtype'],
  exact ae_measurable_infi (λ i, hf i),
end

/-- `liminf` over a general filter is measurable. See `measurable_liminf` for the version over `ℕ`.
-/
lemma measurable_liminf' {ι ι'} {f : ι → δ → α} {u : filter ι} (hf : ∀ i, measurable (f i))
  {p : ι' → Prop} {s : ι' → set ι} (hu : u.has_countable_basis p s) (hs : ∀ i, (s i).countable) :
  measurable (λ x, liminf u (λ i, f i x)) :=
begin
  simp_rw [hu.to_has_basis.liminf_eq_supr_infi],
  refine measurable_bsupr _ hu.countable _,
  exact λ i, measurable_binfi _ (hs i) hf
end

/-- `limsup` over a general filter is measurable. See `measurable_limsup` for the version over `ℕ`.
-/
lemma measurable_limsup' {ι ι'}  {f : ι → δ → α} {u : filter ι} (hf : ∀ i, measurable (f i))
  {p : ι' → Prop} {s : ι' → set ι} (hu : u.has_countable_basis p s) (hs : ∀ i, (s i).countable) :
  measurable (λ x, limsup u (λ i, f i x)) :=
begin
  simp_rw [hu.to_has_basis.limsup_eq_infi_supr],
  refine measurable_binfi _ hu.countable _,
  exact λ i, measurable_bsupr _ (hs i) hf
end

/-- `liminf` over `ℕ` is measurable. See `measurable_liminf'` for a version with a general filter.
-/
lemma measurable_liminf {f : ℕ → δ → α} (hf : ∀ i, measurable (f i)) :
  measurable (λ x, liminf at_top (λ i, f i x)) :=
measurable_liminf' hf at_top_countable_basis (λ i, countable_encodable _)

/-- `limsup` over `ℕ` is measurable. See `measurable_limsup'` for a version with a general filter.
-/
lemma measurable_limsup {f : ℕ → δ → α} (hf : ∀ i, measurable (f i)) :
  measurable (λ x, limsup at_top (λ i, f i x)) :=
measurable_limsup' hf at_top_countable_basis (λ i, countable_encodable _)

end complete_linear_order

section conditionally_complete_linear_order

variables [conditionally_complete_linear_order α] [second_countable_topology α] [order_topology α]

lemma measurable_cSup {ι} {f : ι → δ → α} {s : set ι} (hs : s.countable)
  (hf : ∀ i, measurable (f i)) (bdd : ∀ x, bdd_above ((λ i, f i x) '' s)) :
  measurable (λ x, Sup ((λ i, f i x) '' s)) :=
begin
  cases eq_empty_or_nonempty s with h2s h2s,
  { simp [h2s, measurable_const] },
  { apply measurable_of_Iic, intro y,
    simp_rw [preimage, mem_Iic, cSup_le_iff (bdd _) (h2s.image _), ball_image_iff, set_of_forall],
    exact measurable_set.bInter hs (λ i hi, measurable_set_le (hf i) measurable_const) }
end

end conditionally_complete_linear_order

/-- Convert a `homeomorph` to a `measurable_equiv`. -/
def homemorph.to_measurable_equiv (h : α ≃ₜ β) : α ≃ᵐ β :=
{ to_equiv := h.to_equiv,
  measurable_to_fun := h.continuous_to_fun.measurable,
  measurable_inv_fun := h.continuous_inv_fun.measurable }

end borel_space

instance empty.borel_space : borel_space empty := ⟨borel_eq_top_of_discrete.symm⟩
instance unit.borel_space : borel_space unit := ⟨borel_eq_top_of_discrete.symm⟩
instance bool.borel_space : borel_space bool := ⟨borel_eq_top_of_discrete.symm⟩
instance nat.borel_space : borel_space ℕ := ⟨borel_eq_top_of_discrete.symm⟩
instance int.borel_space : borel_space ℤ := ⟨borel_eq_top_of_discrete.symm⟩
instance rat.borel_space : borel_space ℚ := ⟨borel_eq_top_of_encodable.symm⟩

instance real.measurable_space : measurable_space ℝ := borel ℝ
instance real.borel_space : borel_space ℝ := ⟨rfl⟩

instance nnreal.measurable_space : measurable_space ℝ≥0 := subtype.measurable_space
instance nnreal.borel_space : borel_space ℝ≥0 := subtype.borel_space _

instance ennreal.measurable_space : measurable_space ℝ≥0∞ := borel ℝ≥0∞
instance ennreal.borel_space : borel_space ℝ≥0∞ := ⟨rfl⟩

instance complex.measurable_space : measurable_space ℂ := borel ℂ
instance complex.borel_space : borel_space ℂ := ⟨rfl⟩

section metric_space

variables [metric_space α] [measurable_space α] [opens_measurable_space α]
variables [measurable_space β] {x : α} {ε : ℝ}

open metric

lemma measurable_set_ball : measurable_set (metric.ball x ε) :=
metric.is_open_ball.measurable_set

lemma measurable_set_closed_ball : measurable_set (metric.closed_ball x ε) :=
metric.is_closed_ball.measurable_set

lemma measurable_inf_dist {s : set α} : measurable (λ x, inf_dist x s) :=
(continuous_inf_dist_pt s).measurable

lemma measurable.inf_dist {f : β → α} (hf : measurable f) {s : set α} :
  measurable (λ x, inf_dist (f x) s) :=
measurable_inf_dist.comp hf

lemma measurable_inf_nndist {s : set α} : measurable (λ x, inf_nndist x s) :=
(continuous_inf_nndist_pt s).measurable

lemma measurable.inf_nndist {f : β → α} (hf : measurable f) {s : set α} :
  measurable (λ x, inf_nndist (f x) s) :=
measurable_inf_nndist.comp hf

variables [second_countable_topology α]
lemma measurable_dist : measurable (λ p : α × α, dist p.1 p.2) :=
continuous_dist.measurable

lemma measurable.dist {f g : β → α} (hf : measurable f) (hg : measurable g) :
  measurable (λ b, dist (f b) (g b)) :=
(@continuous_dist α _).measurable2 hf hg

lemma measurable_nndist : measurable (λ p : α × α, nndist p.1 p.2) :=
continuous_nndist.measurable

lemma measurable.nndist {f g : β → α} (hf : measurable f) (hg : measurable g) :
  measurable (λ b, nndist (f b) (g b)) :=
(@continuous_nndist α _).measurable2 hf hg

end metric_space

section emetric_space

variables [emetric_space α] [measurable_space α] [opens_measurable_space α]
variables [measurable_space β] {x : α} {ε : ℝ≥0∞}

open emetric

lemma measurable_set_eball : measurable_set (emetric.ball x ε) :=
emetric.is_open_ball.measurable_set

lemma measurable_edist_right : measurable (edist x) :=
(continuous_const.edist continuous_id).measurable

lemma measurable_edist_left : measurable (λ y, edist y x) :=
(continuous_id.edist continuous_const).measurable

lemma measurable_inf_edist {s : set α} : measurable (λ x, inf_edist x s) :=
continuous_inf_edist.measurable

lemma measurable.inf_edist {f : β → α} (hf : measurable f) {s : set α} :
  measurable (λ x, inf_edist (f x) s) :=
measurable_inf_edist.comp hf

variables [second_countable_topology α]

lemma measurable_edist : measurable (λ p : α × α, edist p.1 p.2) :=
continuous_edist.measurable

lemma measurable.edist {f g : β → α} (hf : measurable f) (hg : measurable g) :
  measurable (λ b, edist (f b) (g b)) :=
(@continuous_edist α _).measurable2 hf hg

lemma ae_measurable.edist {f g : β → α} {μ : measure β}
  (hf : ae_measurable f μ) (hg : ae_measurable g μ) : ae_measurable (λ a, edist (f a) (g a)) μ :=
(@continuous_edist α _).ae_measurable2 hf hg

end emetric_space

namespace real
open measurable_space measure_theory

lemma borel_eq_generate_from_Ioo_rat :
  borel ℝ = generate_from (⋃(a b : ℚ) (h : a < b), {Ioo a b}) :=
is_topological_basis_Ioo_rat.borel_eq_generate_from

lemma measure_ext_Ioo_rat {μ ν : measure ℝ} [locally_finite_measure μ]
  (h : ∀ a b : ℚ, μ (Ioo a b) = ν (Ioo a b)) : μ = ν :=
begin
  refine measure.ext_of_generate_from_of_cover_subset borel_eq_generate_from_Ioo_rat _
    (subset.refl _) _ _ _ _,
  { simp only [is_pi_system, mem_Union, mem_singleton_iff],
    rintros _ _ ⟨a₁, b₁, h₁, rfl⟩ ⟨a₂, b₂, h₂, rfl⟩ ne,
    simp only [Ioo_inter_Ioo, sup_eq_max, inf_eq_min, ← rat.cast_max, ← rat.cast_min,
      nonempty_Ioo] at ne ⊢,
    refine ⟨_, _, _, rfl⟩,
    assumption_mod_cast },
  { exact countable_Union (λ a, (countable_encodable _).bUnion $ λ _ _, countable_singleton _) },
  { exact is_topological_basis_Ioo_rat.sUnion_eq },
  { simp only [mem_Union, mem_singleton_iff],
    rintros _ ⟨a, b, h, rfl⟩,
    refine (measure_mono subset_closure).trans_lt _,
    rw [closure_Ioo],
    exacts [compact_Icc.finite_measure, rat.cast_lt.2 h] },
  { simp only [mem_Union, mem_singleton_iff],
    rintros _ ⟨a, b, hab, rfl⟩,
    exact h a b }
end

lemma borel_eq_generate_from_Iio_rat :
  borel ℝ = generate_from (⋃ a : ℚ, {Iio a}) :=
begin
  let g, swap,
  apply le_antisymm (_ : _ ≤ g) (measurable_space.generate_from_le (λ t, _)),
  { rw borel_eq_generate_from_Ioo_rat,
    refine generate_from_le (λ t, _),
    simp only [mem_Union], rintro ⟨a, b, h, H⟩,
    rw [mem_singleton_iff.1 H],
    rw (set.ext (λ x, _) : Ioo (a : ℝ) b = (⋃c>a, (Iio c)ᶜ) ∩ Iio b),
    { have hg : ∀ q : ℚ, g.measurable_set' (Iio q) :=
        λ q, generate_measurable.basic (Iio q) (by { simp, exact ⟨_, rfl⟩ }),
      refine @measurable_set.inter _ g _ _ _ (hg _),
      refine @measurable_set.bUnion _ _ g _ _ (countable_encodable _) (λ c h, _),
      exact @measurable_set.compl _ _ g (hg _) },
    { suffices : x < ↑b → (↑a < x ↔ ∃ (i : ℚ), a < i ∧ ↑i ≤ x), by simpa,
      refine λ _, ⟨λ h, _, λ ⟨i, hai, hix⟩, (rat.cast_lt.2 hai).trans_le hix⟩,
      rcases exists_rat_btwn h with ⟨c, ac, cx⟩,
      exact ⟨c, rat.cast_lt.1 ac, cx.le⟩ } },
  { simp, rintro r rfl, exact is_open_Iio.measurable_set }
end

end real

variable [measurable_space α]

lemma measurable.nnreal_of_real {f : α → ℝ} (hf : measurable f) :
  measurable (λ x, nnreal.of_real (f x)) :=
nnreal.continuous_of_real.measurable.comp hf

lemma nnreal.measurable_coe : measurable (coe : ℝ≥0 → ℝ) :=
nnreal.continuous_coe.measurable

lemma measurable.nnreal_coe {f : α → ℝ≥0} (hf : measurable f) :
  measurable (λ x, (f x : ℝ)) :=
nnreal.measurable_coe.comp hf

lemma measurable.ennreal_coe {f : α → ℝ≥0} (hf : measurable f) :
  measurable (λ x, (f x : ℝ≥0∞)) :=
ennreal.continuous_coe.measurable.comp hf

lemma ae_measurable.ennreal_coe {f : α → ℝ≥0} {μ : measure α} (hf :  ae_measurable f μ) :
  ae_measurable (λ x, (f x : ℝ≥0∞)) μ :=
ennreal.continuous_coe.measurable.comp_ae_measurable hf

lemma measurable.ennreal_of_real {f : α → ℝ} (hf : measurable f) :
  measurable (λ x, ennreal.of_real (f x)) :=
ennreal.continuous_of_real.measurable.comp hf

/-- The set of finite `ℝ≥0∞` numbers is `measurable_equiv` to `ℝ≥0`. -/
def measurable_equiv.ennreal_equiv_nnreal : {r : ℝ≥0∞ | r ≠ ∞} ≃ᵐ ℝ≥0 :=
ennreal.ne_top_homeomorph_nnreal.to_measurable_equiv

namespace ennreal

lemma measurable_coe : measurable (coe : ℝ≥0 → ℝ≥0∞) :=
measurable_id.ennreal_coe

lemma measurable_of_measurable_nnreal {f : ℝ≥0∞ → α}
  (h : measurable (λ p : ℝ≥0, f p)) : measurable f :=
measurable_of_measurable_on_compl_singleton ∞
  (measurable_equiv.ennreal_equiv_nnreal.symm.measurable_coe_iff.1 h)

/-- `ℝ≥0∞` is `measurable_equiv` to `ℝ≥0 ⊕ unit`. -/
def ennreal_equiv_sum : ℝ≥0∞ ≃ᵐ ℝ≥0 ⊕ unit :=
{ measurable_to_fun  := measurable_of_measurable_nnreal measurable_inl,
  measurable_inv_fun := measurable_sum measurable_coe (@measurable_const ℝ≥0∞ unit _ _ ∞),
  .. equiv.option_equiv_sum_punit ℝ≥0 }

open function (uncurry)

lemma measurable_of_measurable_nnreal_prod [measurable_space β] [measurable_space γ]
  {f : ℝ≥0∞ × β → γ} (H₁ : measurable (λ p : ℝ≥0 × β, f (p.1, p.2)))
  (H₂ : measurable (λ x, f (∞, x))) :
  measurable f :=
let e : ℝ≥0∞ × β ≃ᵐ ℝ≥0 × β ⊕ unit × β :=
  (ennreal_equiv_sum.prod_congr (measurable_equiv.refl β)).trans
    (measurable_equiv.sum_prod_distrib _ _ _) in
e.symm.measurable_coe_iff.1 $ measurable_sum H₁ (H₂.comp measurable_id.snd)

lemma measurable_of_measurable_nnreal_nnreal [measurable_space β]
  {f : ℝ≥0∞ × ℝ≥0∞ → β} (h₁ : measurable (λ p : ℝ≥0 × ℝ≥0, f (p.1, p.2)))
  (h₂ : measurable (λ r : ℝ≥0, f (∞, r))) (h₃ : measurable (λ r : ℝ≥0, f (r, ∞))) :
  measurable f :=
measurable_of_measurable_nnreal_prod
  (measurable_swap_iff.1 $ measurable_of_measurable_nnreal_prod (h₁.comp measurable_swap) h₃)
  (measurable_of_measurable_nnreal h₂)

lemma measurable_of_real : measurable ennreal.of_real :=
ennreal.continuous_of_real.measurable

lemma measurable_to_real : measurable ennreal.to_real :=
ennreal.measurable_of_measurable_nnreal nnreal.measurable_coe

lemma measurable_to_nnreal : measurable ennreal.to_nnreal :=
ennreal.measurable_of_measurable_nnreal measurable_id

instance : has_measurable_mul₂ ℝ≥0∞ :=
begin
  refine ⟨measurable_of_measurable_nnreal_nnreal _ _ _⟩,
  { simp only [← ennreal.coe_mul, measurable_mul.ennreal_coe] },
  { simp only [ennreal.top_mul, ennreal.coe_eq_zero],
    exact measurable_const.piecewise (measurable_set_singleton _) measurable_const },
  { simp only [ennreal.mul_top, ennreal.coe_eq_zero],
    exact measurable_const.piecewise (measurable_set_singleton _) measurable_const }
end

instance : has_measurable_sub₂ ℝ≥0∞ :=
⟨by apply measurable_of_measurable_nnreal_nnreal;
  simp [← ennreal.coe_sub, continuous_sub.measurable.ennreal_coe]⟩

instance : has_measurable_inv ℝ≥0∞ := ⟨ennreal.continuous_inv.measurable⟩

end ennreal

lemma measurable.to_nnreal {f : α → ℝ≥0∞} (hf : measurable f) :
  measurable (λ x, (f x).to_nnreal) :=
ennreal.measurable_to_nnreal.comp hf

lemma measurable_ennreal_coe_iff {f : α → ℝ≥0} :
  measurable (λ x, (f x : ℝ≥0∞)) ↔ measurable f :=
⟨λ h, h.to_nnreal, λ h, h.ennreal_coe⟩

lemma measurable.to_real {f : α → ℝ≥0∞} (hf : measurable f) :
  measurable (λ x, ennreal.to_real (f x)) :=
ennreal.measurable_to_real.comp hf

lemma ae_measurable.to_real {f : α → ℝ≥0∞} {μ : measure α} (hf : ae_measurable f μ) :
  ae_measurable (λ x, ennreal.to_real (f x)) μ :=
ennreal.measurable_to_real.comp_ae_measurable hf

/-- note: `ℝ≥0∞` can probably be generalized in a future version of this lemma. -/
lemma measurable.ennreal_tsum {ι} [encodable ι] {f : ι → α → ℝ≥0∞} (h : ∀ i, measurable (f i)) :
  measurable (λ x, ∑' i, f i x) :=
by { simp_rw [ennreal.tsum_eq_supr_sum], apply measurable_supr,
  exact λ s, s.measurable_sum (λ i _, h i) }

lemma ae_measurable.ennreal_tsum {ι} [encodable ι] {f : ι → α → ℝ≥0∞} {μ : measure α}
  (h : ∀ i, ae_measurable (f i) μ) :
  ae_measurable (λ x, ∑' i, f i x) μ :=
by { simp_rw [ennreal.tsum_eq_supr_sum], apply ae_measurable_supr,
  exact λ s, finset.ae_measurable_sum s (λ i _, h i) }

section normed_group

variables [normed_group α] [opens_measurable_space α] [measurable_space β]

lemma measurable_norm : measurable (norm : α → ℝ) :=
continuous_norm.measurable

lemma measurable.norm {f : β → α} (hf : measurable f) : measurable (λ a, norm (f a)) :=
measurable_norm.comp hf

lemma ae_measurable.norm {f : β → α} {μ : measure β} (hf : ae_measurable f μ) :
  ae_measurable (λ a, norm (f a)) μ :=
measurable_norm.comp_ae_measurable hf

lemma measurable_nnnorm : measurable (nnnorm : α → ℝ≥0) :=
continuous_nnnorm.measurable

lemma measurable.nnnorm {f : β → α} (hf : measurable f) : measurable (λ a, nnnorm (f a)) :=
measurable_nnnorm.comp hf

lemma ae_measurable.nnnorm {f : β → α} {μ : measure β} (hf : ae_measurable f μ) :
  ae_measurable (λ a, nnnorm (f a)) μ :=
measurable_nnnorm.comp_ae_measurable hf

lemma measurable_ennnorm : measurable (λ x : α, (nnnorm x : ℝ≥0∞)) :=
measurable_nnnorm.ennreal_coe

lemma measurable.ennnorm {f : β → α} (hf : measurable f) :
  measurable (λ a, (nnnorm (f a) : ℝ≥0∞)) :=
hf.nnnorm.ennreal_coe

lemma ae_measurable.ennnorm {f : β → α} {μ : measure β} (hf : ae_measurable f μ) :
  ae_measurable (λ a, (nnnorm (f a) : ℝ≥0∞)) μ :=
measurable_ennnorm.comp_ae_measurable hf

end normed_group

section limits

variables [measurable_space β] [metric_space β] [borel_space β]

open metric

/-- A limit (over a general filter) of measurable `ℝ≥0` valued functions is measurable.
The assumption `hs` can be dropped using `filter.is_countably_generated.has_antimono_basis`, but we
don't need that case yet. -/
lemma measurable_of_tendsto_nnreal' {ι ι'} {f : ι → α → ℝ≥0} {g : α → ℝ≥0} (u : filter ι)
  [ne_bot u] (hf : ∀ i, measurable (f i)) (lim : tendsto f u (𝓝 g)) {p : ι' → Prop}
  {s : ι' → set ι} (hu : u.has_countable_basis p s) (hs : ∀ i, (s i).countable) : measurable g :=
begin
  rw [tendsto_pi] at lim, rw [← measurable_ennreal_coe_iff],
  have : ∀ x, liminf u (λ n, (f n x : ℝ≥0∞)) = (g x : ℝ≥0∞) :=
  λ x, ((ennreal.continuous_coe.tendsto (g x)).comp (lim x)).liminf_eq,
  simp_rw [← this],
  show measurable (λ x, liminf u (λ n, (f n x : ℝ≥0∞))),
  exact measurable_liminf' (λ i, (hf i).ennreal_coe) hu hs,
end

/-- A sequential limit of measurable `ℝ≥0` valued functions is measurable. -/
lemma measurable_of_tendsto_nnreal {f : ℕ → α → ℝ≥0} {g : α → ℝ≥0}
  (hf : ∀ i, measurable (f i)) (lim : tendsto f at_top (𝓝 g)) : measurable g :=
measurable_of_tendsto_nnreal' at_top hf lim at_top_countable_basis (λ i, countable_encodable _)

/-- A limit (over a general filter) of measurable functions valued in a metric space is measurable.
The assumption `hs` can be dropped using `filter.is_countably_generated.has_antimono_basis`, but we
don't need that case yet. -/
lemma measurable_of_tendsto_metric' {ι ι'} {f : ι → α → β} {g : α → β}
  (u : filter ι) [ne_bot u] (hf : ∀ i, measurable (f i)) (lim : tendsto f u (𝓝 g)) {p : ι' → Prop}
  {s : ι' → set ι} (hu : u.has_countable_basis p s) (hs : ∀ i, (s i).countable) :
  measurable g :=
begin
  apply measurable_of_is_closed', intros s h1s h2s h3s,
  have : measurable (λ x, inf_nndist (g x) s),
  { refine measurable_of_tendsto_nnreal' u (λ i, (hf i).inf_nndist) _ hu hs, swap,
    rw [tendsto_pi], rw [tendsto_pi] at lim, intro x,
    exact ((continuous_inf_nndist_pt s).tendsto (g x)).comp (lim x) },
  have h4s : g ⁻¹' s = (λ x, inf_nndist (g x) s) ⁻¹' {0},
  { ext x, simp [h1s, ← mem_iff_inf_dist_zero_of_closed h1s h2s, ← nnreal.coe_eq_zero] },
  rw [h4s], exact this (measurable_set_singleton 0),
end

/-- A sequential limit of measurable functions valued in a metric space is measurable. -/
lemma measurable_of_tendsto_metric {f : ℕ → α → β} {g : α → β}
  (hf : ∀ i, measurable (f i)) (lim : tendsto f at_top (𝓝 g)) :
  measurable g :=
measurable_of_tendsto_metric' at_top hf lim at_top_countable_basis (λ i, countable_encodable _)

lemma ae_measurable_of_tendsto_metric_ae {μ : measure α} {f : ℕ → α → β} {g : α → β}
  (hf : ∀ n, ae_measurable (f n) μ)
  (h_ae_tendsto : ∀ᵐ x ∂μ, filter.at_top.tendsto (λ n, f n x) (𝓝 (g x))) :
  ae_measurable g μ :=
begin
  let p : α → (ℕ → β) → Prop := λ x f', filter.at_top.tendsto (λ n, f' n) (𝓝 (g x)),
  let hp : ∀ᵐ x ∂μ, p x (λ n, f n x), from h_ae_tendsto,
  let ae_seq_lim := λ x, ite (x ∈ ae_seq_set hf p) (g x) (⟨f 0 x⟩ : nonempty β).some,
  refine ⟨ae_seq_lim, _, (ite_ae_eq_of_measure_compl_zero g (λ x, (⟨f 0 x⟩ : nonempty β).some)
    (ae_seq_set hf p) (ae_seq.measure_compl_ae_seq_set_eq_zero hf hp)).symm⟩,
  refine measurable_of_tendsto_metric (@ae_seq.measurable α β _ _ _ f μ hf p) _,
  refine tendsto_pi.mpr (λ x, _),
  simp_rw [ae_seq, ae_seq_lim],
  split_ifs with hx,
  { simp_rw ae_seq.mk_eq_fun_of_mem_ae_seq_set hf hx,
    exact @ae_seq.fun_prop_of_mem_ae_seq_set α β _ _ _ _ _ _ hf x hx, },
  { exact tendsto_const_nhds, },
end

lemma measurable_of_tendsto_metric_ae {μ : measure α} [μ.is_complete] {f : ℕ → α → β} {g : α → β}
  (hf : ∀ n, measurable (f n))
  (h_ae_tendsto : ∀ᵐ x ∂μ, filter.at_top.tendsto (λ n, f n x) (𝓝 (g x))) :
  measurable g :=
ae_measurable_iff_measurable.mp
  (ae_measurable_of_tendsto_metric_ae (λ i, (hf i).ae_measurable) h_ae_tendsto)

lemma measurable_limit_of_tendsto_metric_ae {μ : measure α} {f : ℕ → α → β}
  (hf : ∀ n, ae_measurable (f n) μ)
  (h_ae_tendsto : ∀ᵐ x ∂μ, ∃ l : β, filter.at_top.tendsto (λ n, f n x) (𝓝 l)) :
  ∃ (f_lim : α → β) (hf_lim_meas : measurable f_lim),
    ∀ᵐ x ∂μ, filter.at_top.tendsto (λ n, f n x) (𝓝 (f_lim x)) :=
begin
  let p : α → (ℕ → β) → Prop := λ x f', ∃ l : β, filter.at_top.tendsto (λ n, f' n) (𝓝 l),
  have hp_mem : ∀ x, x ∈ ae_seq_set hf p → p x (λ n, f n x),
    from λ x hx, ae_seq.fun_prop_of_mem_ae_seq_set hf hx,
  have hμ_compl : μ (ae_seq_set hf p)ᶜ = 0,
    from ae_seq.measure_compl_ae_seq_set_eq_zero hf h_ae_tendsto,
  let f_lim : α → β := λ x, dite (x ∈ ae_seq_set hf p) (λ h, (hp_mem x h).some)
    (λ h, (⟨f 0 x⟩ : nonempty β).some),
  have hf_lim_conv : ∀ x, x ∈ ae_seq_set hf p → filter.at_top.tendsto (λ n, f n x) (𝓝 (f_lim x)),
  { intros x hx_conv,
    simp only [f_lim, hx_conv, dif_pos],
    exact (hp_mem x hx_conv).some_spec, },
  have hf_lim : ∀ x, filter.at_top.tendsto (λ n, ae_seq hf p n x) (𝓝 (f_lim x)),
  { intros x,
    simp only [f_lim, ae_seq],
    split_ifs,
    { rw funext (λ n, ae_seq.mk_eq_fun_of_mem_ae_seq_set hf h n),
      exact (hp_mem x h).some_spec, },
    { exact tendsto_const_nhds, }, },
  have h_ae_tendsto_f_lim : ∀ᵐ x ∂μ, filter.at_top.tendsto (λ n, f n x) (𝓝 (f_lim x)),
  { refine le_antisymm (le_of_eq (measure_mono_null _ hμ_compl)) (zero_le _),
    exact set.compl_subset_compl.mpr (λ x hx, hf_lim_conv x hx), },
  have h_f_lim_meas : measurable f_lim,
    from measurable_of_tendsto_metric (ae_seq.measurable hf p) (tendsto_pi.mpr (λ x, hf_lim x)),
  exact ⟨f_lim, h_f_lim_meas, h_ae_tendsto_f_lim⟩,
end

end limits

namespace continuous_linear_map

variables {𝕜 : Type*} [normed_field 𝕜]
variables {E : Type*} [normed_group E] [normed_space 𝕜 E] [measurable_space E]
variables [opens_measurable_space E]
variables {F : Type*} [normed_group F] [normed_space 𝕜 F] [measurable_space F] [borel_space F]

protected lemma measurable (L : E →L[𝕜] F) : measurable L :=
L.continuous.measurable

lemma measurable_comp (L : E →L[𝕜] F) {φ : α → E} (φ_meas : measurable φ) :
  measurable (λ (a : α), L (φ a)) :=
L.measurable.comp φ_meas

end continuous_linear_map

namespace continuous_linear_map

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

instance : measurable_space (E →L[𝕜] F) := borel _

instance : borel_space (E →L[𝕜] F) := ⟨rfl⟩

lemma measurable_apply [measurable_space F] [borel_space F] (x : E) :
  measurable (λ f : E →L[𝕜] F, f x) :=
(apply 𝕜 F x).continuous.measurable

lemma measurable_apply' [measurable_space E] [opens_measurable_space E]
  [measurable_space F] [borel_space F] :
  measurable (λ (x : E) (f : E →L[𝕜] F), f x) :=
measurable_pi_lambda _ $ λ f, f.measurable

lemma measurable_coe [measurable_space F] [borel_space F] :
  measurable (λ (f : E →L[𝕜] F) (x : E), f x) :=
measurable_pi_lambda _ measurable_apply

end continuous_linear_map

section continuous_linear_map_nondiscrete_normed_field

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

lemma measurable.apply_continuous_linear_map  {φ : α → F →L[𝕜] E} (hφ : measurable φ) (v : F) :
  measurable (λ a, φ a v) :=
(continuous_linear_map.apply 𝕜 E v).measurable.comp hφ

lemma ae_measurable.apply_continuous_linear_map {φ : α → F →L[𝕜] E} {μ : measure α}
  (hφ : ae_measurable φ μ) (v : F) : ae_measurable (λ a, φ a v) μ :=
(continuous_linear_map.apply 𝕜 E v).measurable.comp_ae_measurable hφ

end continuous_linear_map_nondiscrete_normed_field

section normed_space
variables {𝕜 : Type*} [nondiscrete_normed_field 𝕜] [complete_space 𝕜] [measurable_space 𝕜]
variables [borel_space 𝕜]
variables {E : Type*} [normed_group E] [normed_space 𝕜 E] [measurable_space E] [borel_space E]

lemma measurable_smul_const {f : α → 𝕜} {c : E} (hc : c ≠ 0) :
  measurable (λ x, f x • c) ↔ measurable f :=
measurable_comp_iff_of_closed_embedding (λ y : 𝕜, y • c) (closed_embedding_smul_left hc)

lemma ae_measurable_smul_const {f : α → 𝕜} {μ : measure α} {c : E} (hc : c ≠ 0) :
  ae_measurable (λ x, f x • c) μ ↔ ae_measurable f μ :=
ae_measurable_comp_iff_of_closed_embedding (λ y : 𝕜, y • c) (closed_embedding_smul_left hc)

end normed_space

namespace measure_theory
namespace measure

variables [topological_space α] {μ : measure α}

/-- A measure `μ` is regular if
  - it is finite on all compact sets;
  - it is outer regular: `μ(A) = inf { μ(U) | A ⊆ U open }` for `A` measurable;
  - it is inner regular: `μ(U) = sup { μ(K) | K ⊆ U compact }` for `U` open. -/
structure regular (μ : measure α) : Prop :=
(lt_top_of_is_compact : ∀ {{K : set α}}, is_compact K → μ K < ∞)
(outer_regular : ∀ {{A : set α}}, measurable_set A →
  (⨅ (U : set α) (h : is_open U) (h2 : A ⊆ U), μ U) ≤ μ A)
(inner_regular : ∀ {{U : set α}}, is_open U →
  μ U ≤ ⨆ (K : set α) (h : is_compact K) (h2 : K ⊆ U), μ K)

namespace regular

lemma outer_regular_eq (hμ : μ.regular) {{A : set α}}
  (hA : measurable_set A) : (⨅ (U : set α) (h : is_open U) (h2 : A ⊆ U), μ U) = μ A :=
le_antisymm (hμ.outer_regular hA) $ le_infi $ λ s, le_infi $ λ hs, le_infi $ λ h2s, μ.mono h2s

lemma inner_regular_eq (hμ : μ.regular) {{U : set α}}
  (hU : is_open U) : (⨆ (K : set α) (h : is_compact K) (h2 : K ⊆ U), μ K) = μ U :=
le_antisymm (supr_le $ λ s, supr_le $ λ hs, supr_le $ λ h2s, μ.mono h2s) (hμ.inner_regular hU)

lemma exists_compact_not_null (hμ : regular μ) : (∃ K, is_compact K ∧ μ K ≠ 0) ↔ μ ≠ 0 :=
by simp_rw [ne.def, ← measure_univ_eq_zero, ← hμ.inner_regular_eq is_open_univ,
    ennreal.supr_eq_zero, not_forall, exists_prop, subset_univ, true_and]

protected lemma map [opens_measurable_space α] [measurable_space β] [topological_space β]
  [t2_space β] [borel_space β] (hμ : μ.regular) (f : α ≃ₜ β) :
  (measure.map f μ).regular :=
begin
  have hf := f.measurable,
  have h2f := f.to_equiv.injective.preimage_surjective,
  have h3f := f.to_equiv.surjective,
  split,
  { intros K hK, rw [map_apply hf hK.measurable_set],
    apply hμ.lt_top_of_is_compact, rwa f.compact_preimage },
  { intros A hA, rw [map_apply hf hA, ← hμ.outer_regular_eq (hf hA)],
    refine le_of_eq _, apply infi_congr (preimage f) h2f,
    intro U, apply infi_congr_Prop f.is_open_preimage, intro hU,
    apply infi_congr_Prop h3f.preimage_subset_preimage_iff, intro h2U,
    rw [map_apply hf hU.measurable_set], },
  { intros U hU,
    rw [map_apply hf hU.measurable_set, ← hμ.inner_regular_eq (hU.preimage f.continuous)],
    refine ge_of_eq _, apply supr_congr (preimage f) h2f,
    intro K, apply supr_congr_Prop f.compact_preimage, intro hK,
    apply supr_congr_Prop h3f.preimage_subset_preimage_iff, intro h2U,
    rw [map_apply hf hK.measurable_set] }
end

protected lemma smul (hμ : μ.regular) {x : ℝ≥0∞} (hx : x < ∞) :
  (x • μ).regular :=
begin
  split,
  { intros K hK, exact ennreal.mul_lt_top hx (hμ.lt_top_of_is_compact hK) },
  { intros A hA, rw [coe_smul],
    refine le_trans _ (ennreal.mul_left_mono $ hμ.outer_regular hA),
    simp only [infi_and'], simp only [infi_subtype'],
    haveI : nonempty {s : set α // is_open s ∧ A ⊆ s} := ⟨⟨set.univ, is_open_univ, subset_univ _⟩⟩,
    rw [ennreal.mul_infi], refl', exact ne_of_lt hx },
  { intros U hU, rw [coe_smul], refine le_trans (ennreal.mul_left_mono $ hμ.inner_regular hU) _,
    simp only [supr_and'], simp only [supr_subtype'],
    rw [ennreal.mul_supr], refl' }
end

/-- A regular measure in a σ-compact space is σ-finite. -/
protected lemma sigma_finite [opens_measurable_space α] [t2_space α] [sigma_compact_space α]
  (hμ : regular μ) : sigma_finite μ :=
⟨⟨{ set := compact_covering α,
  set_mem := λ n, (is_compact_compact_covering α n).measurable_set,
  finite := λ n, hμ.lt_top_of_is_compact $ is_compact_compact_covering α n,
  spanning := Union_compact_covering α }⟩⟩

end regular

end measure
end measure_theory

lemma is_compact.measure_lt_top_of_nhds_within [topological_space α]
  {s : set α} {μ : measure α} (h : is_compact s) (hμ : ∀ x ∈ s, μ.finite_at_filter (𝓝[s] x)) :
  μ s < ∞ :=
is_compact.induction_on h (by simp) (λ s t hst ht, (measure_mono hst).trans_lt ht)
  (λ s t hs ht, (measure_union_le s t).trans_lt (ennreal.add_lt_top.2 ⟨hs, ht⟩)) hμ

lemma is_compact.measure_lt_top [topological_space α] {s : set α} {μ : measure α}
  [locally_finite_measure μ] (h : is_compact s) :
  μ s < ∞ :=
h.measure_lt_top_of_nhds_within $ λ x hx, μ.finite_at_nhds_within _ _