lean4-htt/tests/lean/run/indPredRecursion.lean
Rob23oba 5b9567b144
fix: complete overhaul of structural recursion on inductives predicates (#9995)
This PR almost completely rewrites the inductive predicate recursion
algorithm; in particular `IndPredBelow` to function more consistently.
Historically, the `brecOn` generation through `IndPredBelow` has been
very error-prone -- this should be fixed now since the new algorithm is
very direct and doesn't rely on tactics or meta-variables at all.
Additionally, the new structural recursion procedure for inductive
predicates shares more code with regular structural recursion and thus
allows for mutual and nested recursion in the same way it was possible
with regular structural recursion. For example, the following works now:
```lean-4
mutual

inductive Even : Nat → Prop where
  | zero : Even 0
  | succ (h : Odd n) : Even n.succ

inductive Odd : Nat → Prop where
  | succ (h : Even n) : Odd n.succ

end

mutual

theorem Even.exists (h : Even n) : ∃ a, n = 2 * a :=
  match h with
  | .zero => ⟨0, rfl⟩
  | .succ h =>
    have ⟨a, ha⟩ := h.exists
    ⟨a + 1, congrArg Nat.succ ha⟩
termination_by structural h

theorem Odd.exists (h : Odd n) : ∃ a, n = 2 * a + 1 :=
  match h with
  | .succ h =>
    have ⟨a, ha⟩ := h.exists
    ⟨a, congrArg Nat.succ ha⟩
termination_by structural h

end
```

Closes #1672
Closes #10004
2025-09-01 08:17:58 +00:00

352 lines
9.4 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/-!
# Tests for `brecOn` on recursive inductive predicates
-/
/-!
Transitive closure (see issue 1672)
-/
inductive TransClosure (r : αα → Prop) : αα → Prop
| extends : r a b → TransClosure r a b
| trans_left : r a b → TransClosure r b c → TransClosure r a c
/--
info: TransClosure.brecOn.{u_1} {α : Sort u_1} {r : αα → Prop} {motive : (a a_1 : α) → TransClosure r a a_1 → Prop}
{a✝ a✝¹ : α} (t : TransClosure r a✝ a✝¹)
(F_1 : ∀ (a a_1 : α) (t : TransClosure r a a_1), TransClosure.below t → motive a a_1 t) : motive a✝ a✝¹ t
-/
#guard_msgs in
#check TransClosure.brecOn
theorem TransClosure.recTest (h : TransClosure r a b) : True :=
match h with
| .extends _ => trivial
| .trans_left _ h => h.recTest
termination_by structural h
/-!
Symmetric closure (see issue 4540)
-/
inductive SymmGen (r : αα → Prop) : αα → Prop
| rel : ∀ x y, r x y → SymmGen r x y
| symm : ∀ x y, SymmGen r x y → SymmGen r y x
/--
info: SymmGen.brecOn.{u_1} {α : Sort u_1} {r : αα → Prop} {motive : (a a_1 : α) → SymmGen r a a_1 → Prop} {a✝ a✝¹ : α}
(t : SymmGen r a✝ a✝¹) (F_1 : ∀ (a a_1 : α) (t : SymmGen r a a_1), SymmGen.below t → motive a a_1 t) : motive a✝ a✝¹ t
-/
#guard_msgs in
#check SymmGen.brecOn
theorem SymmGen.recTest (h : SymmGen r a b) : True :=
match h with
| .rel _ _ _ => trivial
| .symm _ _ h => h.recTest
termination_by structural h
/-!
Basic typing relation
-/
inductive Ty : Type where
| unit : Ty
| arr : Ty → Ty → Ty
inductive Term : Type where
| var : Nat → Term
| unit : Term
| abs : Term → Term
| app : Term → Term → Term
inductive Wt : List Ty → Term → Ty → Prop where
| var {Γ x} : (h : x < Γ.length) → Wt Γ (.var x) Γ[x]
| unit {Γ} : Wt Γ .unit .unit
| abs {Γ b A B} : Wt (A :: Γ) b B → Wt Γ (.abs b) (.arr A B)
| app {Γ b a A B} : Wt Γ b (.arr A B) → Wt Γ a A → Wt Γ (.app b a) B
/--
info: Wt.brecOn {motive : (a : List Ty) → (a_1 : Term) → (a_2 : Ty) → Wt a a_1 a_2 → Prop} {a✝ : List Ty} {a✝¹ : Term}
{a✝² : Ty} (t : Wt a✝ a✝¹ a✝²)
(F_1 : ∀ (a : List Ty) (a_1 : Term) (a_2 : Ty) (t : Wt a a_1 a_2), Wt.below t → motive a a_1 a_2 t) :
motive a✝ a✝¹ a✝² t
-/
#guard_msgs in
#check Wt.brecOn
theorem Wt.recTest (h : Wt Γ a A) : True :=
match h with
| .var _ => trivial
| .unit => trivial
| .abs h => h.recTest
| .app h h' => h.recTest
termination_by structural h
/-!
Mutual inductives
-/
mutual
inductive EvenDist : Nat → Nat → Prop where
| zero : EvenDist 0 0
| succ_left (h : Nat → OddDist a b) : EvenDist a.succ b
| succ_right (h : OddDist a b) : EvenDist a b.succ
inductive OddDist : Nat → Nat → Prop where
| succ_left (h : EvenDist a b) : OddDist a.succ b
| succ_right (h : EvenDist a b) : OddDist a b.succ
end
/--
info: EvenDist.brecOn {motive_1 : (a a_1 : Nat) → EvenDist a a_1 → Prop} {motive_2 : (a a_1 : Nat) → OddDist a a_1 → Prop}
{a✝ a✝¹ : Nat} (t : EvenDist a✝ a✝¹) (F_1 : ∀ (a a_1 : Nat) (t : EvenDist a a_1), t.below → motive_1 a a_1 t)
(F_2 : ∀ (a a_1 : Nat) (t : OddDist a a_1), t.below → motive_2 a a_1 t) : motive_1 a✝ a✝¹ t
-/
#guard_msgs in
#check EvenDist.brecOn
/--
info: OddDist.brecOn {motive_1 : (a a_1 : Nat) → EvenDist a a_1 → Prop} {motive_2 : (a a_1 : Nat) → OddDist a a_1 → Prop}
{a✝ a✝¹ : Nat} (t : OddDist a✝ a✝¹) (F_1 : ∀ (a a_1 : Nat) (t : EvenDist a a_1), t.below → motive_1 a a_1 t)
(F_2 : ∀ (a a_1 : Nat) (t : OddDist a a_1), t.below → motive_2 a a_1 t) : motive_2 a✝ a✝¹ t
-/
#guard_msgs in
#check OddDist.brecOn
mutual
theorem OddDist.recTest {a b} (h : OddDist b a) : True :=
match h with
| .succ_left h => h.recTest
| .succ_right h => h.recTest
termination_by structural h
theorem OddDist.recTest' {a b} (h : OddDist b a) : True :=
match h with
| .succ_left h => h.recTest
| .succ_right h => h.recTest
termination_by structural h
theorem EvenDist.recTest (h : EvenDist a b) : True :=
match h with
| .zero => trivial
| .succ_left h => (h 3).recTest
| .succ_right h => h.recTest'
termination_by structural h
end
/-!
Nested inductives
-/
inductive Test (p : Nat → Prop) : Nat → Prop
| zero : Test p 0
| one (h : Test p 3 ∧ p 2) : Test p 1
/--
info: Test.brecOn {p : Nat → Prop} {motive_1 : (a : Nat) → Test p a → Prop} {motive_2 : Test p 3 ∧ p 2 → Prop} {a✝ : Nat}
(t : Test p a✝) (F_1 : ∀ (a : Nat) (t : Test p a), t.below → motive_1 a t)
(F_2 : ∀ (t : Test p 3 ∧ p 2), Test.below_1 t → motive_2 t) : motive_1 a✝ t
-/
#guard_msgs in
#check Test.brecOn
/--
info: Test.brecOn_1 {p : Nat → Prop} {motive_1 : (a : Nat) → Test p a → Prop} {motive_2 : Test p 3 ∧ p 2 → Prop}
(t : Test p 3 ∧ p 2) (F_1 : ∀ (a : Nat) (t : Test p a), t.below → motive_1 a t)
(F_2 : ∀ (t : Test p 3 ∧ p 2), Test.below_1 t → motive_2 t) : motive_2 t
-/
#guard_msgs in
#check Test.brecOn_1
-- nested recursion
theorem Test.recTest (h : Test p n) : True :=
match h with
| .zero => trivial
| .one ⟨h, h'⟩ => recTest h
termination_by structural h
-- mutual nested recursion
mutual
theorem Test.recTest2 (h : Test p n) : True :=
match h with
| .zero => trivial
| .one h => recTest3 h
termination_by structural h
theorem Test.recTest3 (h : Test p 3 ∧ p 2) : True :=
match h with
| ⟨h, _⟩ => recTest2 h
termination_by structural h
end
/-!
Reflexive inductives
-/
inductive Test2 (p : Nat → Prop) : Nat → Prop
| zero : Test2 p 0
| abc (h : ∀ n, p (a + n) → Test2 p n) : Test2 p a
/--
info: Test2.brecOn {p : Nat → Prop} {motive : (a : Nat) → Test2 p a → Prop} {a✝ : Nat} (t : Test2 p a✝)
(F_1 : ∀ (a : Nat) (t : Test2 p a), Test2.below t → motive a t) : motive a✝ t
-/
#guard_msgs in
#check Test2.brecOn
theorem Test2.recTest (h : Test2 (fun _ => True) n) : True :=
match h with
| .zero => trivial
| .abc h => recTest (h 0 trivial)
termination_by structural h
/-!
Nested inductives with indices
-/
set_option inductive.autoPromoteIndices false in
inductive And2 (p : Prop) : (q : Prop) → Prop where
| intro (left : p) (right : b) : And2 p b
inductive Test' (p : Nat → Prop) : Nat → Prop where
| zero : Test' p 0
| one (h : And2 (Test' p 2) (p 3)) : Test' p 1
/--
info: Test'.brecOn {p : Nat → Prop} {motive_1 : (a : Nat) → Test' p a → Prop}
{motive_2 : (q : Prop) → And2 (Test' p 2) q → Prop} {a✝ : Nat} (t : Test' p a✝)
(F_1 : ∀ (a : Nat) (t : Test' p a), t.below → motive_1 a t)
(F_2 : ∀ (q : Prop) (t : And2 (Test' p 2) q), Test'.below_1 t → motive_2 q t) : motive_1 a✝ t
-/
#guard_msgs in
#check Test'.brecOn
theorem Test'.recTest (h : Test p n) : True :=
match h with
| .zero => trivial
| .one ⟨h, h'⟩ => recTest h
termination_by structural h
/-!
Recursion with the same inductive twice
-/
inductive NatProp : Prop where
| zero
| succ (k : NatProp) : NatProp
mutual
theorem NatProp.recTest1 (_a : Nat) (x : NatProp) (_c : Nat) : True :=
match x with
| .zero => trivial
| .succ k =>
have := k.recTest2 7
this 3
termination_by structural x
theorem NatProp.recTest2 (_b : Nat) (x : NatProp) (_d : Nat) : True :=
match x with
| .zero => trivial
| .succ k => k.recTest1 3 5
termination_by structural x
end
/-!
Support for named patterns
-/
mutual
inductive TestEven (b : Bool) : Nat → Prop where
| zero : TestEven b 0
| succ (h : Nat → TestOdd b n) (a : Nat) : TestEven b (n + 1)
inductive TestOdd (b : Bool) : Nat → Prop where
| succ (h : TestEven b n) : TestOdd b (n + 1)
end
theorem TestEven.recTest {b : Bool} {n : Nat} (h : TestEven b n) (_a : Bool) : True :=
match h with
| .zero => trivial
| @TestEven.succ _ n h' _a =>
match h' 2 with
| .succ .zero => trivial
| .succ h₁@hh₁:(.succ h'' _) =>
have : True := match h'' 3 with
| .succ h''' => h'''.recTest false
have := h₁
have := hh₁
have := h₁.recTest
trivial
termination_by structural h
/-!
Matching on partial applications
-/
theorem TestEven.recTest2 {b : Bool} {n : Nat} (h : TestEven b n) : True :=
match h with
| .zero => trivial
| @TestEven.succ _ n h' _a =>
-- h' : ∀ (a : Nat), TestOdd b n
match n, h' with
| _ + 1, h'' =>
match h'' 2 with
| .succ h''' => h'''.recTest2
| 0, _ => trivial
termination_by structural h
/-!
Recursive calls as part of match discriminants
-/
def addProofDependency {p : Prop} (_h : p) (a : α) : α := a
theorem NatProp.recTest3 (p : NatProp) : True :=
match p with
| .zero => trivial
| .succ h =>
match addProofDependency h.recTest3 trivial, h with
| ⟨⟩, .zero => trivial
| ⟨⟩, .succ h => h.recTest3
termination_by structural p
/-!
Using the same match twice
-/
theorem NatProp.recTest4 (p : NatProp) : True :=
match p with
| .zero => trivial
| .succ h =>
match h with
| .zero => trivial
| .succ h' =>
have := h.recTest3
h'.recTest4
termination_by structural p
/-!
Recursion on universe polymorphic functions
-/
class SuccTest (α : Type u) where
succ : αα
inductive SuccTest.le {α : Type u} [SuccTest α] : αα → Prop where
| refl {n : α} : le n n
| step {a b : α} (h : le a b) : le a (SuccTest.succ b)
theorem SuccTest.le_trans {α : Type u} [SuccTest α] {a b c : α} : le a b → le b c → le a c
| h, .refl => h
| h, .step h' => .step (le_trans h h')
termination_by structural _ h => h