(* Solutions of "Main topic" are provided here with additional material *) (* ============================================================ *) (* A SPECIFICATION IS RELATION BETWEEN INPUTS AND OUTPUT *) (* ============================================================ *) (* ------------------------------------------------------------ *) (* WARMING UP *) (* Here is a simple example: greatest common divisor between 2 natural numbers *) (* We need auxiliary definitions *) Definition divides (n m: nat): Prop := exists d, n * d = m. Definition is_common_div (n:nat) (m: nat) (cd: nat): Prop := divides cd n /\ divides cd m. (* Now we specify that the gcd [g] of [n] and [m] - is a common divisor of [n] and [m] - is greater than any other common divisor of [n] and [m] *) Definition spec_gcd (n:nat) (m: nat) (g: nat): Prop := is_common_div n m g /\ (forall x, is_common_div n m x -> x <= g). (* Now we could propose a function [gcd] and prove that it satisfies tha above specification *) Fixpoint gcd (n m: nat) : nat. (* NO NEED TO FILL IN HERE! *) Admitted. Theorem gcd_correct: forall n m, spec_gcd n m (gcd n m). Proof. (* NO NEED TO FILL IN HERE! *) Admitted. (* ----------------------------------- *) (* Another simple example: predecessor of a natural number *) Definition spec_pred_first_attempt (n:nat) (p: nat): Prop := n = (S p). (* This specification is wrong because it *enforces* the input [n] to be non-zero, whereas only total functions can be defined. The right specification should then be weakened as follows: *) Definition spec_pred (n:nat) (p: nat): Prop := n <> 0 -> n = (S p). (* Now we can prove: *) Theorem pred_correct: forall n, spec_pred n (pred n). Proof. (* FILL IN HERE *) Admitted. (* ----------------------------------- *) (* Another approach to the predecessor *) (* Instead of considering the standard [pred: nat -> nat] consider a [ppred: forall n, positive n -> nat] *) Definition positive (n: nat) := match n with O => False | _ => True end. (* Then the specification has 3 arguments (2 inputs and 1 output) *) Definition spec_ppred (n: nat) (Pn: positive n) (p: nat) := n = S p. (* The definition of [ppred] is more complicated *) Definition ppred_interactive (n:nat) (Pn: positive n) : nat. destruct n as [ | p]; simpl in Pn. case Pn. exact p. Defined. Definition ppred (n:nat) (Pn: positive n) : nat := match n return positive n -> nat with | 0 => fun F => match F with end | S p => fun _ => p end Pn. Theorem ppred_correct: forall n Pn, spec_ppred n Pn (ppred n Pn). Proof. (* FILL IN HERE *) Admitted. (* ------------------------------------------------------------ *) (* MAIN TOPIC *) (* Finding the minimum element in a list *) Section sec_min. Variable A : Set. Variable default : A. (* comparison : R x y means x less or equal to y *) Variable R : A -> A -> Prop. (* Some properties of R? *) Hypothesis refl: forall x, R x x. Hypothesis total : forall x y, R x y \/ R y x. (* choice 1 for comparison pgm *) Variable bool_comp : A -> A -> bool. Hypothesis bool_comp1: forall x y, bool_comp x y = true -> R x y. Hypothesis bool_comp2: forall x y, bool_comp x y = false -> R y x. (* choice 2 for comparison pgm *) Variable comp : forall x y, {R x y}+{R y x}. Inductive list : Set := | nil : list | cons : A -> list -> list. (* ------------------------------------------------------------ *) (* Informal expectations: - the result is in A - have a list l as a parameter of find_min - the result is a member of l ===> define member - the result is less or equal (R) to all members of l *NO "other" ---> reflexivity of R *) (* Draft Definition find_min (l : list) : A := *) (* Draft thoughts You can put here informal ideas, unfinished statements etc. member : A -> list -> Prop forall l : list, m : A forall x:A, member x l -> R m x *) (* Define member *) Fixpoint member (x:A) (l:list) : Prop := match l with | nil => False | cons y l => x = y \/ member x l end. (* -------------------------------------------------- *) (* Other auxiliary predicates ? *) Definition lower_bound (m: A) (l: list) := forall x:A, member x l -> R m x. (* Some other auxiliary definitions + properties *) (* informally : if x smaller than y then x else y *) Definition minimum (x y : A) : A. destruct (comp x y) as [ xy | yx ]. exact x. exact y. Defined. Lemma min_among : forall x y, minimum x y = x \/ minimum x y = y. Proof. intros x y. unfold minimum. destruct (comp x y) as [ xy | yx ]. left; reflexivity. right; reflexivity. Qed. Lemma min_sm1 : forall x y, R (minimum x y) x. Proof. intros x y. unfold minimum. destruct (comp x y) as [ xy | yx ]. apply refl. exact yx. Qed. Lemma min_sm2 : forall x y, R (minimum x y) y. Admitted. (* ------------------------------------------------------- *) (* Yet another auxiliary predicate to be used later *) Definition non_empty (l: list): Prop := match l with | nil => False | cons x l => True end. (* Another possibility is provide an inductive definition *) Inductive ind_non_empty : list -> Prop := | ind_non_empty_intro: forall x l, ind_non_empty (cons x l). (* ------------------------------------------------------- *) (* Specification of the expected result of [find_min] *) (* The following relation should express expected constraints between the input [l] and the result [m] *) Definition spec_find_min (l: list) (m: A) := member m l /\ lower_bound m l. (* ================================================================= *) (* Issue: dealing with partial functions such as find_min *) (* The first idea which comes to mind is to directly try something like : Definition fm_direct (l: list) (nel: non_empty l): A. As it is recursive, we would try: *) (* Five starts ***** *) Fixpoint fm_direct (l': list): (non_empty l') -> A. (* Not that easy indeed. Try it and you will meet some technical issues. Don't insist too much. *) Admitted. (* A better idea way is as follows. Instead of working with - a list [l'] - a proof that [l'] is non-empty it is just more simple to work with - an element [x] in A - a list [l] considering that the intended meaning of [x] and [l] together represent the list [l'] above. *) (* Then several approaches are possible *) (* ----------------------------------------------------------------- *) (* First choice: basic function + separate correctness proof *) (* find_min auxiliary function *) Fixpoint fm_aux (x: A) (l: list) := match l with | nil => x | cons y l => minimum x (fm_aux y l) end. (* Version 1: defined everywhere, similar to [pred] *) Definition find_min (l: list) : A := match l with | nil => default | cons x l => fm_aux x l end. (* Version 2: more precise typing, similar to [ppred] *) Definition partial_find_min (l: list) : non_empty l -> A := match l with | nil => fun F => match F return A with end | cons x l => fun _ => fm_aux x l end. (* interactively: destruct l as [ | x l]. simpl. intro F; case F. intros _; exact (fm_aux x l). *) (* Remark: using the inductive definition is possible as well *) (* Version 2': variant of version 2 *) Definition partial_find_min (l: list) : ind_non_empty l -> A. Admitted. (* ------------------ *) (* Correctness proofs *) Lemma fm_aux_correct1 : forall (x: A) (l: list), fm_aux x l = x \/ member (fm_aux x l) l. Proof. Admitted. Lemma find_min_correct1 : forall l: list, non_empty l -> member (find_min l) l. Proof. Admitted. Lemma partial_find_min_correct1 : forall (l: list) (ne: non_empty l), member (partial_find_min l ne) l. Admitted. (* TODO: state and prove all similar lemmas about partial_find_min *) (* Additional assumptions on R may be needed... *) Lemma find_min_correct2 : forall l: list, non_empty l -> lower_bound (find_min l) l. Admitted. Theorem find_min_correct : forall l: list, non_empty l -> (* assumption on the input? *) spec_find_min l (find_min l). Proof. intros l ne. red. split. apply find_min_correct1. assumption. apply find_min_correct2. assumption. Qed. (* End of first choice *) (* ----------------------------------------------------------------- *) (* Second choice: enriching functions by proofs *) (* Optional. Go first to "Third choice". Solve this one if you have time *) (* You can define auxiliary (enriched) functions *) Definition find_min_c (l: list) : non_empty l -> {m: A | spec_find_min l m}. Admitted. (* End of second choice *) (* ----------------------------------------------------------------- *) (* Third choice: partial functions described by inductive relations *) Inductive rel_min: list -> A -> Prop := | rm1: forall x, rel_min (cons x nil) x | rm2: forall x l m, rel_min l m -> R x m -> rel_min (cons x l) x | rm3: forall x l m, rel_min l m -> R m x -> rel_min (cons x l) m. (* First we can prove that find_min behaves according to rel_min *) (* This is the only place where fm_aux is taken into account *) (* TO BE READ CAREFULLY *) Lemma find_rel_min: forall l, non_empty l -> rel_min l (find_min l). Proof. intros l ne. destruct l as [ | x l]; simpl in *. case ne; clear ne. (* reasoning about fm_aux *) revert x. induction l as [ | y l Hl]; simpl in *; intro x. apply rm1. (* Target: rm2 and rm3; first, the suitable l for rel_min is (cons y l) *) generalize (Hl y). pose (m:=fm_aux y l); fold m. pose (yl:=cons y l); fold yl. intros Hylm. (* Choose rm2 or rm3 according to (R x m) or (R m x) *) unfold minimum. case (comp x m). intro xm; apply (rm2 _ _ _ Hylm xm). intro mx; apply (rm3 _ _ _ Hylm mx). Qed. (* Now, we can reason on rel_min instead of find_min without worrying about find_min, fm_aux, minimum, etc. *) Lemma rel_min_correct1 : forall (l: list) (m: A), rel_min l m -> member m l. Proof. intros l m r. induction r as [x | x l m rlm Hrlm rxm | x l m rlm Hrm rxml ]; simpl. left; reflexivity. left; reflexivity. right; apply Hrm. Qed. Lemma rel_min_correct2 : forall (l: list) (m: A), rel_min l m -> lower_bound m l. Admitted. Theorem rel_min_correct : forall (l: list) (m: A), rel_min l m -> spec_find_min l m. Proof. split. apply rel_min_correct1; assumption. apply rel_min_correct2; assumption. Qed. (* Expected corollary *) Theorem find_min_correct' : forall l: list, non_empty l -> (* assumption on the input *) spec_find_min l (find_min l). Proof. intros l ne; generalize (find_rel_min l ne). apply rel_min_correct. Qed. (* End of third choice *) End sec_min.