import Erdos1135.ND.LogTime.Clock
import Erdos1135.ND.LogTime.Statement

/-!
# Count splice for logarithmic-time ND31

The top-block argument uses one common natural budget.  The clock theorem
places that budget below the public `C * log N` allowance for every integer in
the block, so the public bad set injects into the common bounded bad event.
-/

namespace Erdos1135
namespace ND

noncomputable section

local instance ndLogTimeCountDecidable (p : Prop) : Decidable p :=
  Classical.propDecidable p

theorem oddSyracuseLogTimeBad_not_hitsWithin_of_budget_le
    {Ctime : ℝ} {b N U : ℕ}
    (hbad : N ∈ oddSyracuseLogTimeBadSet Ctime b)
    (hU : (U : ℝ) ≤ Ctime * Real.log (N : ℝ)) :
    ¬ Tao.syracuseHitsAtMostWithin N b U := by
  intro hhit
  rcases hhit with ⟨m, hmU, hm⟩
  apply hbad.2.2
  have hmUReal : (m : ℝ) ≤ (U : ℝ) := by exact_mod_cast hmU
  exact ⟨m, hmUReal.trans hU, hm⟩

private theorem card_oddBlock_le_two_mul_endpoint_logTime
    {y X : ℝ} (hy : 1 ≤ y) (hX : 1 ≤ X)
    (hupper : y ^ alpha = X) :
    ((oddBlock y).card : ℝ) ≤ 2 * X := by
  classical
  have hy0 : 0 ≤ y := zero_le_one.trans hy
  have hX0 : 0 ≤ X := zero_le_one.trans hX
  have hsubset : oddBlock y ⊆ Finset.range (Nat.floor X + 1) := by
    intro n hn
    have hnBounds := (Tao.taoNyOddWindow_mem hy0).1 hn
    have hnFloor : n ≤ Nat.floor X := by
      apply (Nat.le_floor_iff hX0).2
      simpa only [hupper] using hnBounds.2.1
    exact Finset.mem_range.mpr (Nat.lt_succ_of_le hnFloor)
  calc
    ((oddBlock y).card : ℝ) ≤
        ((Finset.range (Nat.floor X + 1)).card : ℕ) := by
      exact_mod_cast Finset.card_le_card hsubset
    _ = (Nat.floor X : ℝ) + 1 := by simp
    _ ≤ X + 1 := by nlinarith [Nat.floor_le hX0]
    _ ≤ 2 * X := by linarith

/-- The inclusive top public bad count is controlled by the uniform-block
probability of failing one common natural budget. -/
theorem oddSyracuseLogTimeBadTopRatio_le_two_mul_uniformWithinBad
    {Ctime : ℝ} {b U : ℕ} {y X : ℝ}
    (hy : 1 ≤ y) (hX : 1 ≤ X) (hupper : y ^ alpha = X)
    (hwindow : (oddBlock y).Nonempty)
    (hclock : ∀ N : ℕ, Nat.ceil y ≤ N →
      (U : ℝ) ≤ Ctime * Real.log (N : ℝ)) :
    (natCountLEReal
        {n : ℕ | n ∈ oddSyracuseLogTimeBadSet Ctime b ∧
          Nat.ceil y ≤ n} X : ℝ) / X ≤
      2 * Tao.pmfProb (uniformOddBlockPMF y hwindow)
        {N | ¬ Tao.syracuseHitsAtMostWithin N.1 b U} := by
  let E : Set ℕ := {n | ¬ Tao.syracuseHitsAtMostWithin n b U}
  let topCard :=
    ((oddBlock y).filter fun n => n ∈ E).card
  let blockCard := (oddBlock y).card
  have hy0 : 0 ≤ y := zero_le_one.trans hy
  have hX0 : 0 ≤ X := zero_le_one.trans hX
  have hcountNat :
      natCountLEReal
          {n : ℕ | n ∈ oddSyracuseLogTimeBadSet Ctime b ∧
            Nat.ceil y ≤ n} X ≤ topCard := by
    unfold natCountLEReal natCountLE
    rw [Terras.natCount_eq_count, Nat.count_eq_card_filter_range]
    apply Finset.card_le_card
    intro n hn
    simp only [Finset.mem_filter, Finset.mem_range, Set.mem_setOf_eq] at hn ⊢
    rcases hn with ⟨hnRange, hnBad, hny⟩
    have hnFloor : n ≤ Nat.floor X := Nat.lt_succ_iff.mp hnRange
    have hnUpper : (n : ℝ) ≤ y ^ alpha := by
      rw [hupper]
      exact (Nat.le_floor_iff hX0).mp hnFloor
    have hnLower : y ≤ (n : ℝ) := Nat.ceil_le.mp hny
    have hnOdd : n % 2 = 1 := Nat.odd_iff.mp hnBad.2.1
    refine ⟨(Tao.taoNyOddWindow_mem hy0).2
      ⟨hnLower, hnUpper, hnOdd⟩, ?_⟩
    change n ∈ E
    simpa only [E, Set.mem_setOf_eq] using
      (oddSyracuseLogTimeBad_not_hitsWithin_of_budget_le
        hnBad (hclock n hny))
  have hcountReal :
      (natCountLEReal
          {n : ℕ | n ∈ oddSyracuseLogTimeBadSet Ctime b ∧
            Nat.ceil y ≤ n} X : ℝ) ≤ (topCard : ℝ) := by
    exact_mod_cast hcountNat
  have hXpos : 0 < X := zero_lt_one.trans_le hX
  have hblockPosNat : 0 < blockCard := Finset.card_pos.mpr hwindow
  have hblockPos : (0 : ℝ) < blockCard := by exact_mod_cast hblockPosNat
  have hblockCard : (blockCard : ℝ) ≤ 2 * X :=
    card_oddBlock_le_two_mul_endpoint_logTime hy hX hupper
  have hpmf :
      Tao.pmfProb (uniformOddBlockPMF y hwindow)
          {N | N.1 ∈ E} =
        (topCard : ℝ) / (blockCard : ℝ) := by
    convert pmfProb_uniformOddBlockPMF_eq_card_filter_div y hwindow E using 1 <;>
      simp only [topCard, blockCard] <;> congr 2 <;>
        (apply congrArg Finset.card; ext n; simp)
  have hpmfDirect :
      Tao.pmfProb (uniformOddBlockPMF y hwindow)
          {N | ¬ Tao.syracuseHitsAtMostWithin N.1 b U} =
        (topCard : ℝ) / (blockCard : ℝ) := by
    simpa only [E, Set.mem_setOf_eq] using hpmf
  have htopNonneg : (0 : ℝ) ≤ topCard := by positivity
  have hprod :
      (topCard : ℝ) * (blockCard : ℝ) ≤
        (2 * (topCard : ℝ)) * X := by
    have := mul_le_mul_of_nonneg_left hblockCard htopNonneg
    nlinarith
  calc
    (natCountLEReal
        {n : ℕ | n ∈ oddSyracuseLogTimeBadSet Ctime b ∧
          Nat.ceil y ≤ n} X : ℝ) / X ≤ (topCard : ℝ) / X :=
      div_le_div_of_nonneg_right hcountReal hXpos.le
    _ ≤ (2 * (topCard : ℝ)) / (blockCard : ℝ) :=
      (div_le_div_iff₀ hXpos hblockPos).2 hprod
    _ = 2 * ((topCard : ℝ) / (blockCard : ℝ)) := by ring
    _ = 2 * Tao.pmfProb (uniformOddBlockPMF y hwindow)
        {N | ¬ Tao.syracuseHitsAtMostWithin N.1 b U} := by
      rw [hpmfDirect]

/-- Composition of the common-budget count adapter with the scheduled
fixed-target bridge. -/
theorem oddSyracuseLogTimeBadTopRatio_le_two_mul_timeTerms
    {Ctime : ℝ} {b T : ℕ} {q y X : ℝ}
    (hb : 1 ≤ b) (hq : 1 ≤ q) (hy : 1 ≤ y) (hX : 1 ≤ X)
    (hupper : y ^ alpha = X)
    (hwindow : (oddBlock y).Nonempty)
    (hmass : 0 < Tao.logFinsetMass (oddBlock y))
    (hclock : ∀ N : ℕ, Nat.ceil y ≤ N →
      ((Tao.taoSection5N0 (Nat.floor q) + T : ℕ) : ℝ) ≤
        Ctime * Real.log (N : ℝ)) :
    (natCountLEReal
        {n : ℕ | n ∈ oddSyracuseLogTimeBadSet Ctime b ∧
          Nat.ceil y ≤ n} X : ℝ) / X ≤
      2 *
        (uniformScheduledNoHitProbability q y hwindow +
          passFullL1 q y hq hwindow hmass +
          Tao.pmfProb (logOddBlockPMF y hmass)
            {N | ¬ Tao.syracuseHitsAtMostWithin N.1 b T}) := by
  calc
    (natCountLEReal
        {n : ℕ | n ∈ oddSyracuseLogTimeBadSet Ctime b ∧
          Nat.ceil y ≤ n} X : ℝ) / X ≤
        2 * Tao.pmfProb (uniformOddBlockPMF y hwindow)
          {N | ¬ Tao.syracuseHitsAtMostWithin N.1 b
            (Tao.taoSection5N0 (Nat.floor q) + T)} :=
      oddSyracuseLogTimeBadTopRatio_le_two_mul_uniformWithinBad
        hy hX hupper hwindow hclock
    _ ≤ 2 *
        (uniformScheduledNoHitProbability q y hwindow +
          passFullL1 q y hq hwindow hmass +
          Tao.pmfProb (logOddBlockPMF y hmass)
            {N | ¬ Tao.syracuseHitsAtMostWithin N.1 b T}) := by
      exact mul_le_mul_of_nonneg_left
        (pmfProb_uniformOddBlock_fixedTargetBadWithin_add_le
          hb hq hwindow hmass) (by norm_num)

theorem natCountLEReal_oddSyracuseLogTimeBad_bottom_le_ceil
    (Ctime : ℝ) (b : ℕ) (y X : ℝ) :
    natCountLEReal
        {n : ℕ | n ∈ oddSyracuseLogTimeBadSet Ctime b ∧
          n < Nat.ceil y} X ≤ Nat.ceil y := by
  unfold natCountLEReal natCountLE
  exact Terras.natCount_le_of_subset_Iio
    (fun _n hn => hn.2) (Nat.floor X + 1)

theorem oddSyracuseLogTimeBadRatio_eq_zero_of_le
    {Ctime : ℝ} (hCtime : 0 ≤ Ctime)
    {b : ℕ} {X : ℝ} (hX0 : 0 ≤ X) (hXb : X ≤ (b : ℝ)) :
    oddSyracuseLogTimeBadRatio Ctime b X = 0 := by
  have hcount : natCountLEReal (oddSyracuseLogTimeBadSet Ctime b) X = 0 := by
    simp only [natCountLEReal, natCountLE, Terras.natCount_eq_count,
      Nat.count_eq_card_filter_range]
    rw [Finset.card_eq_zero]
    simp only [Finset.filter_eq_empty_iff, Finset.mem_range]
    intro n hnRange hnBad
    have hnFloor : n ≤ Nat.floor X := Nat.lt_succ_iff.mp hnRange
    have hnX : (n : ℝ) ≤ X := (Nat.le_floor_iff hX0).mp hnFloor
    have hnb : n ≤ b := by exact_mod_cast hnX.trans hXb
    apply hnBad.2.2
    have hnOne : (1 : ℝ) ≤ (n : ℝ) := by
      exact_mod_cast hnBad.1
    exact ⟨0, by
      simpa only [Nat.cast_zero] using
        mul_nonneg hCtime (Real.log_nonneg hnOne),
      by simpa only [Function.iterate_zero, id_eq] using hnb⟩
  simp only [oddSyracuseLogTimeBadRatio, hcount, Nat.cast_zero, zero_div]

theorem oddSyracuseLogTimeBadRatio_le_two
    (Ctime : ℝ) (b : ℕ) {X : ℝ} (hX : 1 ≤ X) :
    oddSyracuseLogTimeBadRatio Ctime b X ≤ 2 := by
  have hX0 : 0 ≤ X := zero_le_one.trans hX
  have hXpos : 0 < X := zero_lt_one.trans_le hX
  have hcountNat :
      natCountLEReal (oddSyracuseLogTimeBadSet Ctime b) X ≤
        Nat.floor X + 1 := by
    unfold natCountLEReal natCountLE
    exact Terras.natCount_le _ _
  have hcountReal :
      (natCountLEReal (oddSyracuseLogTimeBadSet Ctime b) X : ℝ) ≤
        2 * X := by
    calc
      (natCountLEReal (oddSyracuseLogTimeBadSet Ctime b) X : ℝ) ≤
          ((Nat.floor X + 1 : ℕ) : ℝ) := by exact_mod_cast hcountNat
      _ = (Nat.floor X : ℝ) + 1 := by norm_num
      _ ≤ X + 1 := by nlinarith [Nat.floor_le hX0]
      _ ≤ 2 * X := by linarith
  unfold oddSyracuseLogTimeBadRatio
  exact (div_le_iff₀ hXpos).2 hcountReal

/-- Full inclusive real-endpoint splice for the public logarithmic-time bad
set. -/
theorem oddSyracuseLogTimeBadRatio_le_ceil_div_add_two_mul_timeTerms
    {Ctime : ℝ} {b T : ℕ} {q y X : ℝ}
    (hb : 1 ≤ b) (hq : 1 ≤ q) (hy : 1 ≤ y) (hX : 1 ≤ X)
    (hupper : y ^ alpha = X)
    (hwindow : (oddBlock y).Nonempty)
    (hmass : 0 < Tao.logFinsetMass (oddBlock y))
    (hclock : ∀ N : ℕ, Nat.ceil y ≤ N →
      ((Tao.taoSection5N0 (Nat.floor q) + T : ℕ) : ℝ) ≤
        Ctime * Real.log (N : ℝ)) :
    oddSyracuseLogTimeBadRatio Ctime b X ≤
      (Nat.ceil y : ℝ) / X +
        2 *
          (uniformScheduledNoHitProbability q y hwindow +
            passFullL1 q y hq hwindow hmass +
            Tao.pmfProb (logOddBlockPMF y hmass)
              {N | ¬ Tao.syracuseHitsAtMostWithin N.1 b T}) := by
  let bottom : Set ℕ :=
    {n : ℕ | n ∈ oddSyracuseLogTimeBadSet Ctime b ∧ n < Nat.ceil y}
  let top : Set ℕ :=
    {n : ℕ | n ∈ oddSyracuseLogTimeBadSet Ctime b ∧ Nat.ceil y ≤ n}
  have hsplit : oddSyracuseLogTimeBadSet Ctime b = bottom ∪ top := by
    ext n
    simp only [bottom, top, Set.mem_union, Set.mem_setOf_eq]
    constructor
    · intro hn
      by_cases hny : n < Nat.ceil y
      · exact Or.inl ⟨hn, hny⟩
      · exact Or.inr ⟨hn, Nat.le_of_not_gt hny⟩
    · rintro (⟨hn, _⟩ | ⟨hn, _⟩) <;> exact hn
  have hdisj : Disjoint bottom top := by
    apply Set.disjoint_left.2
    intro n hnBottom hnTop
    exact (not_lt_of_ge hnTop.2) hnBottom.2
  have hcountEq :
      natCountLEReal (oddSyracuseLogTimeBadSet Ctime b) X =
        natCountLEReal bottom X + natCountLEReal top X := by
    unfold natCountLEReal natCountLE
    rw [hsplit, Terras.natCount_union_of_disjoint hdisj]
  have hbottom : natCountLEReal bottom X ≤ Nat.ceil y :=
    natCountLEReal_oddSyracuseLogTimeBad_bottom_le_ceil Ctime b y X
  have hbottomReal :
      (natCountLEReal bottom X : ℝ) ≤ (Nat.ceil y : ℝ) := by
    exact_mod_cast hbottom
  have hcountReal :
      (natCountLEReal (oddSyracuseLogTimeBadSet Ctime b) X : ℝ) ≤
        (Nat.ceil y : ℝ) + (natCountLEReal top X : ℝ) := by
    rw [hcountEq, Nat.cast_add]
    linarith
  have hXpos : 0 < X := zero_lt_one.trans_le hX
  have hsplitRatio :
      oddSyracuseLogTimeBadRatio Ctime b X ≤
        (Nat.ceil y : ℝ) / X + (natCountLEReal top X : ℝ) / X := by
    unfold oddSyracuseLogTimeBadRatio
    calc
      (natCountLEReal (oddSyracuseLogTimeBadSet Ctime b) X : ℝ) / X ≤
          ((Nat.ceil y : ℝ) + (natCountLEReal top X : ℝ)) / X :=
        div_le_div_of_nonneg_right hcountReal hXpos.le
      _ = (Nat.ceil y : ℝ) / X +
          (natCountLEReal top X : ℝ) / X := by ring
  have htop := oddSyracuseLogTimeBadTopRatio_le_two_mul_timeTerms
    hb hq hy hX hupper hwindow hmass hclock
  change (natCountLEReal top X : ℝ) / X ≤ _ at htop
  linarith

end


end ND
end Erdos1135
