Library LangAlg.tools
Toolbox of simple definitions, lemmas and tactics.
Require Export Eqdep Setoid Morphisms Psatz List Bool.
Export ListNotations.
Set Implicit Arguments.
Infix " ∘ " := Basics.compose (at level 40).
Hint Unfold Basics.compose.
Notation " π{ x } " := (proj1_sig x).
Lifting of a binary function with arguments of type A and values
of type B to a binary function with arguments of type option A and
values of type option B.
Definition option_bimap (A B : Type) (f : A → A → B) x y :=
match (x,y) with
| (Some a,Some b) ⇒ Some (f a b)
| _ ⇒ None
end.
match (x,y) with
| (Some a,Some b) ⇒ Some (f a b)
| _ ⇒ None
end.
Typeclass and notation for axiomatic equivalence relations.
Typeclass and notation for semantic equivalence relations.
Typeclass and notation for axiomatic preorder relations.
Typeclass and notation for semantic preorder relations.
Class SemSmaller (A : Type) := ssmaller : relation A.
Notation " e ≲ f " := (ssmaller e f) (at level 80).
Notation " e ≲ f " := (ssmaller e f) (at level 80).
Typeclass and notation for support functions
Class Support (T : Set → Set → Set) (X Y : Set) :=
support : T X Y → list X.
Notation " ⌊ e ⌋ " := (support e).
support : T X Y → list X.
Notation " ⌊ e ⌋ " := (support e).
Typeclass and notation for functions extracting sets of
variables.
Class Alphabet (T : Set → Set) (X : Set) := variables : T X → list X.
Notation " ⌈ e ⌉ " := (variables e).
Notation " ⌈ e ⌉ " := (variables e).
Typeclass and notation for size functions
Typeclass and notation for semantic maps.
Class semantics (T : Set → Set) (D : Set → Type) (X : Set) (A : Set)
:= interprete : (X → D A) → T X → D A.
Notation " ⟦ e ⟧ σ " := (interprete σ e) (at level 75).
:= interprete : (X → D A) → T X → D A.
Notation " ⟦ e ⟧ σ " := (interprete σ e) (at level 75).
The following two definitions describe the standard way of
relating two terms through their semantics.
Definition semantic_equality
(T : Set → Set) (D : Set → Type) (X : Set)
`{ ∀ A : Set, SemEquiv (D A) }
`{ ∀ A : Set, semantics T D X A } : SemEquiv (T X) :=
fun x y ⇒ ∀ A : Set,∀ σ : X → D A, ⟦x⟧σ ≃ ⟦y⟧σ.
Definition semantic_containment
(T : Set → Set) (D : Set → Type) (X : Set)
`{ ∀ A : Set, SemSmaller (D A) }
`{ ∀ A : Set, semantics T D X A} : SemSmaller (T X) :=
fun x y ⇒ ∀ A : Set,∀ σ : X → D A, ⟦x⟧σ ≲ ⟦y⟧σ.
Hint Unfold semantic_containment semantic_equality ssmaller sequiv: semantics.
Global Instance semantic_equality_Equivalence
(T : Set → Set) (D : Set → Type) (X : Set)
`{eqA: ∀ A : Set, SemEquiv (D A)}
`{S : ∀ A, semantics T D X A}
`{∀ A : Set, Equivalence (eqA A)} :
Equivalence (@semantic_equality T D X eqA S).
Global Instance semantic_containment_PreOrder
(T : Set → Set) (D : Set → Type) (X : Set)
`{inclA: ∀ A : Set, SemSmaller (D A)}
`{S : ∀ A, semantics T D X A}
`{∀ A : Set, PreOrder (inclA A)} :
PreOrder (@semantic_containment T D X inclA S).
Global Instance semantic_containment_PartialOrder
(T : Set → Set) (D : Set → Type) (X : Set)
`{eqA: ∀ A : Set, SemEquiv (D A)}
`{inclA: ∀ A : Set, SemSmaller (D A)}
`{S : ∀ A, semantics T D X A}
`{equivA : ∀ A : Set, Equivalence (eqA A)}
`{preordA : ∀ A : Set, PreOrder (inclA A)}
`{partordA : ∀ A : Set, PartialOrder (eqA A) (inclA A)} :
@PartialOrder
_ _ (@semantic_equality_Equivalence T D X eqA S equivA)
_ (@semantic_containment_PreOrder T D X inclA S preordA).
(T : Set → Set) (D : Set → Type) (X : Set)
`{ ∀ A : Set, SemEquiv (D A) }
`{ ∀ A : Set, semantics T D X A } : SemEquiv (T X) :=
fun x y ⇒ ∀ A : Set,∀ σ : X → D A, ⟦x⟧σ ≃ ⟦y⟧σ.
Definition semantic_containment
(T : Set → Set) (D : Set → Type) (X : Set)
`{ ∀ A : Set, SemSmaller (D A) }
`{ ∀ A : Set, semantics T D X A} : SemSmaller (T X) :=
fun x y ⇒ ∀ A : Set,∀ σ : X → D A, ⟦x⟧σ ≲ ⟦y⟧σ.
Hint Unfold semantic_containment semantic_equality ssmaller sequiv: semantics.
Global Instance semantic_equality_Equivalence
(T : Set → Set) (D : Set → Type) (X : Set)
`{eqA: ∀ A : Set, SemEquiv (D A)}
`{S : ∀ A, semantics T D X A}
`{∀ A : Set, Equivalence (eqA A)} :
Equivalence (@semantic_equality T D X eqA S).
Global Instance semantic_containment_PreOrder
(T : Set → Set) (D : Set → Type) (X : Set)
`{inclA: ∀ A : Set, SemSmaller (D A)}
`{S : ∀ A, semantics T D X A}
`{∀ A : Set, PreOrder (inclA A)} :
PreOrder (@semantic_containment T D X inclA S).
Global Instance semantic_containment_PartialOrder
(T : Set → Set) (D : Set → Type) (X : Set)
`{eqA: ∀ A : Set, SemEquiv (D A)}
`{inclA: ∀ A : Set, SemSmaller (D A)}
`{S : ∀ A, semantics T D X A}
`{equivA : ∀ A : Set, Equivalence (eqA A)}
`{preordA : ∀ A : Set, PreOrder (inclA A)}
`{partordA : ∀ A : Set, PartialOrder (eqA A) (inclA A)} :
@PartialOrder
_ _ (@semantic_equality_Equivalence T D X eqA S equivA)
_ (@semantic_containment_PreOrder T D X inclA S preordA).
Decidable sets
Class decidable_set X :=
{ eqX : X → X → bool;
eqX_eq : ∀ x y, reflect (x = y) (eqX x y)}.
Section decidable.
Context { X : Set }.
Context { D : decidable_set X }.
{ eqX : X → X → bool;
eqX_eq : ∀ x y, reflect (x = y) (eqX x y)}.
Section decidable.
Context { X : Set }.
Context { D : decidable_set X }.
This lemma is the preferred way of translating back and forth
between boolean and propositional equality.
The next few lemmas are useful on occasion.
Lemma eqX_false :
∀ x y, eqX x y = false ↔ x ≠ y.
Lemma X_dec (x y : X) : {x = y} + {x ≠ y}.
Lemma eqX_refl x : eqX x x = true.
∀ x y, eqX x y = false ↔ x ≠ y.
Lemma X_dec (x y : X) : {x = y} + {x ≠ y}.
Lemma eqX_refl x : eqX x x = true.
Whenever we have a decidable set, we can define the following
replacement function.
Definition replace a b := (fun n ⇒ if eqX n a then b else n).
End decidable.
Notation " {| a \ b |} " := (replace a b).
End decidable.
Notation " {| a \ b |} " := (replace a b).
The set of natural numbers is decidable.
The set of natural booleans is decidable.
Filtering a concatenation is the same as concatenating filters.
Filtering reduces the length of the list.
The following lemma give a more precise description.
Lemma filter_length_eq {A} (f : A → bool) l :
length (filter f l) + length (filter (fun x ⇒ negb (f x)) l) = length l.
length (filter f l) + length (filter (fun x ⇒ negb (f x)) l) = length l.
filter only depends on the values of the filtering function, not
its implementation.
Lemma filter_ext_In {A} (f g : A → bool) l :
(∀ x, In x l → f x = g x) → filter f l = filter g l.
Lemma filter_ext {A} (f g : A → bool) l :
(∀ x, f x = g x) → filter f l = filter g l.
(∀ x, In x l → f x = g x) → filter f l = filter g l.
Lemma filter_ext {A} (f g : A → bool) l :
(∀ x, f x = g x) → filter f l = filter g l.
Filtering with the predicate true does not modify the list.
Filtering preserves the property NoDup (absence of duplicates).
If two concatenations are equal, and if the two initial factors
have the same length, then the factors are equal.
Lemma length_app {A} (l1 l2 m1 m2 : list A) :
length l1 = length m1 → l1++l2 = m1++m2 → l1 = m1 ∧ l2 = m2.
We use the following symbol to denote the membership predicate.
We say that two lists are equivalent if they contain the same
elements.
We now establish that ≈ is a congruence, and that ⊆ is a
partial order.
Global Instance equivalent_Equivalence T : Equivalence (@equivalent T).
Global Instance incl_PreOrder T : PreOrder (@incl T).
Global Instance incl_PartialOrder T :
PartialOrder (@equivalent T) (@incl T).
Global Instance incl_PreOrder T : PreOrder (@incl T).
Global Instance incl_PartialOrder T :
PartialOrder (@equivalent T) (@incl T).
Concatenation preserves both relations.
Global Instance incl_app_Proper T :
Proper ((@incl T) ==> (@incl T) ==> (@incl T)) (@app T).
Global Instance equivalent_app_Proper T :
Proper ((@equivalent T) ==> (@equivalent T) ==> (@equivalent T))
(@app T).
Proper ((@incl T) ==> (@incl T) ==> (@incl T)) (@app T).
Global Instance equivalent_app_Proper T :
Proper ((@equivalent T) ==> (@equivalent T) ==> (@equivalent T))
(@app T).
The operation of adding an element to a list preserves both
relations.
Global Instance incl_cons_Proper T a :
Proper ((@incl T) ==> (@incl T)) (cons a).
Global Instance equivalent_cons_Proper T a :
Proper ((@equivalent T) ==> (@equivalent T)) (cons a).
Proper ((@incl T) ==> (@incl T)) (cons a).
Global Instance equivalent_cons_Proper T a :
Proper ((@equivalent T) ==> (@equivalent T)) (cons a).
Global Instance map_incl_Proper {A B} (f : A → B) :
Proper (@incl A ==> @incl B) (map f).
Global Instance map_equivalent_Proper {A B} (f : A → B) :
Proper (@equivalent A ==> @equivalent B) (map f).
Proper (@incl A ==> @incl B) (map f).
Global Instance map_equivalent_Proper {A B} (f : A → B) :
Proper (@equivalent A ==> @equivalent B) (map f).
Global Instance filter_incl_Proper {A} (f : A → bool) :
Proper (@incl A ==> @incl A) (filter f).
Global Instance filter_equivalent_Proper {A} (f : A → bool) :
Proper (@equivalent A ==> @equivalent A) (filter f).
Proper (@incl A ==> @incl A) (filter f).
Global Instance filter_equivalent_Proper {A} (f : A → bool) :
Proper (@equivalent A ==> @equivalent A) (filter f).
The lemmas In_incl_Proper and In_equivalent_Proper are
completely tautological, but turn out to be useful for technical
reasons.
Global Instance In_incl_Proper {A} (x : A):
Proper ((@incl A) ==> Basics.impl) (In x).
Global Instance In_equivalent_Proper {A} (x : A):
Proper ((@equivalent A) ==> iff) (In x).
Proper ((@incl A) ==> Basics.impl) (In x).
Global Instance In_equivalent_Proper {A} (x : A):
Proper ((@equivalent A) ==> iff) (In x).
The following simple facts about inclusion are convenient to have
in our toolbox.
Lemma incl_nil T (A : list T) : [] ⊆ A.
Lemma incl_app_or T (A B C : list T) : A ⊆ B ∨ A ⊆ C → A⊆B++C.
Lemma incl_app_split {A} (l m p : list A) :
l++m ⊆ p ↔ l⊆ p ∧ m ⊆ p.
Hint Resolve incl_appl incl_appr incl_nil app_assoc : eq_list.
Lemma incl_app_or T (A B C : list T) : A ⊆ B ∨ A ⊆ C → A⊆B++C.
Lemma incl_app_split {A} (l m p : list A) :
l++m ⊆ p ↔ l⊆ p ∧ m ⊆ p.
Hint Resolve incl_appl incl_appr incl_nil app_assoc : eq_list.
If a boolean predicate f fails to be true for every element of a
list l, then there must exists a witnessing element y falsifying
f. This is an instance of a classical principle holding
constructively when restricted to decidable properties over finite
domains.
Boolean equality for lists
Fixpoint equal_list l m :=
match (l,m) with
| ([],[]) ⇒ true
| (a::l,b::m) ⇒ eqX a b && equal_list l m
| _ ⇒ false
end.
Lemma equal_list_spec l m : reflect (l = m) (equal_list l m).
match (l,m) with
| ([],[]) ⇒ true
| (a::l,b::m) ⇒ eqX a b && equal_list l m
| _ ⇒ false
end.
Lemma equal_list_spec l m : reflect (l = m) (equal_list l m).
The set of lists of As is decidable.
Boolean predicate testing whether an element belongs to a list.
Definition inb (n : A) l := existsb (eqX n) l.
Lemma inb_spec n l : inb n l = true ↔ n ∈ l.
Lemma inb_false n l : inb n l = false ↔ ¬ n ∈ l.
Lemma inb_spec n l : inb n l = true ↔ n ∈ l.
Lemma inb_false n l : inb n l = false ↔ ¬ n ∈ l.
This boolean predicate allows us to use the excluded middle with
the predicate In.
Lemma inb_dec n l :
{ inb n l = true ∧ n ∈ l } + { inb n l = false ∧ ¬ n ∈ l }.
Lemma In_dec (n : A) l : { n ∈ l } + { ¬ n ∈ l }.
{ inb n l = true ∧ n ∈ l } + { inb n l = false ∧ ¬ n ∈ l }.
Lemma In_dec (n : A) l : { n ∈ l } + { ¬ n ∈ l }.
Removing an element from a list.
Definition rm (x : A) := filter (fun y ⇒ negb (eqX x y)).
Hint Unfold rm.
Lemma in_rm x y l : x ∈ (rm y l) ↔ x ≠ y ∧ x ∈ l .
Hint Unfold rm.
Lemma in_rm x y l : x ∈ (rm y l) ↔ x ≠ y ∧ x ∈ l .
rm reduces the length.
Global Instance rm_incl_Proper a :
Proper (@incl A ==> @incl A) (rm a).
Global Instance rm_equivalent_Proper a :
Proper (@equivalent A ==> @equivalent A) (rm a).
Proper (@incl A ==> @incl A) (rm a).
Global Instance rm_equivalent_Proper a :
Proper (@equivalent A ==> @equivalent A) (rm a).
Removing an element from a lists reduces its length.
Boolean function implementing containment test.
Definition inclb (a b : list A) :=
forallb (fun x ⇒ inb x b) a.
Lemma inclb_correct a b : inclb a b = true ↔ a ⊆ b.
forallb (fun x ⇒ inb x b) a.
Lemma inclb_correct a b : inclb a b = true ↔ a ⊆ b.
Definition seq_morph {B} (φ1 φ2 : A → B) l :=
fun x ⇒ if inb x l then φ1 x else φ2 x.
Notation " f ⧼ l ⧽ g " := (seq_morph f g l) (at level 60).
fun x ⇒ if inb x l then φ1 x else φ2 x.
Notation " f ⧼ l ⧽ g " := (seq_morph f g l) (at level 60).
seq_morph uses its argument list as a set, so replacing a list
with an equivalent list doesn't change the function.
Lemma seq_morph_equivalent {B} l m
(f g : A → B) x :
l ≈ m → (f ⧼l⧽ g) x = (f ⧼m⧽ g) x.
End dec_list.
(f g : A → B) x :
l ≈ m → (f ⧼l⧽ g) x = (f ⧼m⧽ g) x.
End dec_list.
Bimap
Definition bimap A B C (f : A → B → C) l m :=
flat_map (fun a ⇒ map (f a) m) l.
Lemma in_bimap A B C (f : A → B → C) l m x :
x ∈ (bimap f l m) ↔ ∃ a b, f a b = x ∧ a ∈ l ∧ b ∈ m.
flat_map (fun a ⇒ map (f a) m) l.
Lemma in_bimap A B C (f : A → B → C) l m x :
x ∈ (bimap f l m) ↔ ∃ a b, f a b = x ∧ a ∈ l ∧ b ∈ m.
Lemma map_bimap A B C D (g : C → D) (f : A → B → C) l m :
map g (bimap f l m) = bimap (fun x y ⇒ g (f x y)) l m.
map g (bimap f l m) = bimap (fun x y ⇒ g (f x y)) l m.
Global Instance bimap_equiv {A B C} (f : A → B → C) :
Proper ((@equivalent A) ==> (@equivalent B)==> (@equivalent C)) (bimap f).
Proper ((@equivalent A) ==> (@equivalent B)==> (@equivalent C)) (bimap f).
Invertible functions
For we can compute an inverse of a composite function by composing
the inverses of its parts in reverse order.
Lemma inverse_composition {X Y Z : Set}
(f : X → Y) f' (g : Y → Z) g' A :
inverse f f' A → inverse g g' (List.map f A) →
inverse (g∘f) (f'∘g') A.
Lemma injective_compose {X Y Z : Set}
(f : X → Y) (g : Y → Z) A :
injective f A → injective g (List.map f A) → injective (g∘f) A.
(f : X → Y) f' (g : Y → Z) g' A :
inverse f f' A → inverse g g' (List.map f A) →
inverse (g∘f) (f'∘g') A.
Lemma injective_compose {X Y Z : Set}
(f : X → Y) (g : Y → Z) A :
injective f A → injective g (List.map f A) → injective (g∘f) A.
Lemma inverse_replace {X : Set} `{decidable_set X} (a b : X) A :
¬ b ∈ A → inverse {| a \ b |} {| b \ a |} A.
Lemma injective_replace {X : Set} `{decidable_set X} (a b : X) A :
¬ b ∈ A → injective {| a \ b |} A.
¬ b ∈ A → inverse {| a \ b |} {| b \ a |} A.
Lemma injective_replace {X : Set} `{decidable_set X} (a b : X) A :
¬ b ∈ A → injective {| a \ b |} A.
The following trivial lemmas are used in many places in the
proof.
Definition incr a b := a + b.
Notation " ⨥ " := incr.
Definition decr a b := b - a.
Notation " ⨪ " := decr.
Hint Transparent incr decr.
Hint Unfold decr incr.
Lemma inverse_add_left n A :
inverse (⨥ n) (⨪ n) A.
Lemma injective_add_left n A : injective (⨥ n) A.
Notation " ⨥ " := incr.
Definition decr a b := b - a.
Notation " ⨪ " := decr.
Hint Transparent incr decr.
Hint Unfold decr incr.
Lemma inverse_add_left n A :
inverse (⨥ n) (⨪ n) A.
Lemma injective_add_left n A : injective (⨥ n) A.
Lists over a duplicated set
In several parts of the proofs, we'll need to duplicate our alphabet. This section provides the way to do that, as well as some key observations about this new alphabet.
We encode the duplicated alphabet as the Cartesian product of
the original alphabet with the booleans. We will call the pair
(a,true) a positive occurrence of a, and the pair (a,false) a
negative occurrence of a.
A list A is said to be balanced if a letter appear positively
if and only if it appears negatively.
Concatenation preserves balance.
The empty list is balanced.
If the original alphabet is decidable, so is its duplicate.