This PR improves the functional cases principles, by making a more educated guess which function parameters should be targets and which should remain parameters (or be dropped). This simplifies the principles, and increases the chance that `fun_cases` can unfold the function call. Fixes #8296 (at least for the common cases, I hope.)
572 lines
21 KiB
Text
572 lines
21 KiB
Text
axiom testSorry : α
|
||
|
||
def fib : Nat → Nat
|
||
| 0 => 0
|
||
| 1 => 1
|
||
| n+2 => fib n + fib (n+1)
|
||
termination_by x => x
|
||
|
||
/--
|
||
info: fib.fun_cases_unfolding (motive : Nat → Nat → Prop) (case1 : motive 0 0) (case2 : motive 1 1)
|
||
(case3 : ∀ (n : Nat), motive n.succ.succ (fib n + fib (n + 1))) (x✝ : Nat) : motive x✝ (fib x✝)
|
||
-/
|
||
#guard_msgs(pass trace, all) in
|
||
#check fib.fun_cases_unfolding
|
||
|
||
-- set_option trace.Meta.FunInd true in
|
||
def ackermann : Nat → Nat → Nat
|
||
| 0, m => m + 1
|
||
| n+1, 0 => ackermann n 1
|
||
| n+1, m+1 => ackermann n (ackermann (n + 1) m)
|
||
termination_by n m => (n, m)
|
||
|
||
/--
|
||
info: 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✝¹)
|
||
-/
|
||
#guard_msgs(pass trace, all) in
|
||
#check ackermann.fun_cases_unfolding
|
||
|
||
def fib' : Nat → Nat
|
||
| 0 => 0
|
||
| 1 => 1
|
||
| n => fib' (n-1) + fib' (n-2)
|
||
decreasing_by all_goals exact testSorry
|
||
|
||
/--
|
||
info: fib'.fun_cases_unfolding (motive : Nat → Nat → Prop) (case1 : motive 0 0) (case2 : motive 1 1)
|
||
(case3 : ∀ (n : Nat), (n = 0 → False) → (n = 1 → False) → motive n (fib' (n - 1) + fib' (n - 2))) (x✝ : Nat) :
|
||
motive x✝ (fib' x✝)
|
||
-/
|
||
#guard_msgs(pass trace, all) in
|
||
#check fib'.fun_cases_unfolding
|
||
|
||
def fib'' (n : Nat) : Nat :=
|
||
if _h : n < 2 then
|
||
n
|
||
else
|
||
let foo := n - 2
|
||
if foo < 100 then
|
||
fib'' (n - 1) + fib'' foo
|
||
else
|
||
0
|
||
|
||
/--
|
||
info: fib''.fun_cases_unfolding (n : Nat) (motive : Nat → Prop) (case1 : n < 2 → motive n)
|
||
(case2 :
|
||
¬n < 2 →
|
||
let foo := n - 2;
|
||
foo < 100 → motive (fib'' (n - 1) + fib'' foo))
|
||
(case3 :
|
||
¬n < 2 →
|
||
let foo := n - 2;
|
||
¬foo < 100 → motive 0) :
|
||
motive (fib'' n)
|
||
-/
|
||
#guard_msgs(pass trace, all) in
|
||
#check fib''.fun_cases_unfolding
|
||
|
||
-- set_option trace.Meta.FunInd true in
|
||
def filter (p : Nat → Bool) : List Nat → List Nat
|
||
| [] => []
|
||
| x::xs => if p x then x::filter p xs else filter p xs
|
||
|
||
/--
|
||
info: filter.fun_cases (p : Nat → Bool) (motive : List Nat → Prop) (case1 : motive [])
|
||
(case2 : ∀ (x : Nat) (xs : List Nat), p x = true → motive (x :: xs))
|
||
(case3 : ∀ (x : Nat) (xs : List Nat), ¬p x = true → motive (x :: xs)) (x✝ : List Nat) : motive x✝
|
||
-/
|
||
#guard_msgs(pass trace, all) in
|
||
#check filter.fun_cases
|
||
|
||
/--
|
||
info: filter.fun_cases_unfolding (p : Nat → Bool) (motive : List Nat → List Nat → Prop) (case1 : motive [] [])
|
||
(case2 : ∀ (x : Nat) (xs : List Nat), p x = true → motive (x :: xs) (x :: filter p xs))
|
||
(case3 : ∀ (x : Nat) (xs : List Nat), ¬p x = true → motive (x :: xs) (filter p xs)) (x✝ : List Nat) :
|
||
motive x✝ (filter p x✝)
|
||
-/
|
||
#guard_msgs(pass trace, all) in
|
||
#check filter.fun_cases_unfolding
|
||
|
||
/--
|
||
info: filter.induct (p : Nat → Bool) (motive : List Nat → Prop) (case1 : motive [])
|
||
(case2 : ∀ (x : Nat) (xs : List Nat), p x = true → motive xs → motive (x :: xs))
|
||
(case3 : ∀ (x : Nat) (xs : List Nat), ¬p x = true → motive xs → motive (x :: xs)) (a✝ : List Nat) : motive a✝
|
||
-/
|
||
#guard_msgs(pass trace, all) in
|
||
#check filter.induct
|
||
|
||
/--
|
||
info: filter.induct_unfolding (p : Nat → Bool) (motive : List Nat → List Nat → Prop) (case1 : motive [] [])
|
||
(case2 : ∀ (x : Nat) (xs : List Nat), p x = true → motive xs (filter p xs) → motive (x :: xs) (x :: filter p xs))
|
||
(case3 : ∀ (x : Nat) (xs : List Nat), ¬p x = true → motive xs (filter p xs) → motive (x :: xs) (filter p xs))
|
||
(a✝ : List Nat) : motive a✝ (filter p a✝)
|
||
-/
|
||
#guard_msgs(pass trace, all) in
|
||
#check filter.induct_unfolding
|
||
|
||
theorem filter_const_false_is_nil :
|
||
filter (fun _ => false) xs = [] := by
|
||
refine filter.induct_unfolding (fun _ => false) (motive := fun xs r => r = []) ?case1 ?case2 ?case3 xs
|
||
case case1 => rfl
|
||
case case2 => intros; contradiction
|
||
case case3 => intros; assumption
|
||
|
||
theorem filter_filter :
|
||
filter q (filter p xs) = filter (fun x => p x && q x) xs := by
|
||
refine filter.induct_unfolding p (motive := fun xs r => filter q r = filter (fun x => p x && q x) xs) ?case1 ?case2 ?case3 xs
|
||
case case1 => rfl
|
||
case case2 =>
|
||
intros x xs hp ih
|
||
by_cases hq : q x
|
||
case pos => simp [*, filter]
|
||
case neg => simp [*, filter]
|
||
case case3 =>
|
||
intros
|
||
simp [*, filter]
|
||
|
||
|
||
def map (f : Nat → Bool) : List Nat → List Bool
|
||
| [] => []
|
||
| x::xs => f x::map f xs
|
||
termination_by x => x
|
||
|
||
/--
|
||
info: map.fun_cases (motive : List Nat → Prop) (case1 : motive []) (case2 : ∀ (x : Nat) (xs : List Nat), motive (x :: xs))
|
||
(x✝ : List Nat) : motive x✝
|
||
-/
|
||
#guard_msgs(pass trace, all) in
|
||
#check map.fun_cases
|
||
|
||
/--
|
||
info: map.fun_cases_unfolding (f : Nat → Bool) (motive : List Nat → List Bool → Prop) (case1 : motive [] [])
|
||
(case2 : ∀ (x : Nat) (xs : List Nat), motive (x :: xs) (f x :: map f xs)) (x✝ : List Nat) : motive x✝ (map f x✝)
|
||
-/
|
||
#guard_msgs(pass trace, all) in
|
||
#check map.fun_cases_unfolding
|
||
|
||
/--
|
||
info: map.induct (motive : List Nat → Prop) (case1 : motive [])
|
||
(case2 : ∀ (x : Nat) (xs : List Nat), motive xs → motive (x :: xs)) (a✝ : List Nat) : motive a✝
|
||
-/
|
||
#guard_msgs(pass trace, all) in
|
||
#check map.induct
|
||
|
||
/--
|
||
info: map.induct_unfolding (f : Nat → Bool) (motive : List Nat → List Bool → Prop) (case1 : motive [] [])
|
||
(case2 : ∀ (x : Nat) (xs : List Nat), motive xs (map f xs) → motive (x :: xs) (f x :: map f xs)) (a✝ : List Nat) :
|
||
motive a✝ (map f a✝)
|
||
-/
|
||
#guard_msgs(pass trace, all) in
|
||
#check map.induct_unfolding
|
||
|
||
namespace BinaryWF
|
||
|
||
-- set_option trace.Meta.FunInd true in
|
||
def map2 (f : Nat → Nat → Bool) : List Nat → List Nat → List Bool
|
||
| x::xs, y::ys => f x y::map2 f xs ys
|
||
| _, _ => []
|
||
termination_by x => x
|
||
|
||
|
||
/--
|
||
info: BinaryWF.map2.induct_unfolding (f : Nat → Nat → Bool) (motive : List Nat → List Nat → List Bool → Prop)
|
||
(case1 :
|
||
∀ (x : Nat) (xs : List Nat) (y : Nat) (ys : List Nat),
|
||
motive xs ys (map2 f xs ys) → motive (x :: xs) (y :: ys) (f x y :: map2 f xs ys))
|
||
(case2 :
|
||
∀ (x x_1 : List Nat),
|
||
(∀ (x_2 : Nat) (xs : List Nat) (y : Nat) (ys : List Nat), x = x_2 :: xs → x_1 = y :: ys → False) →
|
||
motive x x_1 [])
|
||
(a✝ a✝¹ : List Nat) : motive a✝ a✝¹ (map2 f a✝ a✝¹)
|
||
-/
|
||
#guard_msgs(pass trace, all) in
|
||
#check map2.induct_unfolding
|
||
|
||
end BinaryWF
|
||
|
||
namespace BinaryStructural
|
||
|
||
def map2 (f : Nat → Nat → Bool) : List Nat → List Nat → List Bool
|
||
| x::xs, y::ys => f x y::map2 f xs ys
|
||
| _, _ => []
|
||
termination_by structural x => x
|
||
|
||
/--
|
||
info: BinaryStructural.map2.induct_unfolding (f : Nat → Nat → Bool) (motive : List Nat → List Nat → List Bool → Prop)
|
||
(case1 :
|
||
∀ (x : Nat) (xs : List Nat) (y : Nat) (ys : List Nat),
|
||
motive xs ys (map2 f xs ys) → motive (x :: xs) (y :: ys) (f x y :: map2 f xs ys))
|
||
(case2 :
|
||
∀ (t x : List Nat),
|
||
(∀ (x_1 : Nat) (xs : List Nat) (y : Nat) (ys : List Nat), t = x_1 :: xs → x = y :: ys → False) → motive t x [])
|
||
(a✝ a✝¹ : List Nat) : motive a✝ a✝¹ (map2 f a✝ a✝¹)
|
||
-/
|
||
#guard_msgs(pass trace, all) in
|
||
#check map2.induct_unfolding
|
||
|
||
end BinaryStructural
|
||
|
||
namespace MutualWF
|
||
|
||
mutual
|
||
def map2a (f : Nat → Nat → Bool) : List Nat → List Nat → List Bool
|
||
| x::xs, y::ys => f x y::map2b f xs ys
|
||
| _, _ => []
|
||
termination_by x => x
|
||
def map2b (f : Nat → Nat → Bool) : List Nat → List Nat → List Bool
|
||
| x::xs, y::ys => f x y::map2a f xs ys
|
||
| _, _ => []
|
||
termination_by x => x
|
||
end
|
||
|
||
/--
|
||
info: MutualWF.map2a.mutual_induct_unfolding (f : Nat → Nat → Bool) (motive1 motive2 : List Nat → List Nat → List Bool → Prop)
|
||
(case1 :
|
||
∀ (x : Nat) (xs : List Nat) (y : Nat) (ys : List Nat),
|
||
motive2 xs ys (map2b f xs ys) → motive1 (x :: xs) (y :: ys) (f x y :: map2b f xs ys))
|
||
(case2 :
|
||
∀ (x x_1 : List Nat),
|
||
(∀ (x_2 : Nat) (xs : List Nat) (y : Nat) (ys : List Nat), x = x_2 :: xs → x_1 = y :: ys → False) →
|
||
motive1 x x_1 [])
|
||
(case3 :
|
||
∀ (x : Nat) (xs : List Nat) (y : Nat) (ys : List Nat),
|
||
motive1 xs ys (map2a f xs ys) → motive2 (x :: xs) (y :: ys) (f x y :: map2a f xs ys))
|
||
(case4 :
|
||
∀ (x x_1 : List Nat),
|
||
(∀ (x_2 : Nat) (xs : List Nat) (y : Nat) (ys : List Nat), x = x_2 :: xs → x_1 = y :: ys → False) →
|
||
motive2 x x_1 []) :
|
||
(∀ (a a_1 : List Nat), motive1 a a_1 (map2a f a a_1)) ∧ ∀ (a a_1 : List Nat), motive2 a a_1 (map2b f a a_1)
|
||
-/
|
||
#guard_msgs(pass trace, all) in
|
||
#check map2a.mutual_induct_unfolding
|
||
|
||
/--
|
||
info: MutualWF.map2a.induct_unfolding (f : Nat → Nat → Bool) (motive1 motive2 : List Nat → List Nat → List Bool → Prop)
|
||
(case1 :
|
||
∀ (x : Nat) (xs : List Nat) (y : Nat) (ys : List Nat),
|
||
motive2 xs ys (map2b f xs ys) → motive1 (x :: xs) (y :: ys) (f x y :: map2b f xs ys))
|
||
(case2 :
|
||
∀ (x x_1 : List Nat),
|
||
(∀ (x_2 : Nat) (xs : List Nat) (y : Nat) (ys : List Nat), x = x_2 :: xs → x_1 = y :: ys → False) →
|
||
motive1 x x_1 [])
|
||
(case3 :
|
||
∀ (x : Nat) (xs : List Nat) (y : Nat) (ys : List Nat),
|
||
motive1 xs ys (map2a f xs ys) → motive2 (x :: xs) (y :: ys) (f x y :: map2a f xs ys))
|
||
(case4 :
|
||
∀ (x x_1 : List Nat),
|
||
(∀ (x_2 : Nat) (xs : List Nat) (y : Nat) (ys : List Nat), x = x_2 :: xs → x_1 = y :: ys → False) →
|
||
motive2 x x_1 [])
|
||
(a✝ a✝¹ : List Nat) : motive1 a✝ a✝¹ (map2a f a✝ a✝¹)
|
||
-/
|
||
#guard_msgs(pass trace, all) in
|
||
#check map2a.induct_unfolding
|
||
|
||
end MutualWF
|
||
|
||
namespace MutualStructural
|
||
|
||
-- set_option trace.Meta.FunInd true in
|
||
mutual
|
||
def map2a (f : Nat → Nat → Bool) : List Nat → List Nat → List Bool
|
||
| x::xs, y::ys => f x y::map2b f xs ys
|
||
| _, _ => []
|
||
termination_by structural x => x
|
||
def map2b (f : Nat → Nat → Bool) : List Nat → List Nat → List Bool
|
||
| x::xs, y::ys => f x y::map2a f xs ys
|
||
| _, _ => []
|
||
termination_by structural x => x
|
||
end
|
||
|
||
/--
|
||
info: MutualStructural.map2a.induct (motive_1 motive_2 : List Nat → List Nat → Prop)
|
||
(case1 : ∀ (x : Nat) (xs : List Nat) (y : Nat) (ys : List Nat), motive_2 xs ys → motive_1 (x :: xs) (y :: ys))
|
||
(case2 :
|
||
∀ (t x : List Nat),
|
||
(∀ (x_1 : Nat) (xs : List Nat) (y : Nat) (ys : List Nat), t = x_1 :: xs → x = y :: ys → False) → motive_1 t x)
|
||
(case3 : ∀ (x : Nat) (xs : List Nat) (y : Nat) (ys : List Nat), motive_1 xs ys → motive_2 (x :: xs) (y :: ys))
|
||
(case4 :
|
||
∀ (t x : List Nat),
|
||
(∀ (x_1 : Nat) (xs : List Nat) (y : Nat) (ys : List Nat), t = x_1 :: xs → x = y :: ys → False) → motive_2 t x)
|
||
(a✝ a✝¹ : List Nat) : motive_1 a✝ a✝¹
|
||
-/
|
||
#guard_msgs(pass trace, all) in
|
||
#check map2a.induct
|
||
|
||
|
||
/--
|
||
info: MutualStructural.map2a.mutual_induct_unfolding (f : Nat → Nat → Bool)
|
||
(motive_1 motive_2 : List Nat → List Nat → List Bool → Prop)
|
||
(case1 :
|
||
∀ (x : Nat) (xs : List Nat) (y : Nat) (ys : List Nat),
|
||
motive_2 xs ys (map2b f xs ys) → motive_1 (x :: xs) (y :: ys) (map2a f (x :: xs) (y :: ys)))
|
||
(case2 :
|
||
∀ (t x : List Nat),
|
||
(∀ (x_1 : Nat) (xs : List Nat) (y : Nat) (ys : List Nat), t = x_1 :: xs → x = y :: ys → False) →
|
||
motive_1 t x (map2a f t x))
|
||
(case3 :
|
||
∀ (x : Nat) (xs : List Nat) (y : Nat) (ys : List Nat),
|
||
motive_1 xs ys (map2a f xs ys) → motive_2 (x :: xs) (y :: ys) (map2b f (x :: xs) (y :: ys)))
|
||
(case4 :
|
||
∀ (t x : List Nat),
|
||
(∀ (x_1 : Nat) (xs : List Nat) (y : Nat) (ys : List Nat), t = x_1 :: xs → x = y :: ys → False) →
|
||
motive_2 t x (map2b f t x)) :
|
||
(∀ (a a_1 : List Nat), motive_1 a a_1 (map2a f a a_1)) ∧ ∀ (a a_1 : List Nat), motive_2 a a_1 (map2b f a a_1)
|
||
-/
|
||
#guard_msgs(pass trace, all) in
|
||
#check map2a.mutual_induct_unfolding
|
||
|
||
|
||
/--
|
||
info: MutualStructural.map2a.induct_unfolding (f : Nat → Nat → Bool)
|
||
(motive_1 motive_2 : List Nat → List Nat → List Bool → Prop)
|
||
(case1 :
|
||
∀ (x : Nat) (xs : List Nat) (y : Nat) (ys : List Nat),
|
||
motive_2 xs ys (map2b f xs ys) → motive_1 (x :: xs) (y :: ys) (map2a f (x :: xs) (y :: ys)))
|
||
(case2 :
|
||
∀ (t x : List Nat),
|
||
(∀ (x_1 : Nat) (xs : List Nat) (y : Nat) (ys : List Nat), t = x_1 :: xs → x = y :: ys → False) →
|
||
motive_1 t x (map2a f t x))
|
||
(case3 :
|
||
∀ (x : Nat) (xs : List Nat) (y : Nat) (ys : List Nat),
|
||
motive_1 xs ys (map2a f xs ys) → motive_2 (x :: xs) (y :: ys) (map2b f (x :: xs) (y :: ys)))
|
||
(case4 :
|
||
∀ (t x : List Nat),
|
||
(∀ (x_1 : Nat) (xs : List Nat) (y : Nat) (ys : List Nat), t = x_1 :: xs → x = y :: ys → False) →
|
||
motive_2 t x (map2b f t x))
|
||
(a✝ a✝¹ : List Nat) : motive_1 a✝ a✝¹ (map2a f a✝ a✝¹)
|
||
-/
|
||
#guard_msgs(pass trace, all) in
|
||
#check map2a.induct_unfolding
|
||
|
||
end MutualStructural
|
||
|
||
|
||
abbrev leftChild (i : Nat) := 2*i + 1
|
||
abbrev parent (i : Nat) := (i - 1) / 2
|
||
|
||
def siftDown (a : Array Int) (root : Nat) (e : Nat) (h : e ≤ a.size := by omega) : Array Int :=
|
||
if _ : leftChild root < e then
|
||
let child := leftChild root
|
||
let child := if _ : child+1 < e then
|
||
if a[child] < a[child + 1] then
|
||
child + 1
|
||
else
|
||
child
|
||
else
|
||
child
|
||
if a[root]! < a[child]! then
|
||
let a := a.swapIfInBounds root child
|
||
siftDown a child e testSorry
|
||
else
|
||
a
|
||
else
|
||
a
|
||
termination_by e - root
|
||
decreasing_by exact testSorry
|
||
|
||
/--
|
||
info: siftDown.induct_unfolding (e : Nat) (motive : (a : Array Int) → Nat → e ≤ a.size → Array Int → Prop)
|
||
(case1 :
|
||
∀ (a : Array Int) (root : Nat) (h : e ≤ a.size),
|
||
leftChild root < e →
|
||
let child := leftChild root;
|
||
let child := if x : child + 1 < e then if h : a[child] < a[child + 1] then child + 1 else child else child;
|
||
a[root]! < a[child]! →
|
||
let a_1 := a.swapIfInBounds root child;
|
||
motive a_1 child ⋯ (siftDown a_1 child e ⋯) → motive a root h (siftDown a_1 child e ⋯))
|
||
(case2 :
|
||
∀ (a : Array Int) (root : Nat) (h : e ≤ a.size),
|
||
leftChild root < e →
|
||
let child := leftChild root;
|
||
let child := if x : child + 1 < e then if h : a[child] < a[child + 1] then child + 1 else child else child;
|
||
¬a[root]! < a[child]! → motive a root h a)
|
||
(case3 : ∀ (a : Array Int) (root : Nat) (h : e ≤ a.size), ¬leftChild root < e → motive a root h a) (a : Array Int)
|
||
(root : Nat) (h : e ≤ a.size) : motive a root h (siftDown a root e h)
|
||
-/
|
||
#guard_msgs(pass trace, all) in
|
||
#check siftDown.induct_unfolding
|
||
|
||
/--
|
||
info: siftDown.induct (e : Nat) (motive : (a : Array Int) → Nat → e ≤ a.size → Prop)
|
||
(case1 :
|
||
∀ (a : Array Int) (root : Nat) (h : e ≤ a.size),
|
||
leftChild root < e →
|
||
let child := leftChild root;
|
||
let child := if x : child + 1 < e then if h : a[child] < a[child + 1] then child + 1 else child else child;
|
||
a[root]! < a[child]! →
|
||
let a_1 := a.swapIfInBounds root child;
|
||
motive a_1 child ⋯ → motive a root h)
|
||
(case2 :
|
||
∀ (a : Array Int) (root : Nat) (h : e ≤ a.size),
|
||
leftChild root < e →
|
||
let child := leftChild root;
|
||
let child := if x : child + 1 < e then if h : a[child] < a[child + 1] then child + 1 else child else child;
|
||
¬a[root]! < a[child]! → motive a root h)
|
||
(case3 : ∀ (a : Array Int) (root : Nat) (h : e ≤ a.size), ¬leftChild root < e → motive a root h) (a : Array Int)
|
||
(root : Nat) (h : e ≤ a.size) : motive a root h
|
||
-/
|
||
#guard_msgs(pass trace, all) in
|
||
#check siftDown.induct
|
||
|
||
-- Now something with have
|
||
|
||
def withHave (n : Nat): Bool :=
|
||
have : 0 < n := testSorry
|
||
if n = 42 then true else withHave (n - 1)
|
||
termination_by n
|
||
decreasing_by exact testSorry
|
||
|
||
/--
|
||
info: withHave.induct_unfolding (motive : Nat → Bool → Prop) (case1 : 0 < 42 → motive 42 true)
|
||
(case2 : ∀ (x : Nat), 0 < x → ¬x = 42 → motive (x - 1) (withHave (x - 1)) → motive x (withHave (x - 1))) (n : Nat) :
|
||
motive n (withHave n)
|
||
-/
|
||
#guard_msgs(pass trace, all) in
|
||
#check withHave.induct_unfolding
|
||
|
||
/--
|
||
info: withHave.fun_cases_unfolding (n : Nat) (motive : Bool → Prop) (case1 : 0 < n → n = 42 → motive true)
|
||
(case2 : 0 < n → ¬n = 42 → motive (withHave (n - 1))) : motive (withHave n)
|
||
-/
|
||
#guard_msgs(pass trace, all) in
|
||
#check withHave.fun_cases_unfolding
|
||
|
||
-- Structural Mutual recursion
|
||
|
||
inductive Tree (α : Type u) : Type u where
|
||
| node : α → (Bool → List (Tree α)) → Tree α
|
||
|
||
mutual
|
||
def Tree.size : Tree α → Nat
|
||
| .node _ tsf => 1 + size_aux (tsf true) + size_aux (tsf false)
|
||
termination_by structural t => t
|
||
def Tree.size_aux : List (Tree α) → Nat
|
||
| [] => 0
|
||
| t :: ts => size t + size_aux ts
|
||
end
|
||
|
||
/--
|
||
info: Tree.size.induct_unfolding.{u_1} {α : Type u_1} (motive_1 : Tree α → Nat → Prop) (motive_2 : List (Tree α) → Nat → Prop)
|
||
(case1 :
|
||
∀ (a : α) (tsf : Bool → List (Tree α)),
|
||
motive_2 (tsf true) (Tree.size_aux (tsf true)) →
|
||
motive_2 (tsf false) (Tree.size_aux (tsf false)) →
|
||
motive_1 (Tree.node a tsf) (1 + Tree.size_aux (tsf true) + Tree.size_aux (tsf false)))
|
||
(case2 : motive_2 [] 0)
|
||
(case3 :
|
||
∀ (t : Tree α) (ts : List (Tree α)),
|
||
motive_1 t t.size → motive_2 ts (Tree.size_aux ts) → motive_2 (t :: ts) (t.size + Tree.size_aux ts))
|
||
(a✝ : Tree α) : motive_1 a✝ a✝.size
|
||
-/
|
||
#guard_msgs(pass trace, all) in
|
||
#check Tree.size.induct_unfolding
|
||
|
||
/--
|
||
info: Tree.size_aux.induct_unfolding.{u_1} {α : Type u_1} (motive_1 : Tree α → Nat → Prop)
|
||
(motive_2 : List (Tree α) → Nat → Prop)
|
||
(case1 :
|
||
∀ (a : α) (tsf : Bool → List (Tree α)),
|
||
motive_2 (tsf true) (Tree.size_aux (tsf true)) →
|
||
motive_2 (tsf false) (Tree.size_aux (tsf false)) →
|
||
motive_1 (Tree.node a tsf) (1 + Tree.size_aux (tsf true) + Tree.size_aux (tsf false)))
|
||
(case2 : motive_2 [] 0)
|
||
(case3 :
|
||
∀ (t : Tree α) (ts : List (Tree α)),
|
||
motive_1 t t.size → motive_2 ts (Tree.size_aux ts) → motive_2 (t :: ts) (t.size + Tree.size_aux ts))
|
||
(a✝ : List (Tree α)) : motive_2 a✝ (Tree.size_aux a✝)
|
||
-/
|
||
#guard_msgs(pass trace, all) in
|
||
#check Tree.size_aux.induct_unfolding
|
||
|
||
|
||
-- When the discriminants are duplicated, it is very easy for `FunInd` to be confused
|
||
-- about how to instantiate the equality theorem. Maybe not relevant in practice for now?
|
||
-- Maybe even impossible to solve.
|
||
|
||
-- set_option trace.Meta.FunInd true in
|
||
set_option linter.unusedVariables false in
|
||
def duplicatedDiscriminant (n : Nat) : Bool :=
|
||
match h1 : n, h2 : n with
|
||
| 0, 0 => true
|
||
| a+1, 0 => false -- by simp_all
|
||
| 0, b+1 => false -- by simp_all
|
||
| a, b => true
|
||
|
||
/--
|
||
info: duplicatedDiscriminant.fun_cases_unfolding (motive : Nat → Bool → Prop) (case1 : 0 = 0 → motive 0 true)
|
||
(case2 :
|
||
∀ (a : Nat),
|
||
a.succ = 0 →
|
||
motive 0
|
||
(match h1 : 0, h2 : 0 with
|
||
| 0, 0 => true
|
||
| a.succ, 0 => false
|
||
| 0, b.succ => false
|
||
| a, b => true))
|
||
(case3 :
|
||
∀ (b : Nat),
|
||
0 = b.succ →
|
||
motive b.succ
|
||
(match h1 : b.succ, h2 : b.succ with
|
||
| 0, 0 => true
|
||
| a.succ, 0 => false
|
||
| 0, b_1.succ => false
|
||
| a, b_1 => true))
|
||
(case4 :
|
||
∀ (b : Nat),
|
||
(b = 0 → b = 0 → False) →
|
||
(∀ (a : Nat), b = a.succ → b = 0 → False) →
|
||
(∀ (b_1 : Nat), b = 0 → b = b_1.succ → False) →
|
||
motive b
|
||
(match h1 : b, h2 : b with
|
||
| 0, 0 => true
|
||
| a.succ, 0 => false
|
||
| 0, b_1.succ => false
|
||
| a, b_1 => true))
|
||
(n : Nat) : motive n (duplicatedDiscriminant n)
|
||
-/
|
||
#guard_msgs(pass trace, all) in
|
||
#check duplicatedDiscriminant.fun_cases_unfolding
|
||
|
||
set_option linter.unusedVariables false in
|
||
def with_bif_tailrec : Nat → Nat
|
||
| 0 => 0
|
||
| n+1 =>
|
||
bif n % 2 == 0 then
|
||
with_bif_tailrec n
|
||
else
|
||
with_bif_tailrec (n-1)
|
||
termination_by n => n
|
||
|
||
/--
|
||
info: with_bif_tailrec.induct_unfolding (motive : Nat → Nat → Prop) (case1 : motive 0 0)
|
||
(case2 : ∀ (n : Nat), (n % 2 == 0) = true → motive n (with_bif_tailrec n) → motive n.succ (with_bif_tailrec n))
|
||
(case3 :
|
||
∀ (n : Nat),
|
||
(n % 2 == 0) = false → motive (n - 1) (with_bif_tailrec (n - 1)) → motive n.succ (with_bif_tailrec (n - 1)))
|
||
(a✝ : Nat) : motive a✝ (with_bif_tailrec a✝)
|
||
-/
|
||
#guard_msgs in
|
||
#check with_bif_tailrec.induct_unfolding
|
||
|
||
|
||
def binaryWithMatch (a b : Nat) :=
|
||
match h : decide (a < b) with
|
||
| true => 1 + binaryWithMatch (a - 1) (b - 1)
|
||
| false => 0
|
||
termination_by b
|
||
decreasing_by simp at h; omega
|
||
|
||
/--
|
||
info: binaryWithMatch.induct_unfolding (motive : Nat → Nat → Nat → Prop)
|
||
(case1 :
|
||
∀ (a b : Nat),
|
||
decide (a < b) = true →
|
||
motive (a - 1) (b - 1) (binaryWithMatch (a - 1) (b - 1)) → motive a b (1 + binaryWithMatch (a - 1) (b - 1)))
|
||
(case2 : ∀ (a b : Nat), decide (a < b) = false → motive a b 0) (a b : Nat) : motive a b (binaryWithMatch a b)
|
||
-/
|
||
#guard_msgs in
|
||
#check binaryWithMatch.induct_unfolding
|