Showing posts with label correctness. Show all posts
Showing posts with label correctness. Show all posts

Thursday, 19 June 2008

Proving RLE encoding and decoding in Coq


Require Import List.
Require Import ListPlus.

Set Implicit Arguments.

Notation "y <- x" := (x -> y) (at level 94, left associativity, only parsing).

Definition compose A B C (f : C <- B) (g : B <- A) := fun x => f (g x).
Infix "∘" := compose (at level 30, right associativity).

(*
* rle = map (length &&& head) ∘ group
* unrle = concat ∘ map (uncurry replicate)
*
* is it always true that (unrle ∘ rle) x = x?
*
* notice:
* rle = f ∘ f'
* unrle = g' ∘ g
*
*)

Theorem sandwich : forall I M O
(f : O <- M) (f' : M <- I)
(g : O -> M) (g' : M -> I),
(forall x, (f ∘ g ) x = x) ->
(forall x, (f' ∘ g') x = x) ->
(forall x, (f ∘ f' ∘ g' ∘ g) x = x).
Proof.
unfold compose; intros.
rewrite (H0 (g x)).
apply H.
Qed.

(*
* so all that must be proved now is
* forall x, (concat ∘ group) x = x
* /\ forall x, (map (uncurry replicate) ∘ map (length &&& head))
*
*)

Theorem map_fuse : forall A B C (f : C <- B) (g : B <- A),
forall x, (map f ∘ map g) x = map (f ∘ g) x.
Proof.
induction x.
reflexivity.
simpl; rewrite <- IHx.
reflexivity.
Qed.

(*
* Now we have "only" to prove
* forall x, (concat ∘ group) x = x
* /\ forall x, (uncurry replicate ∘ (length &&& head)) x = x
*
* This is actually impossible though,
* (uncurry replicate ∘ (length &&& head)) "foo" = "fff"
*
* The reason rle works is because group segments the list
* into *homogenous* chunks, if we knew the input to (length && head)
* was a homogenous list then it would be correct and provable.
* It needs access to this precondition though
*
*)

Theorem sandwich' : forall I M O
(P : M -> Prop)
(f : O <- M) (f' : M <- I)
(g : O -> M) (g' : M -> I),
(forall x, (f ∘ g ) x = x) ->
(forall x, P x -> (f' ∘ g') x = x) ->
(forall x, P (g x)) ->
(forall x, (f ∘ f' ∘ g' ∘ g) x = x).
Proof.
unfold compose; intros.
rewrite (H0 (g x)).
apply H.
apply H1.
Qed.

Inductive Homogenous A : listP A -> Prop :=
| HEnd : forall u, Homogenous (nilP u)
| HOne : forall u, Homogenous (u:~:nilP u)
| HAdd : forall u us, Homogenous (u:~:us) -> Homogenous (u:~:u:~:us).
Hint Constructors Homogenous.

Inductive All X (P : X -> Prop) : list X -> Prop :=
| AllNil : All P nil
| AllCons : forall x xs, P x -> All P xs -> All P (x::xs).
Hint Constructors All.

Section RLE.

Variable A : Type.
Variable eq_dec : forall x y : A, { x = y } + { x <> y }.

Definition head_consP X (e : X) (l : list (listP X)) :=
match l with
| nil => nil
| x::xs => (e:~:x)::xs
end.

Fixpoint group (l : list A) : list (listP A) :=
match l with
| nil => nil
| x::xs =>
match xs with
| nil => nilP x::nil
| y::ys =>
match eq_dec x y with
| left equal => head_consP x (group xs)
| right not_equal => nilP x::group xs
end
end
end.
Fixpoint concat (l : list (listP A)) : list A :=
match l with
| nil => nil
| x::xs => appP' x (concat xs)
end.

Definition crush (l : listP A) : (nat * A) := (lengthP l, headP l).
Definition expand (t : nat * A) : listP A := let (n,e) := t in replicateP n e.

Definition rle := map crush ∘ group.
Definition unrle := concat ∘ map expand.

Functional Scheme group_ind := Induction for group Sort Prop.
Lemma concat_group_inv : forall x, (concat ∘ group) x = x.
unfold compose.
intro x; functional induction (group x).

reflexivity.

reflexivity.

Lemma cat_cons : forall u v vs, concat (head_consP u (v::vs)) = u :: concat (v::vs).
destruct vs; reflexivity.
Qed.

destruct (group (y :: _x)).
inversion IHl.
rewrite cat_cons.
rewrite IHl.
reflexivity.

simpl in *.
rewrite IHl.
reflexivity.
Qed.

Lemma expand_crush_inv : forall x, Homogenous x -> (expand ∘ crush) x = x.
unfold compose.
induction x.
reflexivity.
intro; inversion H.
rewrite <- H2 in IHx.
reflexivity.
simpl in *.
rewrite <- H2 in *.
rewrite <- IHx.
reflexivity.
assumption.
Qed.

(*
* some notes I had to write to crack this lemma,
*
* group [] = []
* group [x] = [[x]]
* group (x:y:ys) = if x == y then headCons x (group (y:ys))
* else [x] : group (y:ys)
* where headCons x (y:ys) = (x:y):ys
*
* Theorem: forall xs, All Homogenous (group xs)
*
* group [] ~ trivial
* group [x] ~ trivial
* group (x:y:ys) = [x] : group (y:ys)
* homo ^ ^ homo by induction
*
* group (x:x:ys) = headCons x (group (x:xs))
*
* (group (x:xs)) = ((x:p):q)
* homogenous ^ ^ all homogenous by induction
*
* group (x:x:ys) = headCons x ((x:p):q)
* = ((x:x:p):q)
* homo cons ^
*)

Lemma group_homo : forall x, All (@Homogenous A) (group x).
induction x.
simpl; auto.
destruct x.
simpl; auto.

simpl.
destruct (eq_dec a a0).

rewrite <- e in *.

Lemma paper_plane : forall xs x,
exists p, exists q,
group (x::xs) = ((x:~:p)::q) \/ group (x::xs) = ((nilP x)::q).
induction xs.
simpl.
intro x; exists (nilP x); exists nil; auto.
intro x.
simpl.
destruct (eq_dec x a).
rewrite <- e in *; clear e a.

destruct (IHxs x).
destruct H.
destruct H.
simpl in H.
rewrite H.
simpl.
exists (x :~: x0).
exists x1.
left; reflexivity.
simpl in H.
rewrite H.
simpl.
exists (nilP x).
exists x1.
left; reflexivity.

destruct (IHxs a).
destruct H.
destruct H.
simpl in H.
destruct xs.
exists (nilP a).
exists (nilP a :: nil).
right; reflexivity.
rewrite H.
exists (nilP a).
exists ((a :~: x0) :: x1).
right; reflexivity.
simpl in H.
rewrite H.
exists (nilP a).
exists (nilP a :: x1).
right; reflexivity.
Qed.

pose (paper_plane x a).
inversion e0.
inversion H.
inversion H0.

rewrite H1 in IHx.
simpl in H1.
rewrite H1.
simpl.
apply AllCons.
apply HAdd.
inversion IHx; auto.
inversion IHx; auto.

rewrite H1 in IHx.
simpl in H1.
rewrite H1.
simpl.
apply AllCons.
apply HOne.
inversion IHx.
assumption.

apply AllCons.
auto.
apply IHx.
Qed.

Theorem rle_inv : forall x, unrle (rle x) = x.
intro; unfold unrle, rle.
apply sandwich' with (P := All (@Homogenous A)).

exact concat_group_inv.

clear x; intros; rewrite map_fuse; induction x.
reflexivity.
simpl.
inversion H.
rewrite (IHx H3).
rewrite (expand_crush_inv H2).
reflexivity.

exact group_homo.
Qed.

End RLE.

Require Import Arith.

Eval compute in (rle eq_nat_dec (1::2::3::3::3::5::5::5::1::1::1::6::6::6::6::6::nil)).
(*
* = (0, 1) :: (0, 2) :: (2, 3) :: (2, 5) :: (2, 1) :: (4, 6) :: nil
* : list (nat * nat)
*)

Eval compute in (unrle (rle eq_nat_dec (1::2::3::3::3::5::5::5::1::1::1::6::6::6::6::6::nil))).
(* = 1::2::3::3 ::3 ::5 ::5 ::5::1::1::1::6::6::6::6::6::nil
* : list nat
*)