2018-02-18 17:49:36 +00:00
|
|
|
(** Formal Reasoning About Programs <http://adam.chlipala.net/frap/>
|
|
|
|
* Supplementary Coq material: first-class functions and continuations
|
|
|
|
* Author: Adam Chlipala
|
|
|
|
* License: https://creativecommons.org/licenses/by-nc-nd/4.0/ *)
|
|
|
|
|
|
|
|
Require Import Frap.
|
|
|
|
|
|
|
|
|
2018-02-18 23:19:39 +00:00
|
|
|
(** * Some data fodder for us to compute with later *)
|
2018-02-18 17:49:36 +00:00
|
|
|
|
2018-02-18 18:05:56 +00:00
|
|
|
Record programming_language := {
|
2018-02-18 17:49:36 +00:00
|
|
|
Name : string;
|
|
|
|
PurelyFunctional : bool;
|
|
|
|
AppearedInYear : nat
|
|
|
|
}.
|
|
|
|
|
|
|
|
Definition pascal := {|
|
|
|
|
Name := "Pascal";
|
|
|
|
PurelyFunctional := false;
|
|
|
|
AppearedInYear := 1970
|
|
|
|
|}.
|
|
|
|
|
|
|
|
Definition c := {|
|
|
|
|
Name := "C";
|
|
|
|
PurelyFunctional := false;
|
|
|
|
AppearedInYear := 1972
|
|
|
|
|}.
|
|
|
|
|
|
|
|
Definition gallina := {|
|
|
|
|
Name := "Gallina";
|
|
|
|
PurelyFunctional := true;
|
|
|
|
AppearedInYear := 1989
|
|
|
|
|}.
|
|
|
|
|
|
|
|
Definition haskell := {|
|
|
|
|
Name := "Haskell";
|
|
|
|
PurelyFunctional := true;
|
|
|
|
AppearedInYear := 1990
|
|
|
|
|}.
|
|
|
|
|
|
|
|
Definition ocaml := {|
|
|
|
|
Name := "OCaml";
|
|
|
|
PurelyFunctional := false;
|
|
|
|
AppearedInYear := 1996
|
|
|
|
|}.
|
|
|
|
|
|
|
|
Definition languages := [pascal; c; gallina; haskell; ocaml].
|
|
|
|
|
2018-02-18 23:19:39 +00:00
|
|
|
|
|
|
|
(** * Classic list functions *)
|
|
|
|
|
2018-02-19 19:02:17 +00:00
|
|
|
Fixpoint map {A B} (f : A -> B) (ls : list A) : list B :=
|
2018-02-18 23:19:39 +00:00
|
|
|
match ls with
|
|
|
|
| nil => nil
|
|
|
|
| x :: ls' => f x :: map f ls'
|
|
|
|
end.
|
|
|
|
|
2018-02-19 19:02:17 +00:00
|
|
|
Fixpoint filter {A} (f : A -> bool) (ls : list A) : list A :=
|
2018-02-18 23:19:39 +00:00
|
|
|
match ls with
|
|
|
|
| nil => nil
|
|
|
|
| x :: ls' => if f x then x :: filter f ls' else filter f ls'
|
|
|
|
end.
|
|
|
|
|
2018-02-19 19:02:17 +00:00
|
|
|
Fixpoint fold_left {A B} (f : B -> A -> B) (ls : list A) (acc : B) : B :=
|
2018-02-18 23:19:39 +00:00
|
|
|
match ls with
|
|
|
|
| nil => acc
|
|
|
|
| x :: ls' => fold_left f ls' (f acc x)
|
|
|
|
end.
|
|
|
|
|
2018-02-18 17:49:36 +00:00
|
|
|
Compute map Name languages.
|
|
|
|
Compute map Name (filter PurelyFunctional languages).
|
2018-02-18 21:46:12 +00:00
|
|
|
Compute fold_left max (map AppearedInYear languages) 0.
|
|
|
|
Compute fold_left max (map AppearedInYear (filter PurelyFunctional languages)) 0.
|
|
|
|
|
|
|
|
(* To avoid confusing things, we'll revert to the standard library's (identical)
|
|
|
|
* versions of these functions for the remainder. *)
|
|
|
|
Reset map.
|
|
|
|
|
|
|
|
|
2018-02-19 19:47:40 +00:00
|
|
|
(** * Sorting, parameterized in a comparison operation *)
|
|
|
|
|
|
|
|
Fixpoint insert {A} (le : A -> A -> bool) (new : A) (ls : list A) : list A :=
|
|
|
|
match ls with
|
|
|
|
| [] => [new]
|
|
|
|
| x :: ls' =>
|
|
|
|
if le new x then
|
|
|
|
new :: ls
|
|
|
|
else
|
|
|
|
x :: insert le new ls'
|
|
|
|
end.
|
|
|
|
|
|
|
|
Fixpoint insertion_sort {A} (le : A -> A -> bool) (ls : list A) : list A :=
|
|
|
|
match ls with
|
|
|
|
| [] => []
|
|
|
|
| x :: ls' => insert le x (insertion_sort le ls')
|
|
|
|
end.
|
|
|
|
|
|
|
|
Fixpoint sorted {A} (le : A -> A -> bool) (ls : list A) : bool :=
|
|
|
|
match ls with
|
|
|
|
| [] => true
|
|
|
|
| x1 :: ls' =>
|
|
|
|
match ls' with
|
|
|
|
| x2 :: _ => le x1 x2 && sorted le ls'
|
|
|
|
| [] => true
|
|
|
|
end
|
|
|
|
end.
|
|
|
|
|
|
|
|
Lemma insert_sorted : forall {A} (le : A -> A -> bool) a,
|
|
|
|
(forall x y, le x y = false -> le y x = true)
|
|
|
|
-> forall ls, sorted le ls = true
|
|
|
|
-> sorted le (insert le a ls) = true.
|
|
|
|
Proof.
|
|
|
|
induct ls; simplify; trivial.
|
|
|
|
|
|
|
|
cases (le a a0); simplify.
|
|
|
|
|
|
|
|
rewrite Heq; simplify.
|
|
|
|
trivial.
|
|
|
|
|
|
|
|
cases ls; simplify.
|
|
|
|
rewrite H; trivial.
|
|
|
|
apply andb_true_iff in H0; propositional.
|
|
|
|
cases (le a a1); simplify.
|
|
|
|
apply andb_true_iff in H0; propositional.
|
|
|
|
rewrite H; trivial.
|
|
|
|
simplify.
|
|
|
|
rewrite H3, H4; trivial.
|
|
|
|
rewrite H1; simplify.
|
|
|
|
trivial.
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
Theorem insertion_sort_sorted : forall {A} (le : A -> A -> bool),
|
|
|
|
(forall x y, le x y = false -> le y x = true)
|
|
|
|
-> forall ls,
|
|
|
|
sorted le (insertion_sort le ls) = true.
|
|
|
|
Proof.
|
|
|
|
induct ls; simplify; trivial.
|
|
|
|
apply insert_sorted; trivial.
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
Definition not_introduced_later (l1 l2 : programming_language) : bool :=
|
|
|
|
if AppearedInYear l1 <=? AppearedInYear l2 then true else false.
|
|
|
|
|
|
|
|
Compute insertion_sort
|
|
|
|
not_introduced_later
|
|
|
|
[gallina; pascal; c; ocaml; haskell].
|
|
|
|
|
|
|
|
Corollary insertion_sort_languages : forall langs,
|
|
|
|
sorted not_introduced_later (insertion_sort not_introduced_later langs) = true.
|
|
|
|
Proof.
|
|
|
|
simplify.
|
|
|
|
apply insertion_sort_sorted.
|
|
|
|
unfold not_introduced_later.
|
|
|
|
simplify.
|
|
|
|
cases (AppearedInYear x <=? AppearedInYear y); try equality.
|
|
|
|
cases (AppearedInYear y <=? AppearedInYear x); try equality.
|
|
|
|
linear_arithmetic.
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
|
2018-02-18 21:46:12 +00:00
|
|
|
(** * Motivating continuations with search problems *)
|
|
|
|
|
2018-02-19 19:02:17 +00:00
|
|
|
Fixpoint allSublists {A} (ls : list A) : list (list A) :=
|
2018-02-18 21:46:12 +00:00
|
|
|
match ls with
|
|
|
|
| [] => [[]]
|
|
|
|
| x :: ls' =>
|
|
|
|
let lss := allSublists ls' in
|
|
|
|
lss ++ map (fun ls'' => x :: ls'') lss
|
|
|
|
end.
|
|
|
|
|
|
|
|
Definition sum ls := fold_left plus ls 0.
|
|
|
|
|
|
|
|
Fixpoint sublistSummingTo (ns : list nat) (target : nat) : option (list nat) :=
|
|
|
|
match filter (fun ns' => if sum ns' ==n target then true else false) (allSublists ns) with
|
|
|
|
| ns' :: _ => Some ns'
|
|
|
|
| [] => None
|
|
|
|
end.
|
|
|
|
|
|
|
|
Fixpoint countingDown (from : nat) :=
|
|
|
|
match from with
|
|
|
|
| O => []
|
|
|
|
| S from' => from' :: countingDown from'
|
|
|
|
end.
|
|
|
|
|
|
|
|
Time Compute sublistSummingTo (countingDown 20) 1.
|
|
|
|
|
2018-02-19 19:02:17 +00:00
|
|
|
Fixpoint allSublistsK {A B} (ls : list A)
|
2018-02-18 21:46:12 +00:00
|
|
|
(failed : unit -> B)
|
|
|
|
(found : list A -> (unit -> B) -> B) : B :=
|
|
|
|
match ls with
|
|
|
|
| [] => found [] failed
|
|
|
|
| x :: ls' =>
|
|
|
|
allSublistsK ls'
|
|
|
|
failed
|
|
|
|
(fun sol failed' =>
|
|
|
|
found sol (fun _ => found (x :: sol) failed'))
|
|
|
|
end.
|
|
|
|
|
|
|
|
Definition sublistSummingToK (ns : list nat) (target : nat) : option (list nat) :=
|
|
|
|
allSublistsK ns
|
|
|
|
(fun _ => None)
|
|
|
|
(fun sol failed =>
|
|
|
|
if sum sol ==n target then Some sol else failed tt).
|
|
|
|
|
|
|
|
Time Compute sublistSummingToK (countingDown 20) 1.
|
|
|
|
|
2018-02-18 18:05:56 +00:00
|
|
|
|
|
|
|
(** * The classics in continuation-passing style *)
|
|
|
|
|
2018-02-19 19:02:17 +00:00
|
|
|
Fixpoint mapK {A B R} (f : A -> (B -> R) -> R) (ls : list A) (k : list B -> R) : R :=
|
2018-02-18 18:05:56 +00:00
|
|
|
match ls with
|
|
|
|
| nil => k nil
|
|
|
|
| x :: ls' => f x (fun x' => mapK f ls' (fun ls'' => k (x' :: ls'')))
|
|
|
|
end.
|
|
|
|
|
2018-02-19 19:02:17 +00:00
|
|
|
Fixpoint filterK {A R} (f : A -> (bool -> R) -> R) (ls : list A) (k : list A -> R) : R :=
|
2018-02-18 18:05:56 +00:00
|
|
|
match ls with
|
|
|
|
| nil => k nil
|
|
|
|
| x :: ls' => f x (fun b => filterK f ls' (fun ls'' => k (if b then x :: ls'' else ls'')))
|
|
|
|
end.
|
|
|
|
|
2018-02-19 19:02:17 +00:00
|
|
|
Fixpoint fold_leftK {A B R} (f : B -> A -> (B -> R) -> R) (ls : list A) (acc : B) (k : B -> R) : R :=
|
2018-02-18 18:05:56 +00:00
|
|
|
match ls with
|
|
|
|
| nil => k acc
|
2018-02-18 23:19:39 +00:00
|
|
|
| x :: ls' => f acc x (fun x' => fold_leftK f ls' x' k)
|
2018-02-18 18:05:56 +00:00
|
|
|
end.
|
|
|
|
|
2018-02-19 19:02:17 +00:00
|
|
|
Definition NameK {R} (l : programming_language) (k : string -> R) : R :=
|
2018-02-18 18:05:56 +00:00
|
|
|
k (Name l).
|
2018-02-19 19:02:17 +00:00
|
|
|
Definition PurelyFunctionalK {R} (l : programming_language) (k : bool -> R) : R :=
|
2018-02-18 18:05:56 +00:00
|
|
|
k (PurelyFunctional l).
|
2018-02-19 19:02:17 +00:00
|
|
|
Definition AppearedInYearK {R} (l : programming_language) (k : nat -> R) : R :=
|
2018-02-18 18:05:56 +00:00
|
|
|
k (AppearedInYear l).
|
2018-02-19 19:02:17 +00:00
|
|
|
Definition maxK {R} (n1 n2 : nat) (k : nat -> R) : R :=
|
2018-02-18 18:05:56 +00:00
|
|
|
k (max n1 n2).
|
|
|
|
|
|
|
|
Compute mapK NameK languages (fun ls => ls).
|
|
|
|
Compute filterK PurelyFunctionalK languages (fun ls => mapK NameK ls (fun x => x)).
|
2018-02-18 23:19:39 +00:00
|
|
|
Compute mapK AppearedInYearK languages (fun ls => fold_leftK maxK ls 0 (fun x => x)).
|
2018-02-18 18:05:56 +00:00
|
|
|
Compute filterK PurelyFunctionalK languages
|
|
|
|
(fun ls1 => mapK AppearedInYearK ls1
|
2018-02-18 23:19:39 +00:00
|
|
|
(fun ls2 => fold_leftK maxK ls2 0 (fun x => x))).
|
2018-02-18 18:05:56 +00:00
|
|
|
|
2018-02-19 19:02:17 +00:00
|
|
|
Theorem mapK_ok : forall {A B R} (f : A -> (B -> R) -> R) (f_base : A -> B),
|
2018-02-18 18:05:56 +00:00
|
|
|
(forall x k, f x k = k (f_base x))
|
|
|
|
-> forall (ls : list A) (k : list B -> R),
|
|
|
|
mapK f ls k = k (map f_base ls).
|
|
|
|
Proof.
|
|
|
|
induct ls; simplify; try equality.
|
|
|
|
|
|
|
|
rewrite H.
|
|
|
|
apply IHls.
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
Theorem names_ok : forall langs,
|
|
|
|
mapK NameK langs (fun ls => ls) = map Name langs.
|
|
|
|
Proof.
|
|
|
|
simplify.
|
|
|
|
apply mapK_ok with (f_base := Name).
|
|
|
|
unfold NameK.
|
|
|
|
trivial.
|
|
|
|
Qed.
|
|
|
|
|
2018-02-19 19:02:17 +00:00
|
|
|
Theorem filterK_ok : forall {A R} (f : A -> (bool -> R) -> R) (f_base : A -> bool),
|
2018-02-18 18:05:56 +00:00
|
|
|
(forall x k, f x k = k (f_base x))
|
|
|
|
-> forall (ls : list A) (k : list A -> R),
|
|
|
|
filterK f ls k = k (filter f_base ls).
|
|
|
|
Proof.
|
|
|
|
induct ls; simplify; try equality.
|
|
|
|
|
|
|
|
rewrite H.
|
|
|
|
apply IHls.
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
Theorem purenames_ok : forall langs,
|
|
|
|
filterK PurelyFunctionalK langs (fun ls => mapK NameK ls (fun x => x))
|
|
|
|
= map Name (filter PurelyFunctional langs).
|
|
|
|
Proof.
|
|
|
|
simplify.
|
|
|
|
rewrite filterK_ok with (f_base := PurelyFunctional); trivial.
|
|
|
|
apply mapK_ok with (f_base := Name); trivial.
|
|
|
|
Qed.
|
|
|
|
|
2018-02-19 19:02:17 +00:00
|
|
|
Theorem fold_leftK_ok : forall {A B R} (f : B -> A -> (B -> R) -> R) (f_base : B -> A -> B),
|
2018-02-18 18:05:56 +00:00
|
|
|
(forall x acc k, f x acc k = k (f_base x acc))
|
|
|
|
-> forall (ls : list A) (acc : B) (k : B -> R),
|
2018-02-18 23:19:39 +00:00
|
|
|
fold_leftK f ls acc k = k (fold_left f_base ls acc).
|
2018-02-18 18:05:56 +00:00
|
|
|
Proof.
|
|
|
|
induct ls; simplify; try equality.
|
|
|
|
|
|
|
|
rewrite H.
|
|
|
|
apply IHls.
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
Theorem latest_ok : forall langs,
|
2018-02-18 23:19:39 +00:00
|
|
|
mapK AppearedInYearK langs (fun ls => fold_leftK maxK ls 0 (fun x => x))
|
|
|
|
= fold_left max (map AppearedInYear langs) 0.
|
2018-02-18 18:05:56 +00:00
|
|
|
Proof.
|
|
|
|
simplify.
|
|
|
|
rewrite mapK_ok with (f_base := AppearedInYear); trivial.
|
2018-02-18 23:19:39 +00:00
|
|
|
apply fold_leftK_ok with (f_base := max); trivial.
|
2018-02-18 18:05:56 +00:00
|
|
|
Qed.
|
|
|
|
|
|
|
|
Theorem latestpure_ok : forall langs,
|
|
|
|
filterK PurelyFunctionalK langs
|
|
|
|
(fun ls1 => mapK AppearedInYearK ls1
|
2018-02-18 23:19:39 +00:00
|
|
|
(fun ls2 => fold_leftK maxK ls2 0 (fun x => x)))
|
|
|
|
= fold_left max (map AppearedInYear (filter PurelyFunctional langs)) 0.
|
2018-02-18 18:05:56 +00:00
|
|
|
Proof.
|
|
|
|
simplify.
|
|
|
|
rewrite filterK_ok with (f_base := PurelyFunctional); trivial.
|
|
|
|
rewrite mapK_ok with (f_base := AppearedInYear); trivial.
|
2018-02-18 23:19:39 +00:00
|
|
|
apply fold_leftK_ok with (f_base := max); trivial.
|
2018-02-18 18:05:56 +00:00
|
|
|
Qed.
|
2018-02-19 01:15:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
(** * Tree traversals *)
|
|
|
|
|
|
|
|
Inductive tree {A} :=
|
|
|
|
| Leaf
|
|
|
|
| Node (l : tree) (d : A) (r : tree).
|
|
|
|
Arguments tree : clear implicits.
|
|
|
|
|
2018-02-19 19:02:17 +00:00
|
|
|
Fixpoint size {A} (t : tree A) : nat :=
|
2018-02-19 01:15:10 +00:00
|
|
|
match t with
|
|
|
|
| Leaf => 0
|
2018-02-19 19:02:17 +00:00
|
|
|
| Node l _ r => 2 + size l + size r
|
2018-02-19 01:15:10 +00:00
|
|
|
end.
|
|
|
|
|
|
|
|
Fixpoint flatten {A} (t : tree A) : list A :=
|
|
|
|
match t with
|
|
|
|
| Leaf => []
|
|
|
|
| Node l d r => flatten l ++ d :: flatten r
|
|
|
|
end.
|
|
|
|
|
|
|
|
Fixpoint flattenAcc {A} (t : tree A) (acc : list A) : list A :=
|
|
|
|
match t with
|
|
|
|
| Leaf => acc
|
|
|
|
| Node l d r => flattenAcc l (d :: flattenAcc r acc)
|
|
|
|
end.
|
|
|
|
|
|
|
|
Theorem flattenAcc_ok : forall {A} (t : tree A) acc,
|
|
|
|
flattenAcc t acc = flatten t ++ acc.
|
|
|
|
Proof.
|
|
|
|
induct t; simplify; try equality.
|
|
|
|
|
|
|
|
rewrite IHt1, IHt2.
|
|
|
|
rewrite <- app_assoc.
|
|
|
|
simplify.
|
|
|
|
equality.
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
Fixpoint flattenK {A R} (t : tree A) (acc : list A) (k : list A -> R) : R :=
|
|
|
|
match t with
|
|
|
|
| Leaf => k acc
|
|
|
|
| Node l d r => flattenK r acc (fun acc' =>
|
|
|
|
flattenK l (d :: acc') k)
|
|
|
|
end.
|
|
|
|
|
|
|
|
Theorem flattenK_ok : forall {A R} (t : tree A) acc (k : list A -> R),
|
|
|
|
flattenK t acc k = k (flattenAcc t acc).
|
|
|
|
Proof.
|
|
|
|
induct t; simplify; try equality.
|
|
|
|
|
|
|
|
rewrite IHt2, IHt1.
|
|
|
|
equality.
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
Inductive flatten_continuation {A} :=
|
|
|
|
| KDone
|
|
|
|
| KMore (l : tree A) (d : A) (k : flatten_continuation).
|
|
|
|
Arguments flatten_continuation : clear implicits.
|
|
|
|
|
|
|
|
Definition apply_continuation {A} (acc : list A) (k : flatten_continuation A)
|
|
|
|
(flattenKD : tree A -> list A -> flatten_continuation A -> list A)
|
|
|
|
: list A :=
|
|
|
|
match k with
|
|
|
|
| KDone => acc
|
|
|
|
| KMore l d k' => flattenKD l (d :: acc) k'
|
|
|
|
end.
|
|
|
|
|
|
|
|
Fixpoint flattenKD {A} (fuel : nat) (t : tree A) (acc : list A)
|
|
|
|
(k : flatten_continuation A) : list A :=
|
|
|
|
match fuel with
|
|
|
|
| O => []
|
|
|
|
| S fuel' =>
|
|
|
|
match t with
|
|
|
|
| Leaf => apply_continuation acc k (flattenKD fuel')
|
|
|
|
| Node l d r => flattenKD fuel' r acc (KMore l d k)
|
|
|
|
end
|
|
|
|
end.
|
|
|
|
|
2018-02-19 19:02:17 +00:00
|
|
|
Fixpoint continuation_size {A} (k : flatten_continuation A) : nat :=
|
2018-02-19 01:15:10 +00:00
|
|
|
match k with
|
|
|
|
| KDone => 0
|
2018-02-19 19:02:17 +00:00
|
|
|
| KMore l d k' => 1 + size l + continuation_size k'
|
2018-02-19 01:15:10 +00:00
|
|
|
end.
|
|
|
|
|
|
|
|
Fixpoint flatten_cont {A} (k : flatten_continuation A) : list A :=
|
|
|
|
match k with
|
|
|
|
| KDone => []
|
|
|
|
| KMore l d k' => flatten_cont k' ++ flatten l ++ [d]
|
|
|
|
end.
|
|
|
|
|
|
|
|
Lemma flattenKD_ok' : forall {A} fuel fuel' (t : tree A) acc k,
|
2018-02-19 19:02:17 +00:00
|
|
|
size t + continuation_size k < fuel' < fuel
|
2018-02-19 01:15:10 +00:00
|
|
|
-> flattenKD fuel' t acc k
|
|
|
|
= flatten_cont k ++ flatten t ++ acc.
|
|
|
|
Proof.
|
|
|
|
induct fuel; simplify; cases fuel'; simplify; try linear_arithmetic.
|
|
|
|
|
|
|
|
cases t; simplify; trivial.
|
|
|
|
|
|
|
|
cases k; simplify; trivial.
|
|
|
|
rewrite IHfuel; try linear_arithmetic.
|
|
|
|
repeat rewrite <- app_assoc.
|
|
|
|
simplify.
|
|
|
|
equality.
|
|
|
|
|
|
|
|
rewrite IHfuel.
|
|
|
|
simplify.
|
|
|
|
repeat rewrite <- app_assoc.
|
|
|
|
simplify.
|
|
|
|
equality.
|
|
|
|
simplify.
|
|
|
|
linear_arithmetic.
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
Theorem flattenKD_ok : forall {A} (t : tree A),
|
2018-02-19 19:02:17 +00:00
|
|
|
flattenKD (size t + 1) t [] KDone = flatten t.
|
2018-02-19 01:15:10 +00:00
|
|
|
Proof.
|
|
|
|
simplify.
|
2018-02-19 19:02:17 +00:00
|
|
|
rewrite flattenKD_ok' with (fuel := size t + 2).
|
2018-02-19 01:15:10 +00:00
|
|
|
simplify.
|
|
|
|
apply app_nil_r.
|
|
|
|
simplify.
|
|
|
|
linear_arithmetic.
|
|
|
|
Qed.
|
2018-02-19 19:02:17 +00:00
|
|
|
|
|
|
|
Definition call_stack A := list (tree A * A).
|
|
|
|
|
|
|
|
Definition pop_call_stack {A} (acc : list A) (st : call_stack A)
|
|
|
|
(flattenS : tree A -> list A -> call_stack A -> list A)
|
|
|
|
: list A :=
|
|
|
|
match st with
|
|
|
|
| [] => acc
|
|
|
|
| (l, d) :: st' => flattenS l (d :: acc) st'
|
|
|
|
end.
|
|
|
|
|
|
|
|
Fixpoint flattenS {A} (fuel : nat) (t : tree A) (acc : list A)
|
|
|
|
(st : call_stack A) : list A :=
|
|
|
|
match fuel with
|
|
|
|
| O => []
|
|
|
|
| S fuel' =>
|
|
|
|
match t with
|
|
|
|
| Leaf => pop_call_stack acc st (flattenS fuel')
|
|
|
|
| Node l d r => flattenS fuel' r acc ((l, d) :: st)
|
|
|
|
end
|
|
|
|
end.
|
|
|
|
|
|
|
|
Fixpoint call_stack_to_continuation {A} (st : call_stack A) : flatten_continuation A :=
|
|
|
|
match st with
|
|
|
|
| [] => KDone
|
|
|
|
| (l, d) :: st' => KMore l d (call_stack_to_continuation st')
|
|
|
|
end.
|
|
|
|
|
|
|
|
Lemma flattenS_flattenKD : forall {A} fuel (t : tree A) acc st,
|
|
|
|
flattenS fuel t acc st = flattenKD fuel t acc (call_stack_to_continuation st).
|
|
|
|
Proof.
|
|
|
|
induct fuel; simplify; trivial.
|
|
|
|
|
|
|
|
cases t.
|
|
|
|
|
|
|
|
cases st; simplify; trivial.
|
|
|
|
cases p; simplify.
|
|
|
|
apply IHfuel.
|
|
|
|
|
|
|
|
apply IHfuel.
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
Theorem flattenS_ok : forall {A} (t : tree A),
|
|
|
|
flattenS (size t + 1) t [] [] = flatten t.
|
|
|
|
Proof.
|
|
|
|
simplify.
|
|
|
|
rewrite flattenS_flattenKD.
|
|
|
|
apply flattenKD_ok.
|
|
|
|
Qed.
|
2018-02-19 19:13:57 +00:00
|
|
|
|
|
|
|
|
|
|
|
(** * Proof of our motivating example *)
|
|
|
|
|
|
|
|
Theorem allSublistsK_ok : forall {A B} (ls : list A) (failed : unit -> B) found,
|
|
|
|
(forall sol, (exists ans, (forall failed', found sol failed' = ans)
|
|
|
|
/\ ans <> failed tt)
|
|
|
|
\/ (forall failed', found sol failed' = failed' tt))
|
|
|
|
-> (exists sol ans, In sol (allSublists ls)
|
|
|
|
/\ (forall failed', found sol failed' = ans)
|
|
|
|
/\ allSublistsK ls failed found = ans
|
|
|
|
/\ ans <> failed tt)
|
|
|
|
\/ ((forall sol, In sol (allSublists ls)
|
|
|
|
-> forall failed', found sol failed' = failed' tt)
|
|
|
|
/\ allSublistsK ls failed found = failed tt).
|
|
|
|
Proof.
|
|
|
|
induct ls; simplify.
|
|
|
|
|
|
|
|
specialize (H []).
|
|
|
|
first_order.
|
|
|
|
right.
|
|
|
|
propositional.
|
|
|
|
subst.
|
|
|
|
trivial.
|
|
|
|
trivial.
|
|
|
|
|
|
|
|
assert (let found := (fun (sol : list A) (failed' : unit -> B) =>
|
|
|
|
found sol (fun _ : unit => found (a :: sol) failed')) in
|
|
|
|
(exists (sol : list A) (ans : B),
|
|
|
|
In sol (allSublists ls) /\
|
|
|
|
(forall failed' : unit -> B, found sol failed' = ans) /\
|
|
|
|
allSublistsK ls failed found = ans /\ ans <> failed tt) \/
|
|
|
|
(forall sol : list A,
|
|
|
|
In sol (allSublists ls) -> forall failed' : unit -> B, found sol failed' = failed' tt) /\
|
|
|
|
allSublistsK ls failed found = failed tt).
|
|
|
|
apply IHls.
|
|
|
|
first_order.
|
|
|
|
generalize (H sol).
|
|
|
|
first_order.
|
|
|
|
specialize (H (a :: sol)).
|
|
|
|
first_order.
|
|
|
|
left.
|
|
|
|
exists x; propositional.
|
|
|
|
rewrite H0.
|
|
|
|
trivial.
|
|
|
|
right.
|
|
|
|
simplify.
|
|
|
|
rewrite H0.
|
|
|
|
trivial.
|
|
|
|
|
|
|
|
clear IHls.
|
|
|
|
simplify.
|
|
|
|
first_order.
|
|
|
|
|
|
|
|
generalize (H x); first_order.
|
|
|
|
left; exists x, x1; propositional.
|
|
|
|
apply in_or_app; propositional.
|
|
|
|
specialize (H1 failed).
|
|
|
|
specialize (H4 (fun _ => found (a :: x) failed)).
|
|
|
|
equality.
|
|
|
|
left; exists (a :: x), x0; propositional.
|
|
|
|
apply in_or_app; right; apply in_map_iff.
|
|
|
|
first_order.
|
|
|
|
specialize (H1 failed').
|
|
|
|
rewrite H4 in H1.
|
|
|
|
trivial.
|
|
|
|
|
|
|
|
right; propositional.
|
|
|
|
apply in_app_or in H2; propositional.
|
|
|
|
|
|
|
|
generalize (H sol); first_order.
|
|
|
|
apply H0 with (failed' := failed') in H3.
|
|
|
|
rewrite H2 in H3.
|
|
|
|
equality.
|
|
|
|
|
|
|
|
apply in_map_iff in H3.
|
|
|
|
first_order.
|
|
|
|
subst.
|
|
|
|
generalize (H x); first_order.
|
|
|
|
apply H0 with (failed' := failed) in H3.
|
|
|
|
equality.
|
|
|
|
apply H0 with (failed' := failed') in H3.
|
|
|
|
rewrite H2 in H3; trivial.
|
|
|
|
Qed.
|
|
|
|
|
|
|
|
Theorem sublistSummingToK_ok : forall ns target,
|
|
|
|
match sublistSummingToK ns target with
|
|
|
|
| None => forall sol, In sol (allSublists ns) -> sum sol <> target
|
|
|
|
| Some sol => In sol (allSublists ns) /\ sum sol = target
|
|
|
|
end.
|
|
|
|
Proof.
|
|
|
|
simplify.
|
|
|
|
unfold sublistSummingToK.
|
|
|
|
pose proof (allSublistsK_ok ns (fun _ => None)
|
|
|
|
(fun sol failed => if sum sol ==n target then Some sol else failed tt)).
|
|
|
|
cases H.
|
|
|
|
|
|
|
|
simplify.
|
|
|
|
cases (sum sol ==n target).
|
|
|
|
left; exists (Some sol); equality.
|
|
|
|
propositional.
|
|
|
|
|
|
|
|
first_order.
|
|
|
|
specialize (H0 (fun _ => None)).
|
|
|
|
cases (sum x ==n target); try equality.
|
|
|
|
subst.
|
|
|
|
rewrite H1.
|
|
|
|
propositional.
|
|
|
|
|
|
|
|
first_order.
|
|
|
|
rewrite H0.
|
|
|
|
simplify.
|
|
|
|
apply H with (failed' := fun _ => None) in H1.
|
|
|
|
cases (sum sol ==n target); equality.
|
|
|
|
Qed.
|