Prove a recursive function in Isabelle - isabelle

I have defined the following recursive function:
primrec span :: "('a ⇒ bool) ⇒ 'a list ⇒ 'a list * 'a list" where
"span P [] = ([], [])"
| "span P (x#xs) =
(let (ys, zs) = span P xs in
if P x then (x#ys, zs) else (ys, x#zs))"
which splits a list into two sublists by a filter p and insert x in the middle.
Now I would like to prove the following lemma:
lemma invariant_length:
shows "length (fst (span P l)) + length (snd (span P l)) = length l"
I did the proof by induction
lemma invariant_length:
shows "length (fst (span P l)) + length (snd (span P l)) = length l"
proof (induction l)
case Nil
then show ?case by auto
next
case (Cons a l)
show ?case
proof(cases "P a")
case True
then have "span P (a # l) = (a # fst (span P l), snd (span P l))"
by [where I stuck]
then have "length (fst (span P (a # l))) + length (snd (span P (a # l)))
= length (a # fst (span P l)) + length(snd (span P l)) "
by simp
then have "length (fst (span P (a # l))) + length (snd (span P (a # l)))
= Suc(length (fst (span P l))) + length(snd (span P l)) "
by simp
with Cons have "length (fst (span P (a # l))) + length (snd (span P (a # l))) = Suc (length l)"
by simp
then show ?thesis by simp
next
case False
then have "span P (a # l) = (fst (span P l), a # snd (span P l))"
by [where I stuck]
then have "length (fst (span P (a # l))) + length (snd (span P (a # l)))
= length (fst (span P l)) + length(a # snd (span P l)) "
by simp
then have "length (fst (span P (a # l))) + length (snd (span P (a # l)))
= length (fst (span P l)) + Suc(length(snd (span P l)))"
by simp
with Cons have "length (fst (span P (a # l))) + length (snd (span P (a # l))) = Suc (length l)"
by simp
then show ?thesis by simp
qed
qed
I stuck at the first step of the two cases during the inductive step, where I have marked as [where I stuck]. I tried using auto or simp here but Isabelle says
Failed to finish proof⌂:
goal (1 subgoal):
1. P a ⟹ (case span P l of (ys, x) ⇒ (a # ys, x)) = (a # fst (span P l), snd (span P l))
How can I proceed here?

lemma invariant_length:
"length (fst (span P l)) + length (snd (span P l)) = length l"
by (induction l; simp add: prod.case_eq_if)

Related

Substitute the variables in the Isabelle/HOL Isar proof

I'm writing an Isar proof inside the Isabelle2020 and working with the locales.
On some point I want to use the axiom from the locale, which states
((A.par x y) ∧ (F x ≃ F y)) → (x ≃ y)
where "A" is another locale given as the information for the one where the above axiom is stored, "A.par" is simply a predicate then.
All the predicates and functions in the Axiom use polymorphic types, the free variable "x" itself is of type 'a, for example, the function "F" is of type 'a => 'b and "G" is of type 'b => 'c:
F :: "'a => 'b"
G :: "'b => 'c"
x :: "'a"
Generally, I have to axioms of this form: one for the locale "F" (showed above) and one for the locale "G" (which is exactly the same but with other types here):
((A.par x y) ∧ (G x ≃ G y)) → (x ≃ y)
The problem is that inside the proof I want to use this axiom but applied as
(A.par (F x) (F y)) ∧ (G(F x) ≃ G(F y)) → (F x) ≃ (F y)
Here the types coincide (as I see), because "F x" is of type 'b and "G" is applied exactly on this type.
And it seems I have to explicitly show the prover that "(F x)" here should be considered as "x" in the axiom and so on.
The proof step itself is just to apply the lemma to the conclusion I have already got:
(A.par (F x) (F y)) ∧ (G(F x) ≃ G(F y))
to get
(F x) ≃ (F y)
If we take a look on the output of the Isabelle we will see these:
USING 1)
((E (domain' (F x)) ∧ ¬ (¬ (E (domain' (F y))) ∨ domain' (F x) ≠ domain'
(F y))) ∧ E (codomain' (F x)) ∧ ¬ (¬ (E (codomain' (F y))) ∨ codomain'
(F x) ≠ codomain' (F y))) ∧ E (G (F x)) ∧ ¬ (¬ (E (G (F y))) ∨ G (F x) ≠ G
(F y))
AND 2)
(E ?x ∧ ¬ (¬ (E ?y) ∨ ?x ≠ ?y)) ← (((E (domain' ?x) ∧ ¬ (¬ (E (domain' ?y
)) ∨ domain' ?x ≠ domain' ?y)) ∧ E (codomain' ?x) ∧ ¬ (¬ (E (codomain' ?y
)) ∨ codomain' ?x ≠ codomain' ?y)) ∧ E (G ?x) ∧ ¬ (¬ (E (G ?y)) ∨ G ?x ≠ G
?y))
GET THIS
E (F x) ∧ ¬ (¬ (E (F y)) ∨ F x ≠ F y)
So, here one should merely use the 2)nd fact with substituted schematic variables by "F x" and "F y".
Will be glad to see any suggestions.

Proving the correctness of an algorithm to partition lists in Isabelle

I trying to prove correct an algorithm to split a list of integers into sublists of equal sum in linear time. Here you can see the algorithm I have chosen to do so.
I would like to get some feedback regarding:
The convenience of my definition for the splitting function.
The "induction" hypothesis to use in my situation.
Please, bear in mind that up to now I have only worked with apply-scripts and not with Isar proofs.
Here is a preliminary implementation of the algorithm and the correctness definition:
definition
"ex_balanced_sum xs = (∃ ys zs. sum_list ys = sum_list zs ∧
xs = ys # zs ∧ ys ≠ [] ∧ zs ≠ [])"
fun check_list :: "int list ⇒ int ⇒ int ⇒ bool" where
"check_list [] n acc = False" |
"check_list (x#xs) n acc = (if n = acc then True else (check_list xs (n-x) (acc+x)))"
fun linear_split :: "int list ⇒ bool" where
"linear_split [] = False" |
"linear_split [x] = False" |
"linear_split (x # xs) = check_list xs (sum_list xs) x"
The theorem to prove is as follows:
lemma linear_correct: "linear_split xs ⟷ ex_balanced_sum xs"
If I reason for instance for the first implication as:
lemma linear_correct_1: "linear_split xs ⟹ ex_balanced_sum xs"
apply(induction xs rule: linear_split.induct)
Then I get a list of subgoals that I think are not appropriate:
linear_split [] ⟹ ex_balanced_sum []
⋀x. linear_split [x] ⟹ ex_balanced_sum [x]
⋀x v va. linear_split (x # v # va) ⟹ ex_balanced_sum (x # v # va)
In particular, these subgoals don't have an induction hypothesis! (am I right?). I tried to perform a different induction by just writing apply(induction xs) but then the goals look as:
linear_split [] ⟹ ex_balanced_sum []
⋀a xs. (linear_split xs ⟹ ex_balanced_sum xs) ⟹ linear_split (a # xs) ⟹ ex_balanced_sum (a # xs)
Here the hypothesis is also not an induction hypothesis since it is assuming an implication.
So, what is the best way to define this function to get a nice induction hypothesis?
Edit (a one-function version)
fun check :: "int list ⇒ int ⇒ int ⇒ bool" where
"check [] n acc = False" |
"check [x] n acc = False" |
"check (x # y # xs) n acc = (if n-x = acc+x then True else check (y # xs) (n-x) (acc+x))"
definition "linear_split xs = check xs (sum_list xs) 0"
Background
I was able to prove the theorem linear_correct for a function (splitl) that is very similar to the function check in the statement of the question. Unfortunately, I would prefer not to make any attempts to convert the proof into an apply script.
The proof below is the first proof that came to my mind after I started investigating the question. Thus, there may exist better proofs.
Proof Outline
The proof is based on the induction based on the length of the list. In particular, assume
splitl xs (sum_list xs) 0 ⟹ ex_balanced_sum xs
holds for all lists with the length less than l. If l = 1, then the result is easy to show. Assume, that l>=2. Then the list can be expressed in the form x#v#xs. In this case if it is possible to split the list using splitl, then it can be shown (splitl_reduce) that either
"splitl ((x + v)#xs) (sum_list ((x + v)#xs)) 0" (1)
or
"x = sum_list (v#xs)" (2).
Thus, the proof proceeds by cases for (1) and (2). For (1), the length of the list is (x + v)#xs) is l-1. Hence, by the induction hypothesis ex_balanced_sum (x + v)#xs). Therefore, by the definition of ex_balanced_sum, also ex_balanced_sum x#v#xs. For (2), it can be easily seen that the list can be expressed as [x]#(v#xs) and, in this case, given (2), it satisfies the conditions of ex_balanced_sum by definition.
The proof for the other direction is similar and based on the converse of the lemma associated with (1) and (2) above: if "splitl ((x + v)#xs) (sum_list ((x + v)#xs)) 0" or "x = sum_list (v#xs)", then "splitl (x#v#xs) (sum_list (x#v#xs)) 0".
theory so_ptcoaatplii
imports Complex_Main
begin
definition
"ex_balanced_sum xs =
(∃ ys zs. sum_list ys = sum_list zs ∧ xs = ys # zs ∧ ys ≠ [] ∧ zs ≠ [])"
fun splitl :: "int list ⇒ int ⇒ int ⇒ bool" where
"splitl [] s1 s2 = False" |
"splitl [x] s1 s2 = False" |
"splitl (x # xs) s1 s2 = ((s1 - x = s2 + x) ∨ splitl xs (s1 - x) (s2 + x))"
lemma splitl_reduce:
assumes "splitl (x#v#xs) (sum_list (x#v#xs)) 0"
shows "splitl ((x + v)#xs) (sum_list ((x + v)#xs)) 0 ∨ x = sum_list (v#xs)"
proof -
from assms have prem_cases:
"((x = sum_list (v#xs)) ∨ splitl (v#xs) (sum_list (v#xs)) x)" by auto
{
assume "splitl (v#xs) (sum_list (v#xs)) x"
then have "splitl ((x + v)#xs) (sum_list ((x + v)#xs)) 0"
proof(induction xs arbitrary: x v)
case Nil then show ?case by simp
next
case (Cons a xs) then show ?case by simp
qed
}
with prem_cases show ?thesis by auto
qed
(*Sledgehammered*)
lemma splitl_expand:
assumes "splitl ((x + v)#xs) (sum_list ((x + v)#xs)) 0 ∨ x = sum_list (v#xs)"
shows "splitl (x#v#xs) (sum_list (x#v#xs)) 0"
by (smt assms list.inject splitl.elims(2) splitl.simps(3) sum_list.Cons)
lemma splitl_to_sum: "splitl xs (sum_list xs) 0 ⟹ ex_balanced_sum xs"
proof(induction xs rule: length_induct)
case (1 xs) show ?case
proof-
obtain x v xst where x_xst: "xs = x#v#xst"
by (meson "1.prems" splitl.elims(2))
have main_cases:
"splitl ((x + v)#xst) (sum_list ((x + v)#xst)) 0 ∨ x = sum_list (v#xst)"
by (rule splitl_reduce, insert x_xst "1.prems", rule subst)
{
assume "splitl ((x + v)#xst) (sum_list ((x + v)#xst)) 0"
with "1.IH" x_xst have "ex_balanced_sum ((x + v)#xst)" by simp
then obtain yst zst where
yst_zst: "(x + v)#xst = yst#zst"
and sum_yst_eq_sum_zst: "sum_list yst = sum_list zst"
and yst_ne: "yst ≠ []"
and zst_ne: "zst ≠ []"
unfolding ex_balanced_sum_def by auto
then obtain ystt where ystt: "yst = (x + v)#ystt"
by (metis append_eq_Cons_conv)
with sum_yst_eq_sum_zst have "sum_list (x#v#ystt) = sum_list zst" by simp
moreover have "xs = (x#v#ystt)#zst" using x_xst yst_zst ystt by auto
moreover have "(x#v#ystt) ≠ []" by simp
moreover with zst_ne have "zst ≠ []" by simp
ultimately have "ex_balanced_sum xs" unfolding ex_balanced_sum_def by blast
}
note prem = this
{
assume "x = sum_list (v#xst)"
then have "sum_list [x] = sum_list (v#xst)" by auto
moreover with x_xst have "xs = [x] # (v#xst)" by auto
ultimately have "ex_balanced_sum xs" using ex_balanced_sum_def by blast
}
with prem main_cases show ?thesis by blast
qed
qed
lemma sum_to_splitl: "ex_balanced_sum xs ⟹ splitl xs (sum_list xs) 0"
proof(induction xs rule: length_induct)
case (1 xs) show ?case
proof -
from "1.prems" ex_balanced_sum_def obtain ys zs where
ys_zs: "xs = ys#zs"
and sum_ys_eq_sum_zs: "sum_list ys = sum_list zs"
and ys_ne: "ys ≠ []"
and zs_ne: "zs ≠ []"
by blast
have prem_cases: "∃y v yst. ys = (y#v#yst) ∨ (∃y. ys = [y])"
by (metis remdups_adj.cases ys_ne)
{
assume "∃y. ys = [y]"
then have "splitl xs (sum_list xs) 0"
using splitl.elims(3) sum_ys_eq_sum_zs ys_zs zs_ne by fastforce
}
note prem = this
{
assume "∃y v yst. ys = (y#v#yst)"
then obtain y v yst where y_v_yst: "ys = (y#v#yst)" by auto
then have
"sum_list ((y + v)#yst) = sum_list zs ∧ ((y + v)#yst) ≠ [] ∧ zs ≠ []"
using sum_ys_eq_sum_zs zs_ne by auto
then have ebs_ypv: "ex_balanced_sum (((y + v)#yst)#zs)"
using ex_balanced_sum_def by blast
have l_ypv: "length (((y + v)#yst)#zs) < length xs"
by (simp add: y_v_yst ys_zs)
from l_ypv ebs_ypv have
"splitl (((y + v)#yst)#zs) (sum_list (((y + v)#yst)#zs)) 0"
by (rule "1.IH"[THEN spec, rule_format])
with splitl_expand have splitl_ys_exp:
"splitl ((y#v#yst)#zs) (sum_list ((y#v#yst)#zs)) 0"
by (metis Cons_eq_appendI)
from ys_zs have "splitl xs (sum_list xs) 0"
by (rule ssubst, insert y_v_yst splitl_ys_exp, simp)
}
with prem prem_cases show ?thesis by auto
qed
qed
lemma linear_correct: "ex_balanced_sum xs ⟷ splitl xs (sum_list xs) 0"
using splitl_to_sum sum_to_splitl by auto
end

Basic Isabelle/Isar style (exercise 4.6)

I'm interested in using Isabelle/Isar for writing proofs which are both human-readable and machine checked, and I am looking to improve my style and streamline my proofs.
prog-prove has the following exercise:
Exercise 4.6. Define a recursive function elems :: 'a list ⇒ 'a set and prove x ∈ elems xs ⟹ ∃ ys zs. xs = ys # x # zs ∧ x ∉ elems ys.
Mimicking something similar to what I would write with pen and paper, my solution is
fun elems :: "'a list ⇒ 'a set" where
"elems [] = {}" |
"elems (x # xs) = {x} ∪ elems xs"
fun takeUntil :: "('a ⇒ bool) ⇒ 'a list ⇒ 'a list" where
"takeUntil f [] = []" |
"takeUntil f (x # xs) = (case (f x) of False ⇒ x # takeUntil f xs | True ⇒ [])"
theorem "x ∈ elems xs ⟹ ∃ ys zs. xs = ys # x # zs ∧ x ∉ elems ys"
proof -
assume 1: "x ∈ elems xs"
let ?ys = "takeUntil (λ z. z = x) xs"
let ?zs = "drop (length ?ys + 1) xs"
have "xs = ?ys # x # ?zs ∧ x ∉ elems ?ys"
proof
have 2: "x ∉ elems ?ys"
proof (induction xs)
case Nil
thus ?case by simp
next
case (Cons a xs)
thus ?case
proof -
{
assume "a = x"
hence "takeUntil (λz. z = x) (a # xs) = []" by simp
hence A: ?thesis by simp
}
note eq = this
{
assume "a ≠ x"
hence "takeUntil (λz. z = x) (a # xs) = a # takeUntil (λz. z = x) xs" by simp
hence ?thesis using Cons.IH by auto
}
note noteq = this
have "a = x ∨ a ≠ x" by simp
thus ?thesis using eq noteq by blast
qed
qed
from 1 have "xs = ?ys # x # ?zs"
proof (induction xs)
case Nil
hence False by simp
thus ?case by simp
next
case (Cons a xs)
{
assume 1: "a = x"
hence 2: "takeUntil (λz. z = x) (a # xs) = []" by simp
hence "length (takeUntil (λz. z = x) (a # xs)) + 1 = 1" by simp
hence 3: "drop (length (takeUntil (λz. z = x) (a # xs)) + 1) (a # xs) = xs" by simp
from 1 2 3 have ?case by simp
}
note eq = this
{
assume 1: "a ≠ x"
with Cons.prems have "x ∈ elems xs" by simp
with Cons.IH
have IH: "xs = takeUntil (λz. z = x) xs # x # drop (length (takeUntil (λz. z = x) xs) + 1) xs" by simp
from 1 have 2: "takeUntil (λz. z = x) (a # xs) = a # takeUntil (λz. z = x) (xs)" by simp
from 1 have "drop (length (takeUntil (λz. z = x) (a # xs)) + 1) (a # xs) = drop (length (takeUntil (λz. z = x) xs) + 1) xs" by simp
hence ?case using IH 2 by simp
}
note noteq = this
have "a = x ∨ a ≠ x" by simp
thus ?case using eq noteq by blast
qed
with 2 have 3: ?thesis by blast
thus "xs = takeUntil (λz. z = x) xs # x # drop (length (takeUntil (λz. z = x) xs) + 1) xs" by simp
from 3 show "x ∉ elems (takeUntil (λz. z = x) xs)" by simp
qed
thus ?thesis by blast
qed
but it seems rather long. In particular, I think invoking law of excluded middle here is cumbersome, and I feel like there ought to be some convenient schematic variable like ?goal which can refer to the current goal or something.
How can I make this proof shorter without sacrificing clarity?
Not really an answer to your specific question, but I would nonetheless like to point out, that a more concise prove can still be comprehensible.
lemma "x ∈ elems xs ⟹ ∃ ys zs. xs = ys # x # zs ∧ x ∉ elems ys"
proof (induction)
case (Cons l ls)
thus ?case
proof (cases "x ≠ l")
case True
hence "∃ys zs. ls = ys # x # zs ∧ x ∉ elems ys" using Cons by simp
thus ?thesis using ‹x ≠ l› Cons_eq_appendI by fastforce
qed (fastforce)
qed (simp)
Here's another shorter proof than your own:
fun elems :: ‹'a list ⇒ 'a set› where
‹elems [] = {}› |
‹elems (x#xs) = {x} ∪ elems xs›
lemma elems_prefix_suffix:
assumes ‹x ∈ elems xs›
shows ‹∃pre suf. xs = pre # [x] # suf ∧ x ∉ elems pre›
using assms proof(induction xs)
fix y ys
assume *: ‹x ∈ elems (y#ys)›
and IH: ‹x ∈ elems ys ⟹ ∃pre suf. ys = pre # [x] # suf ∧ x ∉ elems pre›
{
assume ‹x = y›
from this have ‹∃pre suf. y#ys = pre # [x] # suf ∧ x ∉ elems pre›
using * by fastforce
}
note L = this
{
assume ‹x ≠ y› and ‹x ∈ elems ys›
moreover from this obtain pre and suf where ‹ys = pre # [x] # suf› and ‹x ∉ elems pre›
using IH by auto
moreover have ‹y#ys = y#pre # [x] # suf› and ‹x ∉ elems (y#pre)›
by(simp add: calculation)+
ultimately have ‹∃pre suf. y#ys = pre # [x] # suf ∧ x ∉ elems pre›
by(metis append_Cons)
}
from this and L show ‹∃pre suf. y#ys = pre # [x] # suf ∧ x ∉ elems pre›
using * by auto
qed auto ― ‹Base case trivial›
I've used a few features of Isar to compress the proof:
Blocks within the braces {...} allow you to perform hypothetical reasoning.
Facts can be explicitly named using note.
The moreover keyword starts a calculation that implicitly "carries along" facts as they are established. The calculation "comes to a head" with the ultimately keyword. This style can significantly reduce the number of explicitly named facts that you need to introduce over the course of a proof.
The qed auto completes the proof by applying auto to all remaining subgoals. A comment notes that the subgoal remaining is the base case of the induction, which is trivial.

Doing this proof automatically on Isabelle

I satarted to work with Isabelle a few weeks ago and it's hard to me to do some proves automatically. I've just use the rule "less_induct" to show a property on a list.
theorem cuenta_ordena_1:
"cuenta (ordena xs) y = cuenta xs y"
proof(induct "length xs" arbitrary: xs rule: less_induct)
case less
show ?case
proof(cases xs)
assume "xs=[]"
then show ?thesis by simp
next
fix a list
assume "xs=a#list"
have "length(menores a list)<Suc(length list)" by simp
also have "... = length (a#list)" by simp
also have "... = length (xs)" using `xs=a#list` by simp
finally have 1:"length (menores a list)< length xs" by simp
have "length(mayores a list)<Suc(length list)" by simp
also have "... = length (a#list)" by simp
also have "... = length (xs)" using `xs=a#list` by simp
finally have 2:"length (mayores a list)< length xs" by simp
have " cuenta (ordena xs) y= cuenta (ordena (a#list)) y" using `xs=a#list` by simp
also have "...= cuenta ((ordena (menores a list)) # (a # (ordena (mayores a list)))) y " by simp
also have "... = cuenta (ordena (menores a list)) y + cuenta (a # (ordena (mayores a list))) y " by (rule cuenta_append)
also have "... = cuenta (menores a list) y + cuenta (a # (ordena (mayores a list))) y " using less 1 by simp
finally have 3:"cuenta(ordena xs) y = cuenta (menores a list) y + cuenta (a # (ordena (mayores a list))) y" by simp
also have 4:"... = cuenta xs y"
proof(cases "a=y")
case False
then have "cuenta (menores a list) y + cuenta (a # (ordena (mayores a list))) y
= cuenta (menores a list) y + cuenta (ordena (mayores a list)) y " by simp
also have "... = cuenta (menores a list) y + cuenta (mayores a list) y " using less 2 by simp
also have "... = cuenta xs y"
proof (cases "y<a")
case True
hence "cuenta (menores a list) y + cuenta (mayores a list) y
= cuenta list y + cuenta (mayores a list) y" by (simp add: cuenta_menores)
also have "... = cuenta list y" using "True" by (simp add: cuenta_mayores)
also have "... = cuenta (a#list) y" using "False" by simp
finally show ?thesis using `xs=a#list` by simp
next
case False
hence "cuenta (menores a list) y + cuenta (mayores a list) y
= cuenta (mayores a list) y" by (simp add: cuenta_menores)
also have "... = cuenta list y" using "False" by (simp add: cuenta_mayores)
also have "... = cuenta (a#list) y" using `¬(a=y)` by simp
finally show ?thesis using `xs=a#list` by simp
qed
finally show ?thesis by simp
next
case True
hence "¬(y<a)" by simp
have "cuenta (menores a list) y + cuenta (a # (ordena (mayores a list))) y
= cuenta (menores a list) y + Suc(cuenta (ordena (mayores a list)) y) " using "True" by simp
also have "... = cuenta (menores a list) y + Suc(cuenta (mayores a list) y) " using less 2 by simp
also have "... = Suc(cuenta(mayores a list) y)" using `¬(y<a)` by (simp add: cuenta_menores)
also have "... = Suc(cuenta list y)" using `¬(y<a)` by (simp add: cuenta_mayores)
also have "... = cuenta (a#list) y" using "True" by simp
finally show ?thesis using `xs=a#list` by simp
qed
finally show ?thesis using 3 4 by simp
qed
qed
To do the automatic proof I think I have to write something like this:
theorem cuenta_ordena:
"cuenta (ordena xs) y = cuenta xs y"
apply (induction "length xs" arbitrary: xs rule: less_induct)
apply (cases xs)
apply (auto simp add: cuenta_append cuenta_menores cuenta_mayores)
Can you help me?
Thank you!
Based upon your proof and my meagre knowledge of Spanish, I imagine your theory looks something like this:
fun mejores :: "('a :: linorder) ⇒ 'a list ⇒ 'a list" where
"mejores y [] = []"
| "mejores y (x#xs) = (if x ≥ y then [x] else []) # mejores y xs"
fun menores :: "('a :: linorder) ⇒ 'a list ⇒ 'a list" where
"menores y [] = []"
| "menores y (x#xs) = (if x < y then [x] else []) # menores y xs"
lemma length_mejores [simp]: "length (mejores y xs) ≤ length xs"
by (induction xs) simp_all
lemma length_menores [simp]: "length (menores y xs) ≤ length xs"
by (induction xs) simp_all
fun ordena where
"ordena [] = []"
| "ordena (x#xs) = ordena (menores x xs) # [x] # ordena (mejores x xs)"
fun cuenta :: "_ list ⇒ _ ⇒ nat" where
"cuenta [] y = 0"
| "cuenta (x#xs) y = (if y = x then 1 else 0) + cuenta xs y"
The automatic proof you suggested cannot work here, because when you write apply (cases xs), xs is a variable that is universally quantified in the goal. If you want to do case distinction over such a variable, you should do an Isar proof (as you did before).
An easier approach requiring fewer auxiliary lemmas would be the following:
lemma cuenta_append [simp]: "cuenta (xs # ys) y = cuenta xs y + cuenta ys y"
by (induction xs) simp_all
lemma cuenta_mejores_menores: "cuenta (menores x xs) y + cuenta (mejores x xs) y = cuenta xs y"
by (induction xs) auto
...and the proof is completely automatic:
lemma "cuenta (ordena xs) y = cuenta xs y"
by (induction xs rule: ordena.induct) (auto simp: cuenta_mejores_menores)
Note that I used the induction rule for the ordena function. The induction on the list length that you did is more general, but that makes it more difficult to use automation. The rule ordena.induct looks like this:
P [] ⟹
(⋀x xs.
P (menores x xs) ⟹
P (mejores x xs) ⟹
P (x # xs)) ⟹
P a0
That is precisely what you need here. Also, note that if you do want to do induction on list length, using the rule length_induct is much easier than natural-number induction on the list length itself, which is what you did.
Also, a simpler definition of ordena that does not require the auxiliary functions menores and mejores would be:
fun ordena :: "('a :: linorder) list ⇒ 'a list" where
"ordena [] = []"
| "ordena (x#xs) = ordena [y ← xs. y < x] # [x] # ordena [y ← xs. y ≥ x]"
Note that [y ← xs. y < x] is simply syntactic sugar for filter (λy. y < x) xs. Then you don't need cuenta_mejores_menores anymore and can use the following very general lemma on the interaction between cuenta and filter:
lemma cuenta_filter [simp]: "cuenta (filter P xs) y = (if P y then cuenta xs y else 0)"
by (induction xs) simp_all
and the proof goes through automatically again:
lemma "cuenta (ordena xs) y = cuenta xs y"
by (induction xs rule: ordena.induct) auto

How to prove the reversion of a doubling function equals the doubling of a reversion function in Isabelle?

I have a function that doubles the elements of a list in the form
double [x1, x2, ...] = [x1, x1, x2, x2, ...]
namely
fun double :: " 'a list ⇒ 'a list"
where
"double [] = []" |
"double (x#xs) = x # x # double xs"
and a function that reverses the elements of a list with the help of another function snoc that adds an element to the right side of a list:
fun snoc :: "'a list ⇒ 'a ⇒ 'a list"
where
"snoc [] x = [x]" |
"snoc (y # ys) x = y # (snoc ys x)"
fun reverse :: "'a list ⇒ 'a list"
where
"reverse [] = []" |
"reverse (x # xs) = snoc (reverse xs) x"
Now I want to prove that
lemma rev_double: "rev (double xs) = double (rev xs)"
is true.
I tried to apply induction on xs
lemma rev_double: "rev (double xs) = double (rev xs)"
by (induction xs)
and I wrote an auxiliary lemma double_snoc that ensures that doubling a list is the same as doubling its first element and the rest of the list (which uses the function snocleft which inserts an element at the left end of a list)
fun snocleft::"'a list ⇒ 'a ⇒ 'a list "
where
"snocleft [] x = [x]" |
"snocleft (y # ys) x = x # (y # ys)"
lemma double_snoc: "double (snocleft xs y) = y # y # double xs"
by (induction xs) auto
I still haven't made any progress in proving the lemma. Do you have some solutions or hints on how to set up the prove?
You define your function as reverse, but in all of your lemmas, you use rev, referring to the pre-defined list reversal function rev.
What you mean is probably this:
lemma reverse_double: "reverse (double xs) = double (reverse xs)"
If you attempt to prove this by induction (with apply (induction xs)), you will get stuck in the induction case with the following goal:
snoc (snoc (double (reverse xs)) a) a =
double (snoc (reverse xs) a)
This should be intuitively obvious: if you first snoc and then double, it is the same as first doubling and then snoc-ing twice. So let's prove this as an auxiliary lemma:
lemma double_snoc: "double (snoc xs x) = snoc (snoc (double xs) x) x"
by (induction xs) auto
Now the proof of reverse_double goes through automatically:
lemma reverse_double: "reverse (double xs) = double (reverse xs)"
by (induction xs) (auto simp: double_snoc)

Resources