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✝¹) ```
213 lines
5.1 KiB
Text
213 lines
5.1 KiB
Text
/-!
|
||
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
|