frap/DataAbstraction.v

1755 lines
50 KiB
Coq
Raw Normal View History

(** Formal Reasoning About Programs <http://adam.chlipala.net/frap/>
* Chapter 3: Data Abstraction
* Author: Adam Chlipala
* License: https://creativecommons.org/licenses/by-nc-nd/4.0/ *)
Require Import Frap.
Set Implicit Arguments.
2017-02-20 20:24:51 +00:00
(* Perhaps the essence of effective programming is division of large tasks into
* smaller ones, and *data abstraction* is a key technique to that end.
* We provide a clear separation between *interfaces* and *implementations*.
* The author of a library can take responsibility for making it implement an
* interface faithfully, *encapsulating* private state and other implementation
* details in a way that client code can't observe. Then that client code can
* mix and match implementations of some well-specified functionality.
*
* As part of our quick tour through effective Coq programming, we will dwell on
* patterns for data abstraction, including how to state it formally, from the
* perspectives of both libraries and client code. *)
(** * Specification styles for data abstraction *)
(* One of the classic formalisms for data abstraction is the *algebraic* style,
* where requirements on implementations are written out as quantified
* equalities. Any implementation must satisfy these equalities, but we grant
* implementations freedom in internal details. *)
Module Algebraic.
2017-02-20 20:24:51 +00:00
(* Here's an example of an algebraic interface or *specification* ("spec" for
2018-02-26 00:40:10 +00:00
* short). It's for purely functional queues, which follow first-in-first-out
2017-02-20 20:24:51 +00:00
* disciplines. *)
Module Type QUEUE.
Parameter t : Set -> Set.
2017-02-20 20:24:51 +00:00
(* An implementation must include some data type [t].
* Actually, it's more of a *type family*, e.g. like [list] and some other
* polymorphic container types we looked at last time. *)
Parameter empty : forall A, t A.
2017-02-20 20:24:51 +00:00
(* For any type [A] of data, we can build a queue for that element type. *)
Parameter enqueue : forall A, t A -> A -> t A.
2017-02-20 20:24:51 +00:00
(* Enqueue a new element, in the functional style, where we build a new
* queue instead of modifying the original. *)
Parameter dequeue : forall A, t A -> option (t A * A).
2017-02-20 20:24:51 +00:00
(* Given a queue, either return [None] if it is empty or [Some (q', v)] for
* the result of dequeuing one element, where [q'] is the new queue (now
* one element shorter) and [v] is the element we dequeue. *)
2017-02-20 20:24:51 +00:00
(* Which algebraic properties characterize correct queues? *)
(* First, [dequeue] returns [None] exactly on empty queues. *)
Axiom dequeue_empty : forall A,
dequeue (empty A) = None.
Axiom empty_dequeue : forall A (q : t A),
dequeue q = None -> q = empty A.
2017-02-20 20:24:51 +00:00
(* Second, [dequeue] forms a kind of inverse for [enqueue]. *)
Axiom dequeue_enqueue : forall A (q : t A) x,
dequeue (enqueue q x) = Some (match dequeue q with
| None => (empty A, x)
| Some (q', y) => (enqueue q' x, y)
end).
2017-02-20 20:24:51 +00:00
(* These properties turn out to be enough to prove interesting properties
* about client code that uses queues. Before we get there, we should
* define some concrete queue implementations. (If we don't give an
* implementation, we often realize that the spec is *unrealizable*!) *)
End QUEUE.
2017-02-20 20:24:51 +00:00
(* First, there is a fairly straightforward implementation with lists. *)
Module ListQueue : QUEUE.
Definition t : Set -> Set := list.
2017-02-20 20:24:51 +00:00
(* Note that we use identifier [list] alone as a first-class type family,
* without specifying a parameter explicitly. *)
2017-02-20 20:24:51 +00:00
(* We follow the convention of enqueuing onto the front of lists and
* dequeuing from the back, so the first two operations are just the first
* two constructors of [list]. *)
Definition empty A : t A := nil.
Definition enqueue A (q : t A) (x : A) : t A := x :: q.
2017-02-20 20:24:51 +00:00
(* [dequeue] is a little more work: we use recursion to step down to the
* last element of a list. *)
Fixpoint dequeue A (q : t A) : option (t A * A) :=
match q with
| [] => None
| x :: q' =>
match dequeue q' with
| None => Some ([], x)
| Some (q'', y) => Some (x :: q'', y)
end
end.
2017-02-20 20:24:51 +00:00
(* Applying our experience so far with Coq proofs, the algebraic laws are
* unremarkable to establish. *)
Theorem dequeue_empty : forall A, dequeue (empty A) = None.
Proof.
simplify.
equality.
Qed.
Theorem empty_dequeue : forall A (q : t A),
dequeue q = None -> q = empty A.
Proof.
simplify.
cases q.
simplify.
equality.
simplify.
cases (dequeue q).
cases p.
equality.
equality.
Qed.
Theorem dequeue_enqueue : forall A (q : t A) x,
dequeue (enqueue q x) = Some (match dequeue q with
| None => (empty A, x)
| Some (q', y) => (enqueue q' x, y)
end).
Proof.
simplify.
cases (dequeue q).
cases p.
equality.
equality.
Qed.
End ListQueue.
2017-02-20 20:24:51 +00:00
(* There are software-engineering benefits to interface-implementation
* separation even when one only bothers to build a single implementation.
* However, often there are naive and clever optimized versions of a single
* interface. Queues are no exception. Before we get to a truly clever
* version, we'll demonstrate with a less obviously better version:
* enqueuing at the back and dequeuing from the front. *)
Module ReversedListQueue : QUEUE.
Definition t : Set -> Set := list.
2017-02-20 20:24:51 +00:00
(* Still the same internal queue type, but note that Coq's type system
* prevents client code from knowing that fact! [t] appears *opaque*
* or *abstract* from the outside, as we'll see shortly. *)
2017-02-20 20:24:51 +00:00
(* The first two operations are similar, but now we enqueue at the
* list end. *)
Definition empty A : t A := [].
Definition enqueue A (q : t A) (x : A) : t A := q ++ [x].
2017-02-20 20:24:51 +00:00
2020-02-23 21:20:39 +00:00
(* [dequeue] is now constant-time, with no recursion and just a single
2017-02-20 20:24:51 +00:00
* pattern match. *)
Definition dequeue A (q : t A) : option (t A * A) :=
match q with
| [] => None
| x :: q' => Some (q', x)
end.
2017-02-20 20:24:51 +00:00
(* The proofs of the laws are still boring. *)
Theorem dequeue_empty : forall A, dequeue (empty A) = None.
Proof.
simplify.
equality.
Qed.
Theorem empty_dequeue : forall A (q : t A),
dequeue q = None -> q = empty A.
Proof.
simplify.
cases q.
simplify.
equality.
simplify.
equality.
Qed.
Theorem dequeue_enqueue : forall A (q : t A) x,
dequeue (enqueue q x) = Some (match dequeue q with
| None => (empty A, x)
| Some (q', y) => (enqueue q' x, y)
end).
Proof.
simplify.
unfold dequeue, enqueue.
cases q; simplify.
equality.
equality.
Qed.
End ReversedListQueue.
2017-02-20 20:24:51 +00:00
(* Let's take a look at some client code that is agnostic to queue
* implementation details. We have been using Coq's *module system*, inspired
* by those of the ML programming languages, to encode interfaces and
* implementations. Coq also adopts from ML the idea of *functors*, or
* functions from modules to modules. *)
Module DelayedSum (Q : QUEUE).
2017-02-20 20:24:51 +00:00
(* The code in this scope may refer to some unknown implementation [Q] of
* the [QUEUE] interface. *)
(* We will only use a simple example here: enqueue the first [n] natural
* numbers and then successively dequeue them, computing the sum as we
* go. *)
(* First, the function to enqueue the first [n] natural numbers. *)
Fixpoint makeQueue (n : nat) (q : Q.t nat) : Q.t nat :=
match n with
| 0 => q
| S n' => makeQueue n' (Q.enqueue q n')
end.
2017-02-20 20:24:51 +00:00
(* Next, the function to dequeue repeatedly, keeping a sum. *)
Fixpoint computeSum (n : nat) (q : Q.t nat) : nat :=
match n with
| 0 => 0
| S n' => match Q.dequeue q with
| None => 0
| Some (q', v) => v + computeSum n' q'
end
end.
2017-02-20 20:24:51 +00:00
(* This function gives the expected answer, in a simpler form, of
* [computeSum] after [makeQueue]. *)
Fixpoint sumUpto (n : nat) : nat :=
match n with
| 0 => 0
| S n' => n' + sumUpto n'
end.
2017-02-20 20:24:51 +00:00
(* A crucial lemma: what results from dequeuing out of a [makeQueue]
* call? The answer depends on whether the initial queue [q] has anything
* to dequeue. *)
Lemma dequeue_makeQueue : forall n q,
Q.dequeue (makeQueue n q)
= match Q.dequeue q with
2017-02-20 20:24:51 +00:00
| Some (q', v) =>
(* The queue we started with had content. We dequeue from it. *)
Some (makeQueue n q', v)
| None =>
2017-02-20 20:24:51 +00:00
(* No content in initial queue. We get [n-1] (unless [n = 0]). *)
match n with
| 0 => None
| S n' => Some (makeQueue n' q, n')
end
end.
Proof.
induct n.
simplify.
cases (Q.dequeue q).
cases p.
equality.
equality.
simplify.
rewrite IHn.
rewrite Q.dequeue_enqueue.
2017-02-20 20:24:51 +00:00
(* ^-- Crucial step! First use of a law from the interface. *)
cases (Q.dequeue q).
cases p.
equality.
rewrite (Q.empty_dequeue (q := q)).
2017-02-20 20:24:51 +00:00
(* ^-- Another law! *)
equality.
assumption.
Qed.
2017-02-20 20:24:51 +00:00
(* Now we can tackle the final property directly by induction. *)
Theorem computeSum_ok : forall n,
computeSum n (makeQueue n (Q.empty nat)) = sumUpto n.
Proof.
induct n.
simplify.
equality.
simplify.
rewrite dequeue_makeQueue.
rewrite Q.dequeue_enqueue.
rewrite Q.dequeue_empty.
rewrite IHn.
equality.
Qed.
End DelayedSum.
End Algebraic.
2017-02-20 20:24:51 +00:00
(* There is a famous implementation of queues with two stacks, achieving
* amortized constant time for all operations, in contrast to the worst-case
* quadratic time of both queue implementations we just saw. However, to
* justify this fancy implementation, we will need to choose a more permissive
* interface, based on the idea of parameterizing over an arbitrary *equivalence
* relation* between queues, which need not be simple equality. *)
Module AlgebraicWithEquivalenceRelation.
Module Type QUEUE.
2017-02-20 20:24:51 +00:00
(* We still have a type family of queues, plus the same three operations. *)
Parameter t : Set -> Set.
2017-02-20 20:24:51 +00:00
Parameter empty : forall A, t A.
Parameter enqueue : forall A, t A -> A -> t A.
Parameter dequeue : forall A, t A -> option (t A * A).
2017-02-20 20:24:51 +00:00
(* What's new? This equivalence relation. The type [Prop] stands for
* logical truth values, so a function returning it can be seen as a
* relation in the usual mathematical sense. This is a *binary* relation,
* in particular, since it takes two arguments. *)
Parameter equiv : forall A, t A -> t A -> Prop.
2017-02-20 20:24:51 +00:00
(* Let's declare convenient syntax for the relation. *)
Infix "~=" := equiv (at level 70).
2017-02-20 20:24:51 +00:00
(* It really is an equivalence relation, as formalized by the usual three
* laws. *)
Axiom equiv_refl : forall A (a : t A), a ~= a.
Axiom equiv_sym : forall A (a b : t A), a ~= b -> b ~= a.
Axiom equiv_trans : forall A (a b c : t A), a ~= b -> b ~= c -> a ~= c.
2017-02-20 20:24:51 +00:00
(* It must be the case that enqueuing elements preserves the relation. *)
Axiom equiv_enqueue : forall A (a b : t A) (x : A),
a ~= b
-> enqueue a x ~= enqueue b x.
2017-02-20 20:24:51 +00:00
(* We define a derived relation for results of [dequeue]: either both
* [dequeue]s failed to return anything, or both returned the same data
* value along with new queues that are themselves related. *)
Definition dequeue_equiv A (a b : option (t A * A)) :=
match a, b with
| None, None => True
| Some (qa, xa), Some (qb, xb) => qa ~= qb /\ xa = xb
| _, _ => False
end.
Infix "~~=" := dequeue_equiv (at level 70).
Axiom equiv_dequeue : forall A (a b : t A),
a ~= b
-> dequeue a ~~= dequeue b.
2017-02-20 20:24:51 +00:00
(* We retain the three axioms from the prior interface, using our fancy
* relation instead of equality on queues. *)
Axiom dequeue_empty : forall A,
dequeue (empty A) = None.
Axiom empty_dequeue : forall A (q : t A),
dequeue q = None -> q ~= empty A.
Axiom dequeue_enqueue : forall A (q : t A) x,
dequeue (enqueue q x)
~~= match dequeue q with
| None => Some (empty A, x)
| Some (q', y) => Some (enqueue q' x, y)
end.
End QUEUE.
2017-02-20 20:24:51 +00:00
(* It's easy to redo [ListQueue], specifying normal equality for the
* equivalence relation. *)
Module ListQueue : QUEUE.
Definition t : Set -> Set := list.
Definition empty A : t A := nil.
Definition enqueue A (q : t A) (x : A) : t A := x :: q.
Fixpoint dequeue A (q : t A) : option (t A * A) :=
match q with
| [] => None
| x :: q' =>
match dequeue q' with
| None => Some ([], x)
| Some (q'', y) => Some (x :: q'', y)
end
end.
Definition equiv A (a b : t A) := a = b.
Infix "~=" := equiv (at level 70).
Theorem equiv_refl : forall A (a : t A), a ~= a.
Proof.
equality.
Qed.
Theorem equiv_sym : forall A (a b : t A), a ~= b -> b ~= a.
Proof.
equality.
Qed.
Theorem equiv_trans : forall A (a b c : t A), a ~= b -> b ~= c -> a ~= c.
Proof.
equality.
Qed.
Theorem equiv_enqueue : forall A (a b : t A) (x : A),
a ~= b
-> enqueue a x ~= enqueue b x.
Proof.
unfold equiv; equality.
Qed.
Definition dequeue_equiv A (a b : option (t A * A)) :=
match a, b with
| None, None => True
| Some (qa, xa), Some (qb, xb) => qa ~= qb /\ xa = xb
| _, _ => False
end.
Infix "~~=" := dequeue_equiv (at level 70).
Theorem equiv_dequeue : forall A (a b : t A),
a ~= b
-> dequeue a ~~= dequeue b.
Proof.
unfold equiv, dequeue_equiv; simplify.
rewrite H.
cases (dequeue b).
cases p.
equality.
propositional.
Qed.
Theorem dequeue_empty : forall A, dequeue (empty A) = None.
Proof.
simplify.
equality.
Qed.
Theorem empty_dequeue : forall A (q : t A),
dequeue q = None -> q ~= empty A.
Proof.
simplify.
cases q.
simplify.
unfold equiv.
equality.
simplify.
cases (dequeue q).
cases p.
equality.
equality.
Qed.
Theorem dequeue_enqueue : forall A (q : t A) x,
dequeue (enqueue q x)
~~= match dequeue q with
| None => Some (empty A, x)
| Some (q', y) => Some (enqueue q' x, y)
end.
Proof.
unfold dequeue_equiv, equiv.
induct q; simplify.
equality.
cases (dequeue q).
cases p.
equality.
equality.
Qed.
End ListQueue.
2017-02-20 20:24:51 +00:00
(* However, now we can implement the classic two-stacks optimized queue! *)
Module TwoStacksQueue : QUEUE.
2017-02-20 20:24:51 +00:00
(* Every queue is a pair of stacks: one for enqueuing and one for
* dequeuing. *)
Record stackpair (A : Set) := {
EnqueueHere : list A;
2017-02-20 20:24:51 +00:00
(* This stack has more recently enqueued elements closer to the front,
* making enqueuing constant-time. *)
DequeueHere : list A
2017-02-20 20:24:51 +00:00
(* This stack has least recently enqueued elements closer to the front,
* making dequeuing constant-time. *)
}.
2017-02-20 20:24:51 +00:00
(* What's the catch? Sometimes we need to reverse [EnqueueHere] and
* transfer it to [DequeueHere], or otherwise there would never be anything
* to dequeue! Luckily, the work we do in transfering is bounded
* asymptotically by the total number of enqueue/dequeue operations, so
* we get *amortized* constant time. *)
(* By the way, the [Record] feature we used above is similar to e.g. structs
* in C. *)
Definition t := stackpair.
Definition empty A : t A := {|
EnqueueHere := [];
DequeueHere := []
|}.
Definition enqueue A (q : t A) (x : A) : t A := {|
EnqueueHere := x :: q.(EnqueueHere);
DequeueHere := q.(DequeueHere)
|}.
Definition dequeue A (q : t A) : option (t A * A) :=
match q.(DequeueHere) with
| x :: dq => Some ({| EnqueueHere := q.(EnqueueHere);
DequeueHere := dq |}, x)
| [] =>
2017-02-20 20:24:51 +00:00
(* Out of dequeuable elements. Reverse enqueued elements
* and transfer to the other stack. *)
match rev q.(EnqueueHere) with
| [] => None
| x :: eq => Some ({| EnqueueHere := [];
DequeueHere := eq |}, x)
end
end.
2017-02-20 20:24:51 +00:00
(* This function explains which simple queue representation we have in mind,
* for each fancy two-stack representation. *)
Definition elements A (q : t A) : list A :=
q.(EnqueueHere) ++ rev q.(DequeueHere).
2017-02-20 20:24:51 +00:00
(* That function is useful to define our equivalence relation. *)
Definition equiv A (a b : t A) :=
elements a = elements b.
Infix "~=" := equiv (at level 70).
Theorem equiv_refl : forall A (a : t A), a ~= a.
Proof.
equality.
Qed.
Theorem equiv_sym : forall A (a b : t A), a ~= b -> b ~= a.
Proof.
equality.
Qed.
Theorem equiv_trans : forall A (a b c : t A), a ~= b -> b ~= c -> a ~= c.
Proof.
equality.
Qed.
2017-02-20 20:24:51 +00:00
(* Now it is mostly routine to prove the laws, though a few tricks may
* be worth reading through. *)
Theorem equiv_enqueue : forall A (a b : t A) (x : A),
a ~= b
-> enqueue a x ~= enqueue b x.
Proof.
unfold equiv, elements; simplify.
equality.
Qed.
Definition dequeue_equiv A (a b : option (t A * A)) :=
match a, b with
| None, None => True
| Some (qa, xa), Some (qb, xb) => qa ~= qb /\ xa = xb
| _, _ => False
end.
Infix "~~=" := dequeue_equiv (at level 70).
Theorem equiv_dequeue : forall A (a b : t A),
a ~= b
-> dequeue a ~~= dequeue b.
Proof.
unfold equiv, dequeue_equiv, elements, dequeue; simplify.
cases (DequeueHere a); simplify.
cases (rev (EnqueueHere a)); simplify.
cases (DequeueHere b); simplify.
cases (rev (EnqueueHere b)); simplify.
propositional.
SearchRewrite (_ ++ []).
rewrite app_nil_r in H.
rewrite app_nil_r in H.
equality.
cases (EnqueueHere a); simplify.
cases (EnqueueHere b); simplify.
cases (rev l); simplify.
equality.
equality.
equality.
cases (rev l0); simplify.
equality.
equality.
cases (DequeueHere b); simplify.
cases (rev (EnqueueHere b)); simplify.
rewrite app_nil_r in H.
rewrite app_nil_r in H.
equality.
rewrite app_nil_r in H.
rewrite app_nil_r in H.
equality.
rewrite app_nil_r in H.
rewrite H in Heq0.
SearchRewrite (rev (_ ++ _)).
rewrite rev_app_distr in Heq0.
rewrite rev_app_distr in Heq0.
simplify.
invert Heq0.
unfold equiv, elements.
simplify.
rewrite rev_app_distr.
SearchRewrite (rev (rev _)).
rewrite rev_involutive.
rewrite rev_involutive.
equality.
cases (DequeueHere b); simplify.
cases (rev (EnqueueHere b)); simplify.
rewrite app_nil_r in H.
rewrite <- H in Heq1.
cases (EnqueueHere a); simplify.
cases (rev l); simplify.
equality.
rewrite rev_app_distr in Heq1.
simplify.
equality.
rewrite rev_app_distr in Heq1.
rewrite rev_app_distr in Heq1.
simplify.
equality.
unfold equiv, elements.
simplify.
rewrite app_nil_r in H.
rewrite <- H in Heq1.
2018-02-26 00:40:10 +00:00
rewrite rev_app_distr in Heq1.
rewrite rev_app_distr in Heq1.
simplify.
invert Heq1.
rewrite rev_involutive.
rewrite rev_app_distr.
rewrite rev_involutive.
equality.
unfold equiv, elements.
simplify.
SearchAbout app cons nil.
apply app_inj_tail.
rewrite <- app_assoc.
rewrite <- app_assoc.
assumption.
Qed.
Theorem dequeue_empty : forall A, dequeue (empty A) = None.
Proof.
simplify.
equality.
Qed.
Theorem empty_dequeue : forall A (q : t A),
dequeue q = None -> q ~= empty A.
Proof.
simplify.
cases q.
unfold dequeue in *.
simplify.
cases DequeueHere0.
cases (rev EnqueueHere0).
cases EnqueueHere0.
equality.
simplify.
cases (rev EnqueueHere0); simplify.
equality.
equality.
equality.
equality.
Qed.
Theorem dequeue_enqueue : forall A (q : t A) x,
dequeue (enqueue q x)
~~= match dequeue q with
| None => Some (empty A, x)
| Some (q', y) => Some (enqueue q' x, y)
end.
Proof.
unfold dequeue_equiv, equiv; simplify.
cases q; simplify.
unfold dequeue, enqueue; simplify.
cases DequeueHere0; simplify.
cases (rev EnqueueHere0); simplify.
equality.
unfold elements; simplify.
SearchRewrite (rev (_ ++ _)).
rewrite rev_app_distr.
simplify.
equality.
equality.
Qed.
End TwoStacksQueue.
2017-02-20 20:24:51 +00:00
(* The exercise of the generic delayed sum may be repeated with equivalence
* relations. *)
Module DelayedSum (Q : QUEUE).
Fixpoint makeQueue (n : nat) (q : Q.t nat) : Q.t nat :=
match n with
| 0 => q
| S n' => makeQueue n' (Q.enqueue q n')
end.
Fixpoint computeSum (n : nat) (q : Q.t nat) : nat :=
match n with
| 0 => 0
| S n' => match Q.dequeue q with
| None => 0
| Some (q', v) => v + computeSum n' q'
end
end.
Fixpoint sumUpto (n : nat) : nat :=
match n with
| 0 => 0
| S n' => n' + sumUpto n'
end.
Infix "~=" := Q.equiv (at level 70).
Infix "~~=" := Q.dequeue_equiv (at level 70).
Lemma makeQueue_congruence : forall n a b,
a ~= b
-> makeQueue n a ~= makeQueue n b.
Proof.
induct n; simplify.
assumption.
apply IHn.
apply Q.equiv_enqueue.
assumption.
Qed.
Lemma dequeue_makeQueue : forall n q,
Q.dequeue (makeQueue n q)
~~= match Q.dequeue q with
| Some (q', v) => Some (makeQueue n q', v)
| None =>
match n with
| 0 => None
| S n' => Some (makeQueue n' q, n')
end
end.
Proof.
induct n.
simplify.
cases (Q.dequeue q).
cases p.
unfold Q.dequeue_equiv.
propositional.
apply Q.equiv_refl.
unfold Q.dequeue_equiv.
propositional.
simplify.
unfold Q.dequeue_equiv in *.
specialize (IHn (Q.enqueue q n)).
cases (Q.dequeue (makeQueue n (Q.enqueue q n))).
cases p.
pose proof (Q.dequeue_enqueue q n).
unfold Q.dequeue_equiv in *.
cases (Q.dequeue (Q.enqueue q n)).
cases p.
cases (Q.dequeue q).
cases p.
propositional.
apply Q.equiv_trans with (b := makeQueue n t0).
assumption.
apply makeQueue_congruence.
assumption.
equality.
propositional.
apply Q.equiv_trans with (b := makeQueue n t0).
assumption.
apply makeQueue_congruence.
apply Q.equiv_trans with (b := Q.empty nat).
assumption.
apply Q.equiv_sym.
apply Q.empty_dequeue.
assumption.
equality.
cases (Q.dequeue q).
cases p.
propositional.
propositional.
pose proof (Q.dequeue_enqueue q n).
unfold Q.dequeue_equiv in H.
cases (Q.dequeue (Q.enqueue q n)).
cases p.
propositional.
cases (Q.dequeue q).
cases p.
propositional.
propositional.
Qed.
Theorem computeSum_congruence : forall n a b,
a ~= b
-> computeSum n a = computeSum n b.
Proof.
induct n.
simplify.
equality.
simplify.
pose proof (Q.equiv_dequeue H).
unfold Q.dequeue_equiv in H0.
cases (Q.dequeue a).
cases p.
cases (Q.dequeue b).
cases p.
rewrite IHn with (b := t0).
equality.
equality.
propositional.
cases (Q.dequeue b).
propositional.
equality.
Qed.
Theorem computeSum_ok : forall n,
computeSum n (makeQueue n (Q.empty nat)) = sumUpto n.
Proof.
induct n.
simplify.
equality.
simplify.
pose proof (dequeue_makeQueue n (Q.enqueue (Q.empty nat) n)).
unfold Q.dequeue_equiv in H.
cases (Q.dequeue (makeQueue n (Q.enqueue (Q.empty nat) n))).
cases p.
pose proof (Q.dequeue_enqueue (Q.empty nat) n).
unfold Q.dequeue_equiv in H0.
cases (Q.dequeue (Q.enqueue (Q.empty nat) n)).
cases p.
rewrite Q.dequeue_empty in H0.
propositional.
f_equal.
equality.
rewrite <- IHn.
apply computeSum_congruence.
apply Q.equiv_trans with (b := makeQueue n t0).
assumption.
apply makeQueue_congruence.
assumption.
rewrite Q.dequeue_empty in H0.
propositional.
pose proof (Q.dequeue_enqueue (Q.empty nat) n).
unfold Q.dequeue_equiv in H0.
cases (Q.dequeue (Q.enqueue (Q.empty nat) n)).
cases p.
propositional.
rewrite Q.dequeue_empty in H0.
propositional.
Qed.
End DelayedSum.
End AlgebraicWithEquivalenceRelation.
2017-02-20 20:24:51 +00:00
(* It's worth presenting one final style of data-abstraction formalism: we
* introduce *representation functions* in the interface, to map the internal
* representation to some standard one that is easy to reason about. We don't
* expect "real code" to call the representation function. Instead, it's just a
* proof device to let us write convincing laws. Here's the previous example
* redone in this manner, without comment. *)
Module RepFunction.
Module Type QUEUE.
Parameter t : Set -> Set.
Parameter empty : forall A, t A.
Parameter enqueue : forall A, t A -> A -> t A.
Parameter dequeue : forall A, t A -> option (t A * A).
Parameter rep : forall A, t A -> list A.
Axiom empty_rep : forall A,
rep (empty A) = [].
Axiom enqueue_rep : forall A (q : t A) x,
rep (enqueue q x) = x :: rep q.
Axiom dequeue_empty : forall A (q : t A),
rep q = []
-> dequeue q = None.
Axiom dequeue_nonempty : forall A (q : t A) xs x,
rep q = xs ++ [x]
-> exists q', dequeue q = Some (q', x) /\ rep q' = xs.
End QUEUE.
Module ListQueue : QUEUE.
Definition t : Set -> Set := list.
Definition empty A : t A := nil.
Definition enqueue A (q : t A) (x : A) : t A := x :: q.
Fixpoint dequeue A (q : t A) : option (t A * A) :=
match q with
| [] => None
| x :: q' =>
match dequeue q' with
| None => Some ([], x)
| Some (q'', y) => Some (x :: q'', y)
end
end.
Definition rep A (q : t A) := q.
Theorem empty_rep : forall A,
rep (empty A) = [].
Proof.
equality.
Qed.
Theorem enqueue_rep : forall A (q : t A) x,
rep (enqueue q x) = x :: rep q.
Proof.
equality.
Qed.
Theorem dequeue_empty : forall A (q : t A),
rep q = []
-> dequeue q = None.
Proof.
unfold rep; simplify.
rewrite H.
equality.
Qed.
Theorem dequeue_nonempty : forall A (q : t A) xs x,
rep q = xs ++ [x]
-> exists q', dequeue q = Some (q', x) /\ rep q' = xs.
Proof.
unfold rep; induct q.
simplify.
cases xs; simplify.
equality.
equality.
simplify.
cases xs; simplify.
invert H; simplify.
exists [].
equality.
invert H.
assert (exists q' : t A, dequeue (xs ++ [x]) = Some (q', x) /\ q' = xs).
apply IHq.
equality.
first_order.
rewrite H.
exists (a0 :: x0).
equality.
Qed.
End ListQueue.
Module TwoStacksQueue : QUEUE.
Record stackpair (A : Set) := {
EnqueueHere : list A;
DequeueHere : list A
}.
Definition t := stackpair.
Definition empty A : t A := {|
EnqueueHere := [];
DequeueHere := []
|}.
Definition enqueue A (q : t A) (x : A) : t A := {|
EnqueueHere := x :: q.(EnqueueHere);
DequeueHere := q.(DequeueHere)
|}.
Definition dequeue A (q : t A) : option (t A * A) :=
match q.(DequeueHere) with
| x :: dq => Some ({| EnqueueHere := q.(EnqueueHere);
DequeueHere := dq |}, x)
| [] =>
match rev q.(EnqueueHere) with
| [] => None
| x :: eq => Some ({| EnqueueHere := [];
DequeueHere := eq |}, x)
end
end.
Definition rep A (q : t A) : list A :=
q.(EnqueueHere) ++ rev q.(DequeueHere).
Theorem empty_rep : forall A,
rep (empty A) = [].
Proof.
equality.
Qed.
Theorem enqueue_rep : forall A (q : t A) x,
rep (enqueue q x) = x :: rep q.
Proof.
equality.
Qed.
Theorem dequeue_empty : forall A (q : t A),
rep q = []
-> dequeue q = None.
Proof.
unfold rep, dequeue; simplify.
cases (DequeueHere q); simplify.
rewrite app_nil_r in H.
rewrite H.
simplify.
equality.
cases (EnqueueHere q); simplify.
cases (rev l); simplify.
equality.
equality.
equality.
Qed.
Theorem dequeue_nonempty : forall A (q : t A) xs x,
rep q = xs ++ [x]
-> exists q', dequeue q = Some (q', x) /\ rep q' = xs.
Proof.
unfold rep, dequeue; simplify.
cases (DequeueHere q); simplify.
rewrite app_nil_r in H.
rewrite H.
rewrite rev_app_distr; simplify.
exists {| EnqueueHere := []; DequeueHere := rev xs |}.
simplify.
rewrite rev_involutive.
equality.
exists {| EnqueueHere := EnqueueHere q; DequeueHere := l |}.
simplify.
rewrite app_assoc in H.
apply app_inj_tail in H.
propositional.
rewrite H1.
equality.
Qed.
End TwoStacksQueue.
Module DelayedSum (Q : QUEUE).
Fixpoint makeQueue (n : nat) (q : Q.t nat) : Q.t nat :=
match n with
| 0 => q
| S n' => makeQueue n' (Q.enqueue q n')
end.
Fixpoint computeSum (n : nat) (q : Q.t nat) : nat :=
match n with
| 0 => 0
| S n' => match Q.dequeue q with
| None => 0
| Some (q', v) => v + computeSum n' q'
end
end.
Fixpoint sumUpto (n : nat) : nat :=
match n with
| 0 => 0
| S n' => n' + sumUpto n'
end.
Fixpoint upto (n : nat) : list nat :=
match n with
| 0 => []
| S n' => upto n' ++ [n']
end.
Lemma makeQueue_rep : forall n q,
Q.rep (makeQueue n q) = upto n ++ Q.rep q.
Proof.
induct n.
simplify.
equality.
simplify.
rewrite IHn.
rewrite Q.enqueue_rep.
rewrite <- app_assoc.
simplify.
equality.
Qed.
Lemma computeSum_makeQueue' : forall n q,
Q.rep q = upto n
-> computeSum n q = sumUpto n.
Proof.
induct n.
simplify.
equality.
simplify.
pose proof (Q.dequeue_nonempty _ _ H).
first_order.
rewrite H0.
rewrite IHn.
equality.
assumption.
Qed.
Theorem computeSum_ok : forall n,
computeSum n (makeQueue n (Q.empty nat)) = sumUpto n.
Proof.
simplify.
apply computeSum_makeQueue'.
rewrite makeQueue_rep.
rewrite Q.empty_rep.
apply app_nil_r.
Qed.
End DelayedSum.
End RepFunction.
2017-02-12 20:54:34 +00:00
2017-02-20 20:24:51 +00:00
(** * Data abstraction with fixed parameter types *)
(* Finite sets are another classic *abstract data type*, another name for what
* we have been defining so far with modules. Here's a generic finite-set
* interface, following the first algebraic style we saw above. *)
2017-02-12 20:54:34 +00:00
Module Type FINITE_SET.
2017-02-20 20:24:51 +00:00
Parameter key : Set. (* What type of data may be added to these sets? *)
Parameter t : Set. (* What is the type of sets themselves? *)
2017-02-12 20:54:34 +00:00
Parameter empty : t.
2017-02-12 21:27:57 +00:00
Parameter add : t -> key -> t.
2017-02-12 20:54:34 +00:00
Parameter member : t -> key -> bool.
Axiom member_empty : forall k, member empty k = false.
2017-02-12 21:27:57 +00:00
Axiom member_add_eq : forall k s,
member (add s k) k = true.
Axiom member_add_noteq : forall k1 k2 s,
2017-02-12 20:54:34 +00:00
k1 <> k2
2017-02-12 21:27:57 +00:00
-> member (add s k1) k2 = member s k2.
Axiom decidable_equality : forall a b : key, a = b \/ a <> b.
2017-02-20 20:24:51 +00:00
(* This last axiom may be a bit surprising. Coq is so oriented toward
* computation that we don't assume the *law of the excluded middle*, which
* says that every proposition is either true or false. Instead, we prove
* specific instances as needed. But feel free to ignore this point for
* the purposes of this class. *)
2017-02-12 20:54:34 +00:00
End FINITE_SET.
2017-02-20 20:24:51 +00:00
(* We want a generic implementation of finite sets, as found in the standard
* libaries of languages like Java. However, not just any key set will do.
* We need enough computable operations. One sufficient operation is an
* equality test. *)
2017-02-12 20:54:34 +00:00
Module Type SET_WITH_EQUALITY.
Parameter t : Set.
Parameter equal : t -> t -> bool.
Axiom equal_ok : forall a b, if equal a b then a = b else a <> b.
End SET_WITH_EQUALITY.
2017-02-20 20:24:51 +00:00
(* Here's a generic implementation of finite sets, parameterized over an
* arbitrary set with a correct equality operation. Note the use of the [with]
* operator to *refine* the result signature [FINITE_SET]: we reveal that the
2018-02-26 00:40:10 +00:00
* [key] type is actually [SE.T], that is, the key type from the parameter module
2017-02-20 20:24:51 +00:00
* [SE]. *)
2017-02-12 21:27:57 +00:00
Module ListSet(SE : SET_WITH_EQUALITY) <: FINITE_SET with Definition key := SE.t.
2017-02-12 20:54:34 +00:00
Definition key := SE.t.
Definition t := list SE.t.
Definition empty : t := [].
2017-02-12 21:27:57 +00:00
Definition add (s : t) (k : key) : t := k :: s.
2017-02-12 20:54:34 +00:00
Fixpoint member (s : t) (k : key) : bool :=
match s with
| [] => false
| k' :: s' => SE.equal k' k || member s' k
end.
Theorem member_empty : forall k, member empty k = false.
Proof.
simplify.
equality.
Qed.
2017-02-12 21:27:57 +00:00
Theorem member_add_eq : forall k s,
member (add s k) k = true.
2017-02-12 20:54:34 +00:00
Proof.
simplify.
pose proof (SE.equal_ok k k).
cases (SE.equal k k); simplify.
equality.
equality.
Qed.
2017-02-12 21:27:57 +00:00
Theorem member_add_noteq : forall k1 k2 s,
2017-02-12 20:54:34 +00:00
k1 <> k2
2017-02-12 21:27:57 +00:00
-> member (add s k1) k2 = member s k2.
2017-02-12 20:54:34 +00:00
Proof.
simplify.
pose proof (SE.equal_ok k1 k2).
cases (SE.equal k1 k2); simplify.
equality.
equality.
Qed.
2017-02-12 21:27:57 +00:00
Theorem decidable_equality : forall a b : key, a = b \/ a <> b.
Proof.
simplify.
pose proof (SE.equal_ok a b).
cases (SE.equal a b); propositional.
Qed.
2017-02-12 20:54:34 +00:00
End ListSet.
2017-02-20 20:24:51 +00:00
(* Here's an example decidable-equality type for natural numbers. *)
2017-02-12 21:27:57 +00:00
Module NatWithEquality <: SET_WITH_EQUALITY with Definition t := nat.
2017-02-12 20:54:34 +00:00
Definition t := nat.
Fixpoint equal (a b : nat) : bool :=
match a, b with
| 0, 0 => true
| S a', S b' => equal a' b'
| _, _ => false
end.
Theorem equal_ok : forall a b, if equal a b then a = b else a <> b.
Proof.
induct a; simplify.
cases b.
equality.
equality.
cases b.
equality.
specialize (IHa b).
cases (equal a b).
equality.
equality.
Qed.
End NatWithEquality.
2017-02-20 20:24:51 +00:00
(* And here's how to instantiate the generic set for the naturals. *)
2017-02-12 20:54:34 +00:00
Module NatSet := ListSet(NatWithEquality).
2017-02-12 21:27:57 +00:00
2017-02-20 20:24:51 +00:00
(* Now, some generic client code, for testing duplicate-freeness of lists. *)
2017-02-12 21:27:57 +00:00
Module FindDuplicates (FS : FINITE_SET).
Fixpoint noDuplicates' (ls : list FS.key) (s : FS.t) : bool :=
match ls with
| [] => true
| x :: ls' => negb (FS.member s x) && noDuplicates' ls' (FS.add s x)
end.
Definition noDuplicates (ls : list FS.key) := noDuplicates' ls FS.empty.
2017-02-20 20:24:51 +00:00
(* A characterization of having a duplicate: the list can be partitioned into
* pieces revealing the same element [a] at two boundaries. *)
2017-02-12 21:27:57 +00:00
Definition hasDuplicate (ls : list FS.key) :=
exists ls1 a ls2 ls3, ls = ls1 ++ a :: ls2 ++ a :: ls3.
2017-02-20 20:24:51 +00:00
(* A characterization of containing an element [a]: the list can be
* partitioned into two pieces, with [a] at the boundary. *)
2017-02-12 21:27:57 +00:00
Definition contains (a : FS.key) (ls : list FS.key) :=
exists ls1 ls2, ls = ls1 ++ a :: ls2.
Lemma noDuplicates'_ok : forall ls s, if noDuplicates' ls s
then ~(hasDuplicate ls
\/ exists a, FS.member s a = true
/\ contains a ls)
else (hasDuplicate ls
\/ exists a, FS.member s a = true
/\ contains a ls).
Proof.
induct ls; simplify.
unfold hasDuplicate, contains.
propositional.
first_order.
cases x; simplify.
equality.
equality.
first_order.
cases x0; simplify.
equality.
equality.
cases (FS.member s a); simplify.
right.
exists a.
propositional.
unfold contains.
exists [].
exists ls.
simplify.
equality.
specialize (IHls (FS.add s a)).
cases (noDuplicates' ls (FS.add s a)).
propositional.
apply H1.
exists a.
propositional.
apply FS.member_add_eq.
unfold hasDuplicate, contains in *.
first_order.
cases x; simplify.
invert H0.
exists x1.
exists x2.
equality.
invert H0.
exfalso.
apply H with (x3 := x).
exists x0.
exists x1.
exists x2.
equality.
first_order.
apply H1 with x.
propositional.
pose proof (FS.decidable_equality a x).
propositional.
rewrite H4.
apply FS.member_add_eq.
rewrite FS.member_add_noteq.
assumption.
assumption.
cases x0; simplify.
equality.
invert H2.
exists x0.
exists x1.
equality.
first_order.
left.
exists (a :: x).
exists x0.
exists x1.
exists x2.
simplify.
equality.
cases (FS.member s x).
right.
exists x.
propositional.
exists (a :: x0).
exists x1.
simplify.
equality.
left.
pose proof (FS.decidable_equality a x).
propositional.
exists nil.
exists a.
exists x0.
exists x1.
simplify.
equality.
rewrite FS.member_add_noteq in H.
equality.
assumption.
Qed.
Theorem noDuplicates_ok : forall ls, if noDuplicates ls
then ~hasDuplicate ls
else hasDuplicate ls.
Proof.
simplify.
pose proof (noDuplicates'_ok ls FS.empty).
unfold noDuplicates.
cases (noDuplicates' ls FS.empty); first_order.
rewrite FS.member_empty in H.
equality.
Qed.
End FindDuplicates.
Module NatDuplicateFinder := FindDuplicates(NatSet).
Compute NatDuplicateFinder.noDuplicates [].
Compute NatDuplicateFinder.noDuplicates [1].
Compute NatDuplicateFinder.noDuplicates [1; 2].
Compute NatDuplicateFinder.noDuplicates [1; 2; 3].
Compute NatDuplicateFinder.noDuplicates [1; 2; 1; 3].
2017-02-12 22:49:30 +00:00
2017-02-20 20:24:51 +00:00
(** * Custom implementations of abstract data types *)
(* Sometimes we want to write custom implementations of polymorphic data types.
* Our last example of duplicate detection is a good one: we can make it much
* faster when we know something about how the lists will be built. In
* particular, finite sets of natural numbers can be made compact when we know
* that the common case is *intervals*, sets of consecutive numbers. *)
2017-02-12 22:49:30 +00:00
Module NatRangeSet <: FINITE_SET with Definition key := nat.
Definition key := nat.
Inductive rangeSet : Set :=
| Empty
2017-02-20 20:24:51 +00:00
(* Set has no elements. *)
2017-02-12 22:49:30 +00:00
| Range (from to : nat)
2017-02-20 20:24:51 +00:00
(* Set contains exactly the numbers from [from] to [to], inclusive. *)
| AdHoc (s : NatSet.t)
(* Set isn't an interval, so fall back on the list-based version. *).
2017-02-12 22:49:30 +00:00
Definition t := rangeSet.
2017-02-20 20:24:51 +00:00
(* When we realize that a freshly formed set isn't an interval, we often need
* to convert an interval to an ad-hoc set. These functions accomplish
* that. *)
2017-02-12 22:49:30 +00:00
Fixpoint fromRange' (from to : nat) : NatSet.t :=
match to with
| 0 => NatSet.add NatSet.empty 0
| S to' => if NatWithEquality.equal to from
then NatSet.add NatSet.empty to
else NatSet.add (fromRange' from to') (S to')
end.
Definition fromRange (from to : nat) : NatSet.t :=
if Compare_dec.leb from to
then fromRange' from to
else NatSet.empty.
Definition empty : t := Empty.
Definition add (s : t) (k : key) : t :=
match s with
| Empty => Range k k
| Range from to =>
if Compare_dec.leb from k && Compare_dec.leb k to
then s
else if NatWithEquality.equal k (from - 1) && Compare_dec.leb from to
then Range k to
else if NatWithEquality.equal k (to + 1) && Compare_dec.leb from to
then Range from k
else AdHoc (NatSet.add (fromRange from to) k)
| AdHoc s' => AdHoc (NatSet.add s' k)
end.
Definition member (s : t) (k : key) : bool :=
match s with
| Empty => false
| Range from to => Compare_dec.leb from to && Compare_dec.leb from k && Compare_dec.leb k to
| AdHoc s' => NatSet.member s' k
end.
Theorem member_empty : forall k, member empty k = false.
Proof.
simplify.
equality.
Qed.
Lemma member_fromRange' : forall k from to,
from <= to
-> NatSet.member (fromRange' from to) k = Compare_dec.leb from k && Compare_dec.leb k to.
Proof.
induct to; simplify.
cases k; simplify.
rewrite Compare_dec.leb_correct by assumption.
equality.
rewrite Compare_dec.leb_correct by linear_arithmetic.
equality.
cases from; simplify.
cases k; simplify.
apply IHto.
linear_arithmetic.
pose proof (NatWithEquality.equal_ok to k).
cases (NatWithEquality.equal to k); simplify.
rewrite Compare_dec.leb_correct by linear_arithmetic.
equality.
rewrite IHto by linear_arithmetic.
cases to.
rewrite Compare_dec.leb_correct_conv by linear_arithmetic.
equality.
cases (Compare_dec.leb k to).
apply Compare_dec.leb_complete in Heq0.
rewrite Compare_dec.leb_correct by linear_arithmetic.
equality.
apply Compare_dec.leb_complete_conv in Heq0.
rewrite Compare_dec.leb_correct_conv by linear_arithmetic.
equality.
pose proof (NatWithEquality.equal_ok to from).
cases (NatWithEquality.equal to from); simplify.
cases k; simplify.
equality.
pose proof (NatWithEquality.equal_ok to k).
cases (NatWithEquality.equal to k); simplify.
rewrite Compare_dec.leb_correct by linear_arithmetic.
rewrite Compare_dec.leb_correct by linear_arithmetic.
equality.
cases (Compare_dec.leb from k); simplify.
apply Compare_dec.leb_complete in Heq1.
rewrite Compare_dec.leb_correct_conv by linear_arithmetic.
equality.
equality.
cases k; simplify.
apply IHto.
linear_arithmetic.
rewrite IHto by linear_arithmetic.
pose proof (NatWithEquality.equal_ok to k).
cases (NatWithEquality.equal to k); simplify.
rewrite Compare_dec.leb_correct by linear_arithmetic.
rewrite Compare_dec.leb_correct by linear_arithmetic.
equality.
cases to.
rewrite (Compare_dec.leb_correct_conv 0 k) by linear_arithmetic.
equality.
cases (Compare_dec.leb k to).
apply Compare_dec.leb_complete in Heq1.
rewrite (Compare_dec.leb_correct k (S to)) by linear_arithmetic.
equality.
apply Compare_dec.leb_complete_conv in Heq1.
rewrite (Compare_dec.leb_correct_conv (S to) k) by linear_arithmetic.
equality.
Qed.
Theorem member_add_eq : forall k s,
member (add s k) k = true.
Proof.
unfold member, add; simplify.
cases s.
SearchAbout Compare_dec.leb.
rewrite Compare_dec.leb_correct.
equality.
linear_arithmetic.
cases (Compare_dec.leb from k); simplify.
cases (Compare_dec.leb k to); simplify.
rewrite Heq.
rewrite Heq0.
apply Compare_dec.leb_complete in Heq.
apply Compare_dec.leb_complete in Heq0.
rewrite Compare_dec.leb_correct by linear_arithmetic.
equality.
pose proof (NatWithEquality.equal_ok k (from - 1)).
cases (NatWithEquality.equal k (from - 1)).
apply leb_complete in Heq.
apply leb_complete_conv in Heq0.
linear_arithmetic.
simplify.
pose proof (NatWithEquality.equal_ok k (to + 1)).
cases (NatWithEquality.equal k (to + 1)); simplify.
cases (Compare_dec.leb from to).
rewrite Heq.
rewrite Compare_dec.leb_correct by linear_arithmetic.
equality.
apply NatSet.member_add_eq.
pose proof (NatWithEquality.equal_ok k k).
cases (NatWithEquality.equal k k); simplify.
equality.
equality.
pose proof (NatWithEquality.equal_ok k (from - 1)).
cases (NatWithEquality.equal k (from - 1)); simplify.
cases (Compare_dec.leb from to).
apply Compare_dec.leb_complete in Heq1.
rewrite Compare_dec.leb_correct by linear_arithmetic.
rewrite Compare_dec.leb_correct by linear_arithmetic.
equality.
pose proof (NatWithEquality.equal_ok k (to + 1)).
cases (NatWithEquality.equal k (to + 1)); simplify.
pose proof (NatWithEquality.equal_ok k k).
cases (NatWithEquality.equal k k); simplify.
equality.
equality.
pose proof (NatWithEquality.equal_ok k k).
cases (NatWithEquality.equal k k); simplify.
equality.
equality.
pose proof (NatWithEquality.equal_ok k (to + 1)).
cases (NatWithEquality.equal k (to + 1)); simplify.
cases (Compare_dec.leb from to).
apply Compare_dec.leb_complete in Heq2.
apply Compare_dec.leb_complete_conv in Heq.
linear_arithmetic.
apply NatSet.member_add_eq.
pose proof (NatWithEquality.equal_ok k k).
cases (NatWithEquality.equal k k); simplify.
equality.
equality.
apply NatSet.member_add_eq.
Qed.
Theorem member_add_noteq : forall k1 k2 s,
k1 <> k2
-> member (add s k1) k2 = member s k2.
Proof.
simplify.
unfold member, add.
cases s.
cases (Compare_dec.leb k1 k2); simplify.
rewrite Compare_dec.leb_correct by linear_arithmetic.
apply Compare_dec.leb_complete in Heq.
rewrite Compare_dec.leb_correct_conv.
equality.
unfold key in *. (* Tricky step! Coq needs to see that we are really working with numbers. *)
linear_arithmetic.
rewrite Compare_dec.leb_correct by linear_arithmetic.
equality.
cases (Compare_dec.leb from k1); simplify.
cases (Compare_dec.leb k1 to); simplify.
equality.
pose proof (NatWithEquality.equal_ok k1 (from - 1)).
cases (NatWithEquality.equal k1 (from - 1)); simplify.
apply leb_complete in Heq.
apply leb_complete_conv in Heq0.
linear_arithmetic.
pose proof (NatWithEquality.equal_ok k1 (to + 1)).
cases (NatWithEquality.equal k1 (to + 1)); simplify.
cases (Compare_dec.leb from to).
rewrite H1.
cases (Compare_dec.leb from k2); simplify.
cases (Compare_dec.leb k2 to).
apply Compare_dec.leb_complete in Heq5.
apply Compare_dec.leb_complete in Heq3.
rewrite Compare_dec.leb_correct by linear_arithmetic.
rewrite Compare_dec.leb_correct by linear_arithmetic.
equality.
apply Compare_dec.leb_complete in Heq3.
rewrite Compare_dec.leb_correct by linear_arithmetic.
apply Compare_dec.leb_complete_conv in Heq5.
unfold key in *.
rewrite Compare_dec.leb_correct_conv by linear_arithmetic.
equality.
rewrite andb_false_r.
equality.
simplify.
pose proof (NatWithEquality.equal_ok k1 k2).
cases (NatWithEquality.equal k1 k2); simplify.
equality.
unfold fromRange.
rewrite Heq3.
apply NatSet.member_empty.
pose proof (NatWithEquality.equal_ok k1 k2).
cases (NatWithEquality.equal k1 k2); simplify.
equality.
unfold fromRange.
cases (Compare_dec.leb from to); simplify.
apply member_fromRange'.
apply Compare_dec.leb_complete.
assumption.
equality.
pose proof (NatWithEquality.equal_ok k1 (from - 1)).
cases (NatWithEquality.equal k1 (from - 1)); simplify.
cases (Compare_dec.leb from to).
apply Compare_dec.leb_complete in Heq1.
rewrite Compare_dec.leb_correct by linear_arithmetic.
f_equal.
f_equal.
cases (Compare_dec.leb k1 k2).
apply Compare_dec.leb_complete in Heq2.
apply Compare_dec.leb_complete_conv in Heq.
unfold key in *.
rewrite Compare_dec.leb_correct by linear_arithmetic.
equality.
apply Compare_dec.leb_complete_conv in Heq2.
apply Compare_dec.leb_complete_conv in Heq.
unfold key in *.
rewrite Compare_dec.leb_correct_conv by linear_arithmetic.
equality.
pose proof (NatWithEquality.equal_ok k1 (to + 1)).
cases (NatWithEquality.equal k1 (to + 1)); simplify.
pose proof (NatWithEquality.equal_ok k1 k2).
cases (NatWithEquality.equal k1 k2); simplify.
unfold key in *; linear_arithmetic.
unfold fromRange.
rewrite Heq1.
apply NatSet.member_empty.
pose proof (NatWithEquality.equal_ok k1 k2).
cases (NatWithEquality.equal k1 k2); simplify.
equality.
unfold fromRange.
rewrite Heq1.
apply NatSet.member_empty.
pose proof (NatWithEquality.equal_ok k1 (to + 1)).
cases (NatWithEquality.equal k1 (to + 1)); simplify.
cases (Compare_dec.leb from to).
rewrite Heq; simplify.
apply Compare_dec.leb_complete in Heq2.
apply Compare_dec.leb_complete_conv in Heq.
linear_arithmetic.
rewrite NatSet.member_add_noteq by assumption; simplify.
unfold fromRange.
rewrite Heq2.
apply NatSet.member_empty.
pose proof (NatWithEquality.equal_ok k1 k2).
cases (NatWithEquality.equal k1 k2); simplify.
equality.
unfold fromRange.
cases (Compare_dec.leb from to); simplify.
apply member_fromRange'.
apply Compare_dec.leb_complete; assumption.
equality.
apply NatSet.member_add_noteq.
assumption.
Qed.
Theorem decidable_equality : forall a b : key, a = b \/ a <> b.
Proof.
simplify.
pose proof (NatWithEquality.equal_ok a b).
cases (NatWithEquality.equal a b); propositional.
Qed.
End NatRangeSet.
2017-02-20 20:24:51 +00:00
(* Time for a head-to-head performance contest between our naive and clever
* sets! *)
2017-02-12 22:49:30 +00:00
Module FasterNatDuplicateFinder := FindDuplicates(NatRangeSet).
Fixpoint upto (n : nat) : list nat :=
match n with
| 0 => []
| S n' => n' :: upto n'
end.
2017-02-20 21:06:33 +00:00
Compute upto 10.
2018-02-26 00:40:10 +00:00
Time Compute NatDuplicateFinder.noDuplicates (upto 1000).
Time Compute FasterNatDuplicateFinder.noDuplicates (upto 1000).