Solution for Labs 03
Require Import Coq.Lists.List.
Import ListNotations.
Require Import Coq.micromega.Psatz. (* `lia` tactic for linear integer arithmetic *)
(* enables use of `eauto with lia` *)
Hint Extern 1 => lia: lia.
(* destruct pairs in context *)
Ltac destruct_pairs :=
match goal with
| H: _ * _ |- _ => destruct H
end.
(* destruct existentials in context *)
Ltac destruct_exists :=
match goal with
| H: exists x, _ |- _ => let freshX := fresh x in destruct H as [ freshX ]
end.
(* destruct refinements in context *)
Ltac destruct_refinements :=
match goal with
| H: { x: _ | _ } |- _ => let freshX := fresh x in destruct H as [ freshX ]
end.
(* one step using basic Coq tactics, using computation simplification *)
Ltac step0 :=
(intuition auto) || congruence || subst ||
destruct_exists || destruct_pairs || destruct_refinements.
(* one step using basic Coq tactics, with computation simplification *)
(* use `repeat step` for performing all possible simplifications *)
Ltac step := step0 || cbn in *.
(* performs case analysis whenever there is a `match` somewhere *)
(* you can e.g. use `repeat step || destruct_match` if you want to *)
(* perform as many simplifications and case analyses as possible *)
Ltac destruct_match :=
match goal with
| [ |- context[match ?t with _ => _ end]] =>
let matched := fresh "m" in
destruct t eqn:matched
| [ H: context[match ?t with _ => _ end] |- _ ] =>
let matched := fresh "m" in
destruct t eqn:matched
end.
(* when the goal is an equality between two terms starting using the same constructor *)
(* this generates subgoals for proving that the arguments are equal *)
Ltac constructor_equality :=
match goal with
| |- ?F _ = ?F _ => is_constructor F; f_equal
| |- ?F _ _ = ?F _ _ => is_constructor F; f_equal
| |- ?F _ _ _ = ?F _ _ _ => is_constructor F; f_equal
| |- ?F _ _ _ _ = ?F _ _ _ _ => is_constructor F; f_equal
| |- ?F _ _ _ _ _ = ?F _ _ _ _ _ => is_constructor F; f_equal
| |- ?F _ _ _ _ _ _ = ?F _ _ _ _ _ _ => is_constructor F; f_equal
end.
(* when there is an equality between the same constructor in a hypothesis H *)
(* this creates new hypotheses stating that the arguments are equal *)
Ltac invert_constructor_equality :=
match goal with
| H: ?F _ = ?F _ |- _ => is_constructor F; inversion H; clear H
| H: ?F _ _ = ?F _ _ |- _ => is_constructor F; inversion H; clear H
| H: ?F _ _ _ = ?F _ _ _ |- _ => is_constructor F; inversion H; clear H
| H: ?F _ _ _ _ = ?F _ _ _ _ |- _ => is_constructor F; inversion H; clear H
| H: ?F _ _ _ _ _ = ?F _ _ _ _ _ |- _ => is_constructor F; inversion H; clear H
| H: ?F _ _ _ _ _ _ = ?F _ _ _ _ _ _ |- _ => is_constructor F; inversion H; clear H
end.
Definition queue (T : Type) : Type := list T * list T.
Definition empty_queue (T : Type) : queue T := (nil, nil).
Definition enqueue { T } (x : T) (q : queue T) : queue T :=
let (front, rear) := q in
(front, x :: rear).
Definition dequeue { T } (q : queue T) : option (T * queue T) :=
let (front, rear) := q in
match front with
| nil =>
let reversed_rear := rev rear in
match reversed_rear with
| nil => None
| x :: xs => Some (x, (xs, nil))
end
| x :: xs => Some (x, (xs, rear))
end.
Definition toList { T } (q : queue T) : list T := fst q ++ rev (snd q).
Lemma enqueue_correct:
forall T (x : T) (q : queue T),
toList (enqueue x q) = toList q ++ [x].
Proof.
unfold toList, enqueue; repeat step || destruct_match;
eauto with datatypes.
Qed.
Lemma dequeue_some_sound:
forall T (x : T) (q q' : queue T),
dequeue q = Some (x, q') ->
toList q = x :: toList q'.
Proof.
unfold toList, dequeue;
repeat step || destruct_match || invert_constructor_equality || rewrite app_nil_r.
Qed.
Lemma dequeue_none_sound:
forall T (q : queue T),
dequeue q = None ->
toList q = [].
Proof.
unfold toList, dequeue; repeat step || destruct_match.
Qed.
Lemma dequeue_some_complete:
forall T (x : T) (xs : list T) (q : queue T),
toList q = x :: xs ->
exists (q' : queue T),
dequeue q = Some (x, q') /\
toList q' = xs.
Proof.
unfold toList, dequeue;
repeat step || destruct_match || invert_constructor_equality || eexists;
eauto with datatypes.
Qed.
Lemma dequeue_none_complete:
forall T (q : queue T),
toList q = [] ->
dequeue q = None.
Proof.
unfold toList, dequeue; repeat step || destruct_match.
Qed.
Theorem dequeue_none_correct:
forall T (q : queue T),
toList q = [] <->
dequeue q = None.
Proof.
intros; split; eauto using dequeue_none_sound, dequeue_none_complete.
Qed.
Theorem dequeue_some_correct:
forall T (q : queue T) (x : T) (xs : list T),
toList q = x :: xs <->
exists (q' : queue T),
dequeue q = Some (x, q') /\
toList q' = xs.
Proof.
repeat step || destruct_exists;
eauto using dequeue_some_sound, dequeue_some_complete.
Qed.