import Std

def function (n : Nat) : Nat := match n with
  | 0 => 0 + 1
  | Nat.succ n => function n + 1
termination_by (n,0)

example : function 150 = 151 := by
  conv =>
    lhs
    cbv

example : ((1,1).1,1).1 = 1 := by
  conv =>
    lhs
    cbv


def f : Unit -> Nat × Nat := fun _ => (1, 2)

example : (f ()).2 = 2 := by
  conv =>
    lhs
    cbv

def g : Unit → (Nat → Nat) × (Nat → Nat) := fun _ => (fun x => x + 1, fun x => x + 3)

example : (g ()).1 6 = 7 := by
  conv =>
    lhs
    cbv

example : "abx" ++ "c" = "a" ++ "bxc" := by
  conv =>
    lhs
    cbv
  conv =>
    rhs
    cbv

example : instHAdd.1 2 2 = 4 := by
  conv =>
    lhs
    cbv

example : (fun y : Nat → Nat => (fun x => y x)) Nat.succ 1 = 2 := by
  conv =>
    lhs
    cbv

example : (Std.TreeMap.empty.insert "a" "b" : Std.TreeMap String String).toList = [("a", "b")] := by
  conv =>
    lhs
    cbv

theorem array_test : (List.replicate 200 5 : List Nat).reverse = List.replicate 200 5 := by
  conv =>
    lhs
    cbv
  conv =>
    rhs
    cbv

def testFun (l : List Nat) : Nat := Id.run do
  let mut i := 0
  for _ in l do
    i := i + 1
  return i

-- Possibly a good benchmark for dealing with let expressions
example : testFun [1,2,3,4,5] = 5 := by
  conv =>
    lhs
    cbv

example : "ab".length + "ab".length = ("ab" ++ "ab").length := by
  conv =>
    lhs
    cbv
  conv =>
    rhs
    cbv

example : (((Std.TreeMap.empty : Std.TreeMap Nat Nat).insert 2 4).toList ++ [(5, 6)]).reverse = [(5,6), (2,4)] := by
  conv =>
    lhs
    cbv

def h := ()

example : h = () := by
  conv =>
    lhs
    cbv

def IsSubseq (s₁ : String) (s₂ : String) : Prop :=
  List.Sublist s₁.toList s₂.toList

def vowels : List Char := ['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U']

def removeVowels (s : String) : String :=
    String.ofList (s.toList.filter (· ∉ vowels))

example : removeVowels "abcdef" = "bcdf" := by
  conv =>
    lhs
    cbv

example : removeVowels "abcdef\nghijklm" = "bcdf\nghjklm" := by
  conv =>
    lhs
    cbv

example : removeVowels "aaaaa" = "" := by
  conv =>
    lhs
    cbv

example : removeVowels "aaBAA" = "B" := by
  conv =>
    lhs
    cbv

example : removeVowels "zbcd" = "zbcd" := by
  conv =>
    lhs
    cbv

def Nat.factorial : Nat → Nat
  | 0 => 1
  | .succ n => Nat.succ n * factorial n

notation:10000 n "!" => Nat.factorial n

def Nat.brazilianFactorial : Nat → Nat
  | .zero => 1
  | .succ n => (Nat.succ n)! * brazilianFactorial n

def special_factorial (n : Nat) : Nat :=
  special_factorial.go n 1 1 0
where
  go (n fact brazilFact curr : Nat) : Nat :=
    if _h: curr >= n
    then brazilFact
    else
      let fact' := (curr + 1) * fact
      let brazilFact' := fact' * brazilFact
      special_factorial.go n fact' brazilFact' (Nat.succ curr)
  termination_by n - curr

example : Nat.brazilianFactorial 4 = 288 := by
  conv =>
    lhs
    cbv

example : special_factorial 4 = 288 := by
  conv =>
    lhs
    cbv

example : Nat.brazilianFactorial 5 = 34560 := by
  conv =>
    lhs
    cbv

example : Nat.brazilianFactorial 7 = 125411328000 := by
  conv =>
    lhs
    cbv

attribute [cbv_opaque] Std.DHashMap.emptyWithCapacity
attribute [cbv_opaque] Std.DHashMap.insert
attribute [cbv_eval] Std.DHashMap.contains_emptyWithCapacity
attribute [cbv_eval] Std.DHashMap.contains_insert

example : ((Std.DHashMap.emptyWithCapacity : Std.DHashMap Nat (fun _ => Nat)).insert 5 3).contains 5 = true := by
  conv =>
    lhs
    cbv

def myConst : Nat := Nat.zero

@[cbv_eval] theorem myConst_spec : myConst = 0 := by rfl

example : myConst = 0 := by conv => lhs; cbv

def myAdd (m n : Nat) := match m with
| 0 => n
| m' + 1 => (myAdd m' n) + 1

@[cbv_eval] theorem myAdd_test : myAdd 22 23 = 45 := by rfl

theorem fast_path : myAdd 22 23 = 45 := by conv => lhs; cbv

/--
info: theorem fast_path : myAdd 22 23 = 45 :=
Eq.mpr
  (id
    ((fun a a_1 e_a =>
        Eq.rec (motive := fun a_2 e_a => ∀ (a_3 : Nat), (a = a_3) = (a_2 = a_3)) (fun a_2 => Eq.refl (a = a_2)) e_a)
      (myAdd 22 23) 45 (Eq.trans myAdd_test (Eq.refl 45)) 45))
  (Eq.refl 45)
-/
#guard_msgs in
#print fast_path

theorem slow_path : myAdd 0 1 = 1 := by conv => lhs; cbv

/--
info: theorem slow_path : myAdd 0 1 = 1 :=
Eq.mpr
  (id
    ((fun a a_1 e_a =>
        Eq.rec (motive := fun a_2 e_a => ∀ (a_3 : Nat), (a = a_3) = (a_2 = a_3)) (fun a_2 => Eq.refl (a = a_2)) e_a)
      (myAdd 0 1) 1 (Eq.trans (myAdd.eq_1 1) (Eq.refl 1)) 1))
  (Eq.refl 1)
-/
#guard_msgs in
#print slow_path

/-! ## Or/And short-circuit evaluation -/

theorem or_test_1 : (1 < 2 ∨ (10000).factorial = 0) = True := by cbv

/--
info: theorem or_test_1 : (1 < 2 ∨ 10000! = 0) = True :=
of_eq_true
  (Eq.trans
    (congrFun'
      (congrArg Eq
        (Lean.Sym.or_eq_true_left (1 < 2) (10000! = 0) (Lean.Sym.Nat.lt_eq_true 1 2 (eagerReduce (Eq.refl true)))))
      True)
    (eq_self True))
-/
#guard_msgs in
#print or_test_1

theorem or_test_2 : (3 < 2 ∨ 1 < 2) = True := by cbv

/--
info: theorem or_test_2 : (3 < 2 ∨ 1 < 2) = True :=
of_eq_true
  (Eq.trans
    (congrFun'
      (congrArg Eq
        (Eq.trans (Lean.Sym.or_eq_right (3 < 2) (1 < 2) (Lean.Sym.Nat.lt_eq_false 3 2 (eagerReduce (Eq.refl false))))
          (Lean.Sym.Nat.lt_eq_true 1 2 (eagerReduce (Eq.refl true)))))
      True)
    (eq_self True))
-/
#guard_msgs in
#print or_test_2

theorem or_test_3 : (3 < 2 ∨ 5 < 4) = False := by cbv

/--
info: theorem or_test_3 : (3 < 2 ∨ 5 < 4) = False :=
of_eq_true
  (Eq.trans
    (congrFun'
      (congrArg Eq
        (Eq.trans (Lean.Sym.or_eq_right (3 < 2) (5 < 4) (Lean.Sym.Nat.lt_eq_false 3 2 (eagerReduce (Eq.refl false))))
          (Lean.Sym.Nat.lt_eq_false 5 4 (eagerReduce (Eq.refl false)))))
      False)
    (eq_self False))
-/
#guard_msgs in
#print or_test_3

theorem and_test_1 : (3 < 2 ∧ (10000).factorial = 0) = False := by cbv

/--
info: theorem and_test_1 : (3 < 2 ∧ 10000! = 0) = False :=
of_eq_true
  (Eq.trans
    (congrFun'
      (congrArg Eq
        (Lean.Sym.and_eq_false_left (3 < 2) (10000! = 0) (Lean.Sym.Nat.lt_eq_false 3 2 (eagerReduce (Eq.refl false)))))
      False)
    (eq_self False))
-/
#guard_msgs in
#print and_test_1

theorem and_test_2 : (1 < 2 ∧ 3 < 4) = True := by cbv

/--
info: theorem and_test_2 : (1 < 2 ∧ 3 < 4) = True :=
of_eq_true
  (Eq.trans
    (congrFun'
      (congrArg Eq
        (Eq.trans (Lean.Sym.and_eq_left (1 < 2) (3 < 4) (Lean.Sym.Nat.lt_eq_true 1 2 (eagerReduce (Eq.refl true))))
          (Lean.Sym.Nat.lt_eq_true 3 4 (eagerReduce (Eq.refl true)))))
      True)
    (eq_self True))
-/
#guard_msgs in
#print and_test_2

theorem and_test_3 : (1 < 2 ∧ 5 < 4) = False := by cbv

/--
info: theorem and_test_3 : (1 < 2 ∧ 5 < 4) = False :=
of_eq_true
  (Eq.trans
    (congrFun'
      (congrArg Eq
        (Eq.trans (Lean.Sym.and_eq_left (1 < 2) (5 < 4) (Lean.Sym.Nat.lt_eq_true 1 2 (eagerReduce (Eq.refl true))))
          (Lean.Sym.Nat.lt_eq_false 5 4 (eagerReduce (Eq.refl false)))))
      False)
    (eq_self False))
-/
#guard_msgs in
#print and_test_3

theorem or_and : (1 < 2 ∨ (3 < 2 ∧ (10000).factorial = 0)) = True := by cbv

/--
info: theorem or_and : (1 < 2 ∨ 3 < 2 ∧ 10000! = 0) = True :=
of_eq_true
  (Eq.trans
    (congrFun'
      (congrArg Eq
        (Lean.Sym.or_eq_true_left (1 < 2) (3 < 2 ∧ 10000! = 0)
          (Lean.Sym.Nat.lt_eq_true 1 2 (eagerReduce (Eq.refl true)))))
      True)
    (eq_self True))
-/
#guard_msgs in
#print or_and

theorem and_or : (3 < 2 ∧ (1 < 2 ∨ (10000).factorial = 0)) = False := by cbv

/--
info: theorem and_or : (3 < 2 ∧ (1 < 2 ∨ 10000! = 0)) = False :=
of_eq_true
  (Eq.trans
    (congrFun'
      (congrArg Eq
        (Lean.Sym.and_eq_false_left (3 < 2) (1 < 2 ∨ 10000! = 0)
          (Lean.Sym.Nat.lt_eq_false 3 2 (eagerReduce (Eq.refl false)))))
      False)
    (eq_self False))
-/
#guard_msgs in
#print and_or
