lean4-htt/tests/lean/run/funinduction_generalize.lean
Joachim Breitner 3d1d8fc1de
feat: unfolding functional induction principles (#8088)
This PR adds the “unfolding” variant of the functional induction and
functional cases principles, under the name `foo.induct_unfolding` resp.
`foo.fun_cases_unfolding`. These theorems combine induction over the
structure of a recursive function with the unfolding of that function,
and should be more reliable, easier to use and more efficient than just
case-splitting and then rewriting with equational theorems.

For example  instead of
```
ackermann.induct
  (motive : Nat → Nat → Prop)
  (case1 : ∀ (m : Nat), motive 0 m)
  (case2 : ∀ (n : Nat), motive n 1 → motive (Nat.succ n) 0)
  (case3 : ∀ (n m : Nat), motive (n + 1) m → motive n (ackermann (n + 1) m) → motive (Nat.succ n) (Nat.succ m))
  (x x : Nat) : motive x x
```
one gets
```
ackermann.fun_cases_unfolding
  (motive : Nat → Nat → Nat → Prop)
  (case1 : ∀ (m : Nat), motive 0 m (m + 1))
  (case2 : ∀ (n : Nat), motive n.succ 0 (ackermann n 1))
  (case3 : ∀ (n m : Nat), motive n.succ m.succ (ackermann n (ackermann (n + 1) m)))
  (x✝ x✝¹ : Nat) : motive x✝ x✝¹ (ackermann x✝ x✝¹)
```
2025-04-29 16:43:06 +00:00

213 lines
5.1 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.

/-!
Checks the generalization behavior of `fun_induction`.
In particular that it behaves the same as `induction … using ….induct`.
-/
variable (xs ys : List Nat)
variable (P : ∀ {α}, List α → Prop)
-- We re-define this here to avoid stage0 complications
def zipWith (f : α → β → γ) : (xs : List α) → (ys : List β) → List γ
| x::xs, y::ys => f x y :: zipWith f xs ys
| _, _ => []
/--
error: unsolved goals
case case1
xs ys : List Nat
P : {α : Type} → List α → Prop
x✝ : Nat
xs✝ : List Nat
y✝ : Nat
ys✝ : List Nat
ih1✝ : P (xs✝.zip ys✝)
⊢ P ((x✝ :: xs✝).zip (y✝ :: ys✝))
case case2
xs ys : List Nat
P : {α : Type} → List α → Prop
t✝ ys✝ : List Nat
x✝ : ∀ (x : Nat) (xs : List Nat) (y : Nat) (ys : List Nat), t✝ = x :: xs → ys✝ = y :: ys → False
⊢ P (t✝.zip ys✝)
-/
#guard_msgs in
example : P (List.zip xs ys) := by
fun_induction zipWith _ xs ys
/--
error: unsolved goals
case case1
xs ys : List Nat
P : {α : Type} → List α → Prop
x✝ : Nat
xs✝ : List Nat
y✝ : Nat
ys✝ : List Nat
ih1✝ : xs✝.isEmpty = true → P (xs✝.zip ys✝)
h : (x✝ :: xs✝).isEmpty = true
⊢ P ((x✝ :: xs✝).zip (y✝ :: ys✝))
case case2
xs ys : List Nat
P : {α : Type} → List α → Prop
t✝ ys✝ : List Nat
x✝ : ∀ (x : Nat) (xs : List Nat) (y : Nat) (ys : List Nat), t✝ = x :: xs → ys✝ = y :: ys → False
h : t✝.isEmpty = true
⊢ P (t✝.zip ys✝)
-/
#guard_msgs in
example (h : xs.isEmpty) : P (List.zip xs ys) := by
fun_induction zipWith _ xs ys
/--
error: unsolved goals
case case1
xs ys : List Nat
P : {α : Type} → List α → Prop
x✝ : Nat
xs✝ : List Nat
y✝ : Nat
ys✝ : List Nat
ih1✝ : xs✝.isEmpty = true → P (xs✝.zip ys)
h : (x✝ :: xs✝).isEmpty = true
⊢ P ((x✝ :: xs✝).zip ys)
case case2
xs ys : List Nat
P : {α : Type} → List α → Prop
t✝ ys✝ : List Nat
x✝ : ∀ (x : Nat) (xs : List Nat) (y : Nat) (ys : List Nat), t✝ = x :: xs → ys✝ = y :: ys → False
h : t✝.isEmpty = true
⊢ P (t✝.zip ys)
-/
#guard_msgs in
example (h : xs.isEmpty) : P (List.zip xs ys) := by
fun_induction zipWith _ xs (ys.take 2)
/--
error: unsolved goals
case case1
xs ys : List Nat
P : {α : Type} → List α → Prop
x✝ : Nat
xs✝ : List Nat
y✝ : Nat
ys✝ : List Nat
ih1✝ : xs✝.isEmpty = true → P (xs✝.zip ys)
h : (x✝ :: xs✝).isEmpty = true
⊢ P ((x✝ :: xs✝).zip ys)
case case2
xs ys : List Nat
P : {α : Type} → List α → Prop
t✝ ys✝ : List Nat
x✝ : ∀ (x : Nat) (xs : List Nat) (y : Nat) (ys : List Nat), t✝ = x :: xs → ys✝ = y :: ys → False
h : t✝.isEmpty = true
⊢ P (t✝.zip ys)
-/
#guard_msgs in
example (h : xs.isEmpty) : P (List.zip xs ys) := by
induction xs, ys.take 2 using zipWith.induct
/--
error: unsolved goals
case case1
xs ys : List Nat
P : {α : Type} → List α → Prop
h : xs.isEmpty = true
x✝ : Nat
xs✝ : List Nat
y✝ : Nat
ys✝ : List Nat
ih1✝ : P (xs.zip ys✝)
⊢ P (xs.zip (y✝ :: ys✝))
case case2
xs ys : List Nat
P : {α : Type} → List α → Prop
h : xs.isEmpty = true
t✝ ys✝ : List Nat
x✝ : ∀ (x : Nat) (xs : List Nat) (y : Nat) (ys : List Nat), t✝ = x :: xs → ys✝ = y :: ys → False
⊢ P (xs.zip ys✝)
-/
#guard_msgs in
example (h : xs.isEmpty) : P (List.zip xs ys) := by
fun_induction zipWith _ (xs.take 2) ys
/--
error: unsolved goals
case case1
xs ys : List Nat
P : {α : Type} → List α → Prop
h : xs.isEmpty = true
x✝ : Nat
xs✝ : List Nat
y✝ : Nat
ys✝ : List Nat
ih1✝ : P (xs.zip ys✝)
⊢ P (xs.zip (y✝ :: ys✝))
case case2
xs ys : List Nat
P : {α : Type} → List α → Prop
h : xs.isEmpty = true
t✝ ys✝ : List Nat
x✝ : ∀ (x : Nat) (xs : List Nat) (y : Nat) (ys : List Nat), t✝ = x :: xs → ys✝ = y :: ys → False
⊢ P (xs.zip ys✝)
-/
#guard_msgs in
example (h : xs.isEmpty) : P (List.zip xs ys) := by
induction xs.take 2, ys using zipWith.induct
/--
error: unsolved goals
case case1
P : {α : Type} → List α → Prop
x✝ : Nat
xs✝ : List Nat
y✝ : Nat
ys✝ : List Nat
ih1✝ : ∀ (xs : List Nat), xs.isEmpty = true → P (xs.zip ys✝)
xs : List Nat
h : xs.isEmpty = true
⊢ P (xs.zip (y✝ :: ys✝))
case case2
P : {α : Type} → List α → Prop
t✝ ys✝ : List Nat
x✝ : ∀ (x : Nat) (xs : List Nat) (y : Nat) (ys : List Nat), t✝ = x :: xs → ys✝ = y :: ys → False
xs : List Nat
h : xs.isEmpty = true
⊢ P (xs.zip ys✝)
-/
#guard_msgs in
example (h : xs.isEmpty) : P (List.zip xs ys) := by
fun_induction zipWith _ (xs.take 2) ys generalizing xs
/--
error: unsolved goals
case case1
P : {α : Type} → List α → Prop
x✝ : Nat
xs✝ : List Nat
y✝ : Nat
ys✝ : List Nat
ih1✝ : ∀ (xs : List Nat), xs.isEmpty = true → P (xs.zip ys✝)
xs : List Nat
h : xs.isEmpty = true
⊢ P (xs.zip (y✝ :: ys✝))
case case2
P : {α : Type} → List α → Prop
t✝ ys✝ : List Nat
x✝ : ∀ (x : Nat) (xs : List Nat) (y : Nat) (ys : List Nat), t✝ = x :: xs → ys✝ = y :: ys → False
xs : List Nat
h : xs.isEmpty = true
⊢ P (xs.zip ys✝)
-/
#guard_msgs in
example (h : xs.isEmpty) : P (List.zip xs ys) := by
induction xs.take 2, ys using zipWith.induct generalizing xs