import Std.Tactic.Do
import Mathlib


namespace Paradox

-- https://leanprover.zulipchat.com/#narrow/channel/236446-Type-theory/topic/Paradoxes.20and.20Type.20Universes/with/538016579

axiom Bad : Type

axiom bad : (α : Type) × α ↪ Bad

noncomputable def k (P : Bad → Prop) : Bad :=
  bad ⟨Bad → Prop, P⟩

def Q (b : Bad) : Prop :=
  ∃ P, k P = b ∧ ¬P b

theorem k_injective : k.Injective :=
  fun _ _ hab => eq_of_heq
    (Sigma.mk.inj (bad.injective hab)).2

theorem down (h : Q (k Q)) : ¬Q (k Q) :=
  h.elim fun _ hP =>
    (congrArg Not (congrFun (k_injective hP.1) (k Q))).mp hP.2

theorem up (h : ¬Q (k Q)) : Q (k Q) :=
  ⟨Q, rfl, h⟩

theorem false : False :=
  down (up fun h => down h h) (up fun h => down h h)

-- 'false' depends on axioms: [Bad, bad]
#print axioms Paradox.false

end Paradox



inductive Fmt where
  | Arg : Fmt → Fmt
  | Nat : Fmt → Fmt
  | Char : Char → Fmt → Fmt
  | End

def toFmt : List Char → Fmt
  | '*' :: xs => .Arg <| toFmt xs
  | '#' :: xs => .Nat <| toFmt xs
  | x :: xs => .Char x <| toFmt xs
  | [] => .End

def FormatType : Fmt → Type 1
  | .Arg fmt => {α : Type} → [ToString α] → α → FormatType fmt
  | .Nat fmt => Nat → FormatType fmt
  | .Char _ fmt => FormatType fmt
  | .End => PLift String

def format (fmt : String) : FormatType <| toFmt fmt.toList :=
  let rec formatAux (acc : String) : (fmt : Fmt) → FormatType fmt
    | .Arg fmt => fun x ↦ formatAux (acc ++ toString x) fmt
    | .Nat fmt => fun x ↦ formatAux (acc ++ "#" ++ toString x) fmt
    | .Char c fmt => formatAux (acc.push c) fmt
    | .End => .up acc
  formatAux "" <| toFmt fmt.toList

#eval format "Hello * # * *" (-1) 1 [42, 69] "Meow" |>.down





-- TODO: prove binary search

def Array.binarySearch (A : Array ℕ) (x : ℕ) := Id.run do
  let n := A.size
  let mut l : Fin (n + 1) := 0
  let mut r : Fin (n + 1) := ⟨n, by grind⟩
  while h : l < r do
    let m : Fin (n + 1) := ⟨(l.val + r.val) / 2, by grind⟩
    if A[m]'(by grind) < x then
      l := m + 1
    else
      r := m
  return l

open Std.Do in
lemma binarySearchCorrect (A : Array ℕ) (hA : A.Pairwise (· ≤ ·)) : ∀ i (hi : i < A.binarySearch x), A[i] < x := by
  sorry
