CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

| Download

Try doing some basic maths questions in the Lean Theorem Prover. Functions, real numbers, equivalence relations and groups. Click on README.md and then on "Open in CoCalc with one click".

Project: Xena
Views: 18536
License: APACHE
/-
Copyright (c) 2018 Simon Hudon. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Simon Hudon, Scott Morrison
-/

import tactic.interactive tactic.finish tactic.ext tactic.lift tactic.apply
       tactic.reassoc_axiom tactic.tfae tactic.elide tactic.ring_exp
       tactic.clear tactic.simp_rw

example (m n p q : nat) (h : m + n = p) : true :=
begin
  have : m + n = q,
  { generalize_hyp h' : m + n = x at h,
    guard_hyp h' := m + n = x,
    guard_hyp h := x = p,
    guard_target m + n = q,
    admit },
  have : m + n = q,
  { generalize_hyp h' : m + n = x at h ⊢,
    guard_hyp h' := m + n = x,
    guard_hyp h := x = p,
    guard_target x = q,
    admit },
  trivial
end

example (α : Sort*) (L₁ L₂ L₃ : list α)
  (H : L₁ ++ L₂ = L₃) : true :=
begin
  have : L₁ ++ L₂ = L₂,
  { generalize_hyp h : L₁ ++ L₂ = L at H,
    induction L with hd tl ih,
    case list.nil
    { tactic.cleanup,
      change list.nil = L₃ at H,
      admit },
    case list.cons
    { change list.cons hd tl = L₃ at H,
      admit } },
  trivial
end

example (x y : ℕ) (p q : Prop) (h : x = y) (h' : p ↔ q) : true :=
begin
  symmetry' at h,
  guard_hyp' h := y = x,
  guard_hyp' h' := p ↔ q,
  symmetry' at *,
  guard_hyp' h := x = y,
  guard_hyp' h' := q ↔ p,
  trivial
end

section apply_rules

example {a b c d e : nat} (h1 : a ≤ b) (h2 : c ≤ d) (h3 : 0 ≤ e) :
a + c * e + a + c + 0 ≤ b + d * e + b + d + e :=
add_le_add (add_le_add (add_le_add (add_le_add h1 (mul_le_mul_of_nonneg_right h2 h3)) h1 ) h2) h3

example {a b c d e : nat} (h1 : a ≤ b) (h2 : c ≤ d) (h3 : 0 ≤ e) :
a + c * e + a + c + 0 ≤ b + d * e + b + d + e :=
by apply_rules [add_le_add, mul_le_mul_of_nonneg_right]

@[user_attribute]
meta def mono_rules : user_attribute :=
{ name := `mono_rules,
  descr := "lemmas usable to prove monotonicity" }
attribute [mono_rules] add_le_add mul_le_mul_of_nonneg_right

example {a b c d e : nat} (h1 : a ≤ b) (h2 : c ≤ d) (h3 : 0 ≤ e) :
a + c * e + a + c + 0 ≤ b + d * e + b + d + e :=
by apply_rules [mono_rules]

example {a b c d e : nat} (h1 : a ≤ b) (h2 : c ≤ d) (h3 : 0 ≤ e) :
a + c * e + a + c + 0 ≤ b + d * e + b + d + e :=
by apply_rules mono_rules

end apply_rules

section h_generalize

variables {α β γ φ ψ : Type} (f : α → α → α → φ → γ)
          (x y : α) (a b : β) (z : φ)
          (h₀ : β = α) (h₁ : β = α) (h₂ : φ = β)
          (hx : x == a) (hy : y == b) (hz : z == a)
include f x y z a b hx hy hz

example : f x y x z = f (eq.rec_on h₀ a) (cast h₀ b) (eq.mpr h₁.symm a) (eq.mpr h₂ a) :=
begin
  guard_hyp_nums 16,
  h_generalize hp : a == p with hh,
  guard_hyp_nums 19,
  guard_hyp' hh := β = α,
  guard_target f x y x z = f p (cast h₀ b) p (eq.mpr h₂ a),
  h_generalize hq : _ == q,
  guard_hyp_nums 21,
  guard_target f x y x z = f p q p (eq.mpr h₂ a),
  h_generalize _ : _ == r,
  guard_hyp_nums 23,
  guard_target f x y x z = f p q p r,
  casesm* [_ == _, _ = _], refl
end

end h_generalize

section h_generalize

variables {α β γ φ ψ : Type} (f : list α → list α → γ)
          (x : list α) (a : list β) (z : φ)
          (h₀ : β = α) (h₁ : list β = list α)
          (hx : x == a)
include f x z a hx h₀ h₁

example : true :=
begin
  have : f x x = f (eq.rec_on h₀ a) (cast h₁ a),
  { guard_hyp_nums 11,
    h_generalize : a == p with _,
    guard_hyp_nums 13,
    guard_hyp' h := β = α,
    guard_target f x x = f p (cast h₁ a),
    h_generalize! : a == q ,
    guard_hyp_nums 13,
    guard_target ∀ q, f x x = f p q,
    casesm* [_ == _, _ = _],
    success_if_fail { refl },
    admit },
  trivial
end

end h_generalize

section tfae

example (p q r s : Prop)
  (h₀ : p ↔ q)
  (h₁ : q ↔ r)
  (h₂ : r ↔ s) :
  p ↔ s :=
begin
  scc,
end

example (p' p q r r' s s' : Prop)
  (h₀ : p' → p)
  (h₀ : p → q)
  (h₁ : q → r)
  (h₁ : r' → r)
  (h₂ : r ↔ s)
  (h₂ : s → p)
  (h₂ : s → s') :
  p ↔ s :=
begin
  scc,
end

example (p' p q r r' s s' : Prop)
  (h₀ : p' → p)
  (h₀ : p → q)
  (h₁ : q → r)
  (h₁ : r' → r)
  (h₂ : r ↔ s)
  (h₂ : s → p)
  (h₂ : s → s') :
  p ↔ s :=
begin
  scc',
  assumption
end

example : tfae [true, ∀ n : ℕ, 0 ≤ n * n, true, true] := begin
  tfae_have : 3 → 1, { intro h, constructor },
  tfae_have : 2 → 3, { intro h, constructor },
  tfae_have : 2 ← 1, { intros h n, apply nat.zero_le },
  tfae_have : 4 ↔ 2, { tauto },
  tfae_finish,
end

example : tfae [] := begin
  tfae_finish,
end

variables P Q R : Prop

example : tfae [P, Q, R] :=
begin
  have : P → Q := sorry, have : Q → R := sorry, have : R → P := sorry,
  --have : R → Q := sorry, -- uncommenting this makes the proof fail
  tfae_finish
end

example : tfae [P, Q, R] :=
begin
  have : P → Q := sorry, have : Q → R := sorry, have : R → P := sorry,
  have : R → Q := sorry, -- uncommenting this makes the proof fail
  tfae_finish
end

example : tfae [P, Q, R] :=
begin
  have : P ↔ Q := sorry, have : Q ↔ R := sorry,
  tfae_finish -- the success or failure of this tactic is nondeterministic!
end

example (p : unit → Prop) : tfae [p (), p ()] :=
begin
  tfae_have : 1 ↔ 2, from iff.rfl,
  tfae_finish
end

end tfae

section clear_aux_decl

example (n m : ℕ) (h₁ : n = m) (h₂ : ∃ a : ℕ, a = n ∧ a = m) : 2 * m = 2 * n :=
let ⟨a, ha⟩ := h₂ in
begin
  clear_aux_decl, -- subst will fail without this line
  subst h₁
end

example (x y : ℕ) (h₁ : ∃ n : ℕ, n * 1 = 2) (h₂ : 1 + 1 = 2 → x * 1 = y) : x = y :=
let ⟨n, hn⟩ := h₁ in
begin
  clear_aux_decl, -- finish produces an error without this line
  finish
end

end clear_aux_decl

section congr

example (c : Prop → Prop → Prop → Prop) (x x' y z z' : Prop)
  (h₀ : x ↔ x')
  (h₁ : z ↔ z') :
  c x y z ↔ c x' y z' :=
begin
  congr',
  { guard_target x = x', ext, assumption },
  { guard_target z = z', ext, assumption },
end

end congr

section convert_to

example {a b c d : ℕ} (H : a = c) (H' : b = d) : a + b = d + c :=
by {convert_to c + d = _ using 2, from H, from H', rw[add_comm]}

example {a b c d : ℕ} (H : a = c) (H' : b = d) : a + b = d + c :=
by {convert_to c + d = _ using 0, congr' 2, from H, from H', rw[add_comm]}

example (a b c d e f g N : ℕ) : (a + b) + (c + d) + (e + f) + g ≤ a + d + e + f + c + g + b :=
by {ac_change a + d + e + f + c + g + b ≤ _, refl}

end convert_to

section swap

example {α₁ α₂ α₃ : Type} : true :=
by {have : α₁, have : α₂, have : α₃, swap, swap,
    rotate, rotate, rotate, rotate 2, rotate 2, triv, recover}

end swap

section lift

example (n m k x z u : ℤ) (hn : 0 < n) (hk : 0 ≤ k + n) (hu : 0 ≤ u) (h : k + n = 2 + x) :
  k + n = m + x :=
begin
  lift n to ℕ using le_of_lt hn,
    guard_target (k + ↑n = m + x), guard_hyp hn := (0 : ℤ) < ↑n,
  lift m to ℕ,
    guard_target (k + ↑n = ↑m + x), tactic.swap, guard_target (0 ≤ m), tactic.swap,
    tactic.num_goals >>= λ n, guard (n = 2),
  lift (k + n) to ℕ using hk with l hl,
    guard_hyp l := ℕ, guard_hyp hl := ↑l = k + ↑n, guard_target (↑l = ↑m + x),
    tactic.success_if_fail (tactic.get_local `hk),
  lift x to ℕ with y hy,
    guard_hyp y := ℕ, guard_hyp hy := ↑y = x, guard_target (↑l = ↑m + x),
  lift z to ℕ with w,
    guard_hyp w := ℕ, tactic.success_if_fail (tactic.get_local `z),
  lift u to ℕ using hu with u rfl hu,
    guard_hyp hu := (0 : ℤ) ≤ ↑u,
  all_goals { admit }
end

-- test lift of functions
example (α : Type*) (f : α → ℤ) (hf : ∀ a, 0 ≤ f a) (hf' : ∀ a, f a < 1) (a : α) : 0 ≤ 2 * f a :=
begin
  lift f to α → ℕ using hf,
    guard_target ((0:ℤ) ≤ 2 * (λ i : α, (f i : ℤ)) a),
    guard_hyp hf' := ∀ a, ((λ i : α, (f i:ℤ)) a) < 1,
  trivial
end

instance can_lift_unit : can_lift unit unit :=
⟨id, λ x, true, λ x _, ⟨x, rfl⟩⟩

/- test whether new instances of `can_lift` are added as simp lemmas -/
run_cmd do l ← can_lift_attr.get_cache, guard (`can_lift_unit ∈ l)

/- test error messages -/
example (n : ℤ) (hn : 0 < n) : true :=
begin
  success_if_fail_with_msg {lift n to ℕ using hn} "lift tactic failed. The type of\n  hn\nis
  0 < n\nbut it is expected to be\n  0 ≤ n",
  success_if_fail_with_msg {lift (n : option ℤ) to ℕ}
    "Failed to find a lift from option ℤ to ℕ. Provide an instance of\n  can_lift (option ℤ) ℕ",
  trivial
end

example (n : ℤ) : ℕ :=
begin
  success_if_fail_with_msg {lift n to ℕ}
    "lift tactic failed. Tactic is only applicable when the target is a proposition.",
  exact 0
end

end lift

private meta def get_exception_message (t : lean.parser unit) : lean.parser string
| s := match t s with
       | result.success a s' := result.success "No exception" s
       | result.exception none pos s' := result.success "Exception no msg" s
       | result.exception (some msg) pos s' := result.success (msg ()).to_string s
       end

@[user_command] meta def test_parser1_fail_cmd
(_ : interactive.parse (lean.parser.tk "test_parser1")) : lean.parser unit :=
do
  let msg := "oh, no!",
  let t : lean.parser unit := tactic.fail msg,
  s ← get_exception_message t,
  if s = msg then tactic.skip
  else interaction_monad.fail "Message was corrupted while being passed through `lean.parser.of_tactic`"
.

-- Due to `lean.parser.of_tactic'` priority, the following *should not* fail with
-- a VM check error, and instead catch the error gracefully and just
-- run and succeed silently.
test_parser1

section category_theory
open category_theory
variables {C : Type} [category.{1} C]

example (X Y Z W : C) (x : X ⟶ Y) (y : Y ⟶ Z) (z z' : Z ⟶ W) (w : X ⟶ Z)
  (h : x ≫ y = w)
  (h' : y ≫ z = y ≫ z') :
  x ≫ y ≫ z = w ≫ z' :=
begin
  rw [h',reassoc_of h],
end

end category_theory

section is_eta_expansion
/- test the is_eta_expansion tactic -/
open function tactic
structure my_equiv (α : Sort*) (β : Sort*) :=
(to_fun    : α → β)
(inv_fun   : β → α)
(left_inv  : left_inverse inv_fun to_fun)
(right_inv : right_inverse inv_fun to_fun)

infix ` my≃ `:25 := my_equiv

protected def my_rfl {α} : α my≃ α :=
⟨id, λ x, x, λ x, rfl, λ x, rfl⟩

def eta_expansion_test : ℕ × ℕ := ((1,0).1,(1,0).2)
run_cmd do e ← get_env, x ← e.get `eta_expansion_test,
  let v := (x.value.get_app_args).drop 2,
  let nms := [`prod.fst, `prod.snd],
  guard $ expr.is_eta_expansion_test (nms.zip v) = some `((1, 0))

def eta_expansion_test2 : ℕ my≃ ℕ :=
⟨my_rfl.to_fun, my_rfl.inv_fun, λ x, rfl, λ x, rfl⟩

run_cmd do e ← get_env, x ← e.get `eta_expansion_test2,
  let v := (x.value.get_app_args).drop 2,
  projs ← e.structure_fields_full `my_equiv,
  b ← expr.is_eta_expansion_aux x.value (projs.zip v),
  guard $ b = some `(@my_rfl ℕ)

run_cmd do e ← get_env, x1 ← e.get `eta_expansion_test, x2 ← e.get `eta_expansion_test2,
  b1 ← expr.is_eta_expansion x1.value,
  b2 ← expr.is_eta_expansion x2.value,
  guard $ b1 = some `((1, 0)) ∧ b2 = some `(@my_rfl ℕ)

structure my_str (n : ℕ) := (x y : ℕ)

def dummy : my_str 3 := ⟨3, 1, 1⟩
def wrong_param : my_str 2 := ⟨2, dummy.1, dummy.2⟩
def right_param : my_str 3 := ⟨3, dummy.1, dummy.2⟩

run_cmd do e ← get_env,
  x ← e.get `wrong_param, o ← x.value.is_eta_expansion,
  guard o.is_none,
  x ← e.get `right_param, o ← x.value.is_eta_expansion,
  guard $ o = some `(dummy)


end is_eta_expansion

section elide

variables {x y z w : ℕ}
variables (h  : x + y + z ≤ w)
          (h' : x ≤ y + z + w)
include h h'

example : x + y + z ≤ w :=
begin
  elide 0 at h,
  elide 2 at h',
  guard_hyp h := @hidden _ (x + y + z ≤ w),
  guard_hyp h' := x ≤ @has_add.add (@hidden Type nat) (@hidden (has_add nat) nat.has_add)
                                   (@hidden ℕ (y + z)) (@hidden ℕ w),
  unelide at h,
  unelide at h',
  guard_hyp h' := x ≤ y + z + w,
  exact h, -- there was a universe problem in `elide`. `exact h` lets the kernel check
           -- the consistency of the universes
end

end elide

section struct_eq

@[ext]
structure foo (α : Type*) :=
(x y : ℕ)
(z : {z // z < x})
(k : α)
(h : x < y)

example {α : Type*} : Π (x y : foo α), x.x = y.x → x.y = y.y → x.z == y.z → x.k = y.k → x = y :=
foo.ext

example {α : Type*} : Π (x y : foo α), x = y ↔ x.x = y.x ∧ x.y = y.y ∧ x.z == y.z ∧ x.k = y.k :=
foo.ext_iff

example {α} (x y : foo α) (h : x = y) : y = x :=
begin
  ext,
  { guard_target' y.x = x.x, rw h },
  { guard_target' y.y = x.y, rw h },
  { guard_target' y.z == x.z, rw h },
  { guard_target' y.k = x.k, rw h },
end

end struct_eq

section ring_exp
  example (a b : ℤ) (n : ℕ) : (a + b)^(n + 2) = (a^2 + 2 * a * b + b^2) * (a + b)^n := by ring_exp
end ring_exp

section clear'

example {α} {β : α → Type} (a : α) (b : β a) : unit :=
begin
  success_if_fail { clear a b }, -- fails since `b` depends on `a`
  success_if_fail { clear' a },  -- fails since `b` depends on `a`
  clear' a b,
  guard_hyp_nums 2,
  exact ()
end

example {α} {β : α → Type} (a : α) : β a → unit :=
begin
  success_if_fail { clear' a }, -- fails since the target depends on `a`
  exact λ _, ()
end

end clear'

section clear_dependent

example {α} {β : α → Type} (a : α) (b : β a) : unit :=
begin
  success_if_fail { clear' a }, -- fails since `b` depends on `a`
  clear_dependent a,
  guard_hyp_nums 2,
  exact ()
end

example {α} {β : α → Type} (a : α) : β a → unit :=
begin
  success_if_fail { clear_dependent a }, -- fails since the target depends on `a`
  exact λ _, ()
end

end clear_dependent

section simp_rw
  example {α β : Type} {f : α → β} {t : set β} :
    (∀ s, f '' s ⊆ t) = ∀ s : set α, ∀ x ∈ s, x ∈ f ⁻¹' t :=
  by simp_rw [set.image_subset_iff, set.subset_def]
end simp_rw