import Mathlib.Tactic
import Mathlib.Data.Real.Basic
import Mathlib.Topology.Basic
import Mathlib.Data.Finset.Basic
import Mathlib.Algebra.BigOperators.Basic
import Mathlib.Order.CompleteLattice

/-!
Self-contained slice for Chapter 11.3 target theorem:
theorem integ_of_piecewise_const
-/

namespace Chapter11

/-- Bounded intervals on ℝ, as in Section 11.1 -/
inductive BoundedInterval where
  | Ioo (a b:ℝ) : BoundedInterval
  | Icc (a b:ℝ) : BoundedInterval
  | Ioc (a b:ℝ) : BoundedInterval
  | Ico (a b:ℝ) : BoundedInterval

open BoundedInterval

/-- Coercion of a bounded interval to a set, as in Section 11.1 -/
@[coe]
def BoundedInterval.toSet (I: BoundedInterval) : Set ℝ := match I with
  | Ioo a b => .Ioo a b
  | Icc a b => .Icc a b
  | Ioc a b => .Ioc a b
  | Ico a b => .Ico a b

instance BoundedInterval.inst_coeSet : Coe BoundedInterval (Set ℝ) where
  coe := toSet

open Classical in
/-- DecidableEq instance to allow Finset BoundedInterval, as in Section 11.1 -/
noncomputable instance BoundedInterval.decidableEq : DecidableEq BoundedInterval :=
  instDecidableEqOfLawfulBEq

/-- Accessor for the left endpoint, as in Section 11.1 -/
abbrev BoundedInterval.a (I: BoundedInterval) : ℝ := match I with
  | Ioo a _ => a
  | Icc a _ => a
  | Ioc a _ => a
  | Ico a _ => a

/-- Accessor for the right endpoint, as in Section 11.1 -/
abbrev BoundedInterval.b (I: BoundedInterval) : ℝ := match I with
  | Ioo _ b => b
  | Icc _ b => b
  | Ioc _ b => b
  | Ico _ b => b

/-- Length of a bounded interval, as in Section 11.1 -/
abbrev BoundedInterval.length (I: BoundedInterval) : ℝ := max (I.b - I.a) 0

/-- Using ||ₗ subscript here to not override || -/
macro:max atomic("|" noWs) a:term noWs "|ₗ" : term => `(BoundedInterval.length $a)

/-- Subset relation on bounded intervals, as in Section 11.1 -/
instance BoundedInterval.instSubset : HasSubset BoundedInterval where
  Subset I J := ∀ x, x ∈ (I:Set ℝ) → x ∈ (J:Set ℝ)

/-- Partitions of a bounded interval, as in Section 11.1 -/
@[ext]
structure Partition (I: BoundedInterval) where
  intervals : Finset BoundedInterval
  exists_unique (x:ℝ) (hx : x ∈ (I:Set ℝ)) : ∃! J, J ∈ intervals ∧ x ∈ (J:Set ℝ)
  contains (J : BoundedInterval) (hJ : J ∈ intervals) : J ⊆ I

/-- Membership of a subinterval in a partition, as in Section 11.1 -/
instance Partition.instMembership (I: BoundedInterval) : Membership BoundedInterval (Partition I) where
  mem P J := J ∈ P.intervals

/-- Definition 11.2.1 -/
abbrev Constant {X Y:Type} (f: X → Y) : Prop := ∃ c, ∀ x, f x = c

open Classical in
/-- A canonical value for a constant function (noncomputable), Section 11.2 -/
noncomputable abbrev constant_value {X Y:Type} [hY: Nonempty Y] (f:X → Y) : Y :=
  if h: Constant f then h.choose else hY.some

/-- Constant-on a set, Section 11.2 -/
abbrev ConstantOn (f: ℝ → ℝ) (X: Set ℝ) : Prop := Constant (fun x : X ↦ f ↑x)

/-- Canonical value of a function on a set, Section 11.2 -/
noncomputable abbrev constant_value_on (f:ℝ → ℝ) (X: Set ℝ) : ℝ :=
  constant_value (fun x : X ↦ f ↑x)

/-- Definition 11.2.3 (Piecewise constant functions I) -/
abbrev PiecewiseConstantWith (f:ℝ → ℝ) {I: BoundedInterval} (P: Partition I) : Prop :=
  ∀ J ∈ P, ConstantOn f (J:Set ℝ)

/-- Definition 11.2.5 (Piecewise constant functions II) -/
abbrev PiecewiseConstantOn (f:ℝ → ℝ) (I: BoundedInterval) : Prop :=
  ∃ P : Partition I, PiecewiseConstantWith f P

/-- Definition 11.2.9 (Piecewise constant integral I) -/
noncomputable abbrev PiecewiseConstantWith.integ (f:ℝ → ℝ) {I: BoundedInterval} (P: Partition I)  :
  ℝ := ∑ J ∈ P.intervals, constant_value_on f (J:Set ℝ) * |J|ₗ

open Classical in
/-- Definition 11.2.14 (Piecewise constant integral II)  -/
noncomputable abbrev PiecewiseConstantOn.integ (f:ℝ → ℝ) (I: BoundedInterval) :
  ℝ := if h: PiecewiseConstantOn f I then PiecewiseConstantWith.integ f h.choose else 0

/-- Shorthand to view the integral given a witness of piecewise constancy, Section 11.2 -/
noncomputable abbrev PiecewiseConstantOn.integ' {f:ℝ → ℝ} {I: BoundedInterval}
  (_:PiecewiseConstantOn f I) := PiecewiseConstantOn.integ f I

namespace Chapter9

/-- Definition 9.6.1: boundedness on a set -/
abbrev BddOn (f:ℝ → ℝ) (X:Set ℝ) : Prop := ∃ M, ∀ x ∈ X, |f x| ≤ M

end Chapter9

open BoundedInterval Chapter9

/-- Definition 11.3.1 (Majorization of functions) -/
abbrev MajorizesOn (g f:ℝ → ℝ) (I: BoundedInterval) : Prop := ∀ x ∈ (I:Set ℝ), f x ≤ g x

/-- Minorization of functions, Section 11.3 -/
abbrev MinorizesOn (g f:ℝ → ℝ) (I: BoundedInterval) : Prop := ∀ x ∈ (I:Set ℝ), g x ≤ f x

theorem MinorizesOn.iff (g f:ℝ → ℝ) (I: BoundedInterval) :
  MinorizesOn g f I ↔ MajorizesOn f g I := by rfl

/-- Definition 11.3.2 (Upper and lower Riemann integrals) -/
noncomputable abbrev upper_integral (f:ℝ → ℝ) (I: BoundedInterval) : ℝ :=
  sInf ((PiecewiseConstantOn.integ · I) '' {g | MajorizesOn g f I ∧ PiecewiseConstantOn g I})

noncomputable abbrev lower_integral (f:ℝ → ℝ) (I: BoundedInterval) : ℝ :=
  sSup ((PiecewiseConstantOn.integ · I) '' {g | MinorizesOn g f I ∧ PiecewiseConstantOn g I})

/-- Definition 11.3.4 (Riemann integral)
As we permit junk values, the simplest definition for the Riemann integral is the upper integral. -/
noncomputable abbrev integ (f:ℝ → ℝ) (I: BoundedInterval) : ℝ := upper_integral f I

/-- Integrability on a bounded interval, Section 11.3 -/
noncomputable abbrev IntegrableOn (f:ℝ → ℝ) (I: BoundedInterval) : Prop :=
  BddOn f (I:Set ℝ) ∧ lower_integral f I = upper_integral f I

/-- Lemma 11.3.7 / Exercise 11.3.3 -/
theorem integ_of_piecewise_const {f:ℝ → ℝ} {I: BoundedInterval}
  (hf: PiecewiseConstantOn f I) :
  IntegrableOn f I ∧ integ f I = hf.integ' := by
end Chapter11