(* Final Exam --- February 26, 2023
You are allowed to search and use any property provided in the
standard library of Coq. *)
Require Import Nat.
Require Import List.
Notation "[ ]" := nil.
Notation "[ x ; .. ; y ]" := (cons x .. (cons y nil) ..).
Definition admit {T: Type} : T. Admitted.
(* 1. Prove the following fact about natural numbers by induction. *)
Lemma mul_2_r : forall n : nat,
n * 2 = n + n.
Proof.
intros. induction n.
- reflexivity.
- simpl. rewrite IHn. rewrite <- plus_Sn_m. apply f_equal.
rewrite PeanoNat.Nat.add_comm. reflexivity. Qed.
(* 2. Define a function called cubic so that (cubic n) returns true
iff n is a cubic number, i.e. n = n' * n' * n' for some n'. *)
Fixpoint cubic_m_n (m:nat)(n:nat) : bool :=
match m with
| 0=> n=?0
| S m' => if m*m*m=?n then true
else cubic_m_n m' n
end.
Definition cubic (n : nat) : bool :=
cubic_m_n n n.
Example cubic_test1 : cubic 8 = true.
Proof. simpl. reflexivity. Qed.
Example cubic_test2 : cubic 25 = false.
Proof. simpl. reflexivity. Qed.
(* 3. Let two sequences of numbers F1(n) and F2(n) be given as follows.
F1(0) = 1
F1(n) = 2 * F1(n-1) for n > 0.
F2(0) = F2(1) = 1
F2(n+2) = F2(n) + F2(n+1) for n >= 0.
Define the function Seq such that (Seq n) returns the sequence
[F1(0); F2(0); F1(1); F2(1); ... ; F1(n); F2(n)].
*)
Fixpoint F1 (n:nat) : nat :=
match n with
|0=>1
|S n'=> 2*(F1 n')
end.
Fixpoint F2 (n:nat) :nat :=
match n with
| 0=>1
|S 0=>1
|S n'=>match n' with
| 0 => 2
| S n'' => F2 n' + F2 n''
end
end.
Fixpoint Seq (n:nat) : list nat :=
match n with
|0=>[1;1]
| S n'=>(Seq n')++[F1 n;F2 n]
end.
Example Seq_test : Seq 5 = [1; 1; 2; 1; 4; 2; 8; 3; 16; 5; 32; 8].
Proof. simpl. reflexivity. Qed.
(* 4. Let lt be the predicate such that (lt n m) holds iff n is strictly
less than m. Prove the following theorem about lt. *)
Inductive lt : nat -> nat -> Prop :=
| lt1 : forall n, lt n (S n)
| lt2 : forall n m, lt n m -> lt n (S m).
Theorem n_lt_m__Sn_lt_Sm : forall n m,
lt n m -> lt (S n) (S m).
Proof.
intros.
induction H.
- apply lt1.
- apply lt2. apply IHlt.
Qed.
Theorem lt_add_n: forall n p q,lt p q->lt (n+p) (n+q).
intros.
induction n.
- simpl. apply H.
- simpl. apply n_lt_m__Sn_lt_Sm. apply IHn.
Qed.
Theorem lt_add : forall n m p q, lt n m -> lt p q -> lt (n+p) (m+q).
Proof.
intros. induction H.
- simpl. apply lt2. apply lt_add_n. apply H0.
- simpl. apply lt2. apply IHlt.
Qed.
(* 5. Write a function (transform):
transform : list nat -> list (list nat )
which transforms a list into a list of 3 sublists. The first sublist
contains all the odd numbers in the original list; the second sublist contains
all the numbers divisible by 3 in the original list; the last sublist contains
all the other numbers in the original list.
The order of elements in the three sublists should be the same as their
order in the original list.
Hint: You may use the Coq function (filter).
*)
Fixpoint is_odd_number(n:nat) : bool :=
match n with
|0=>false
|S 0=>true
|S (S n') => is_odd_number n'
end.
Fixpoint divisible_by_three (n:nat) :bool :=
match n with
|O=> true
|S O => false
|S (S O) => false
|S (S (S n')) => divisible_by_three n'
end.
Definition transform (l : list nat) : list (list nat) :=
match l with
|[]=>[[];[];[]]
|_=>[filter is_odd_number l;filter divisible_by_three l;
filter (fun n=> andb (negb (is_odd_number n)) (negb (divisible_by_three n))) l]
end.
Example transform_test:
transform [3;7;6;9;4;5;16;14;15] = [[3; 7; 9; 5; 15]; [3; 6; 9; 15]; [4; 16; 14]].
Proof. simpl. reflexivity. Qed.
(* 6. Prove the following fact about excluded middle. *)
Theorem de_morgan :
(forall P, P \/ ~P) -> (forall P Q, ~(~P \/ ~Q) -> P /\ Q).
Proof.
unfold not. intros. destruct (H P).
- split.
+ apply H1.
+ destruct (H Q).
{ apply H2. }
{ destruct H0. right. apply H2. }
- split.
+ destruct H0. left. apply H1.
+ destruct (H Q).
{ apply H2. }
{ destruct H0. left. apply H1. }
Qed.
(* 7. Consider the following type btree about binary trees.
Define a function to give an inorder traversal of the tree and collect
all the odd numbers in a list.
*)
Inductive btree : Set :=
| leaf : nat -> btree
| node : nat -> btree -> btree -> btree.
Fixpoint inorder (t: btree) : list nat :=
match t with
| leaf n => if is_odd_number n then [n] else []
| node n bt1 bt2 => if is_odd_number n then inorder bt1 ++ [n] ++ inorder bt2
else inorder bt1 ++ inorder bt2
end.
Example bt_test : inorder (node 5 (node 1 (leaf 0) (node 3 (leaf 2) (leaf 4)))
(node 9 (node 7 (leaf 6) (leaf 8)) (leaf 10)))
= [1; 3; 5; 7; 9].
Proof. simpl. reflexivity. Qed.
(* 8. The following definitions specify the abstract syntax of
some arithmetic expressions and an evaluation function. *)
Inductive aexp : Type :=
| ANum : nat -> aexp
| APlus : aexp -> aexp -> aexp
| AMinus : aexp -> aexp -> aexp
| AMult : aexp -> aexp -> aexp.
Fixpoint aeval (a : aexp) : nat :=
match a with
| ANum n => n
| APlus a1 a2 => (aeval a1) + (aeval a2)
| AMinus a1 a2 => (aeval a1) - (aeval a2)
| AMult a1 a2 => (aeval a1) * (aeval a2)
end.
(* Suppose we define a function that takes an arithmetic expression
and slightly simplifies it, changing every occurrence of [e + 0],
[e - 0], or [1 * e] into [e], and [0 * e] into [0]. *)
Fixpoint optimize (a:aexp) : aexp :=
match a with
| ANum n => ANum n
| APlus e1 (ANum 0)=> optimize e1
| AMinus e1 (ANum 0) => optimize e1
| AMult (ANum 0) e1 => ANum 0
| AMult (ANum 1) e1 => optimize e1
| APlus e1 e2 => APlus (optimize e1) (optimize e2)
| AMinus e1 e2 => AMinus (optimize e1) (optimize e2)
| AMult e1 e2 => AMult (optimize e1) (optimize e2)
end.
(* Prove the following theorem that states the correctness of the
optimization with respect to the evaluation of arithmetic expressions. *)
Theorem optimize_sound: forall a,
aeval (optimize a) = aeval a.
Proof.
intros. induction a.
- simpl. reflexivity.
- destruct a2;try(simpl; simpl in IHa2; rewrite IHa2; rewrite IHa1; reflexivity).
+ destruct n.
{ simpl; rewrite IHa1; rewrite PeanoNat.Nat.add_0_r; reflexivity. }
{ simpl; rewrite IHa1; reflexivity. }
- destruct a2;try(simpl; simpl in IHa2; rewrite IHa2; rewrite IHa1; reflexivity).
+ destruct n.
{ simpl; rewrite IHa1; rewrite PeanoNat.Nat.sub_0_r; reflexivity. }
{ simpl; rewrite IHa1; reflexivity. }
- destruct a1;try(simpl; simpl in IHa1; rewrite IHa1; rewrite IHa2; reflexivity).
+ destruct n as [|n'] eqn:Eqn'.
{ simpl; reflexivity. }
{ destruct n'.
{ simpl. rewrite IHa2. rewrite PeanoNat.Nat.add_0_r. reflexivity. }
{ simpl. rewrite IHa2. reflexivity. }
}
Qed.
(* 9. Define in OCaml the function (map2 f l1 l2), where f is a function
that takes two parameters and l1 and l2 are lists, that calls f on each
pair of corresponding elements from l1 and l2 and returns a list of the
results. If l1 and l2 do not have the same number of elements. For example,
the length is n1 for one list and n2 for the other, with n1 < n2, then f
only operates on the first n1 elements for both lists. For example,
# map2 (fun x y -> x + y) [1;2;3;4] [5;6;7;8;9;10];;
- : int list = [6; 8; 10; 12]
*)
let rec map2 f l1 l2 =
match l1,l2 with
|[],_ -> []
|_,[] -> []
|n1::l1',n2::l2' -> (n1+n2)::(map2 f l1' l2')
;;
map2 (fun x y -> x + y) [1;2;3;4] [5;6;7;8;9;10];;
(* 10. We will define in OCaml the function (merge sort list), where list
is an unsorted list of numbers, that implements merge sort and returns a
new list containing the elements of list in sorted order.
Let us proceed in three steps.
(a) Define a function (split l), where l is a list of integers, that returns a
tuple of two lists, (l1, l2), such that half the elements of l are in l1 and half
are in l2, in alternating order. For example,
# split [1];;
- : int list * int list = ([1], [])
# split [1;7;2;6;8;3;9;5;4];;
- : int list * int list = ([1; 2; 8; 9; 4], [7; 6; 3; 5])
*)
let rec split l =
match l with
|[]->([] ,[])
|n::l'-> match l' with
|[] -> ([n],[])
|m::l''->let (l,r) = split l'' in
(n::l,m::r)
;;
split [1];;
split [1;7;2;6;8;3;9;5;4];;
(*
(b) Define a function (merge l1 l2) that merges two lists sorted in ascending
order into a new list, still in ascending order. For example,
# merge [1;3;5] [2;6;8];;
- : int list = [1; 2; 3; 5; 6; 8]
*)
let rec merge l1 l2 =
match l1,l2 with
|[],_->l2
|_,[]->l1
|n1::l1',n2::l2'->if n1<n2 then n1::merge l1' l2
else n2::merge l1 l2'
;;
merge [] [1];;
merge [1;3;5] [2;6;8];;
(*
(c) Now define the function (merge sort list) by making use of the two functions
split and merge defined above to sort the list list. For example,
# merge_sort [2;4;1;6;9;6;4;1;3;5;10] ;;
- : int list = [1; 1; 2; 3; 4; 4; 5; 6; 6; 9; 10]
*)
let rec mergesort list =
match list with
| [] -> []
|[x] -> [x]
| h::tl -> let (l , r) = split list in
merge (mergesort l) (mergesort r);;
mergesort[2;4;1;6;9;6;4;1;3;5;10] ;;