diff --git a/theories/base.v b/theories/base.v
index 886543243b65101fd634494e27fdd800dbbfa33a..10c91ff0296ab8cafb1ff9a863b6de712c82fc5b 100644
--- a/theories/base.v
+++ b/theories/base.v
@@ -107,17 +107,11 @@ Instance unit_inhabited: Inhabited unit := populate ().
 Instance list_inhabited {A} : Inhabited (list A) := populate [].
 Instance prod_inhabited {A B} (iA : Inhabited A)
     (iB : Inhabited B) : Inhabited (A * B) :=
-  match iA, iB with
-  | populate x, populate y => populate (x,y)
-  end.
+  match iA, iB with populate x, populate y => populate (x,y) end.
 Instance sum_inhabited_l {A B} (iA : Inhabited A) : Inhabited (A + B) :=
-  match iA with
-  | populate x => populate (inl x)
-  end.
+  match iA with populate x => populate (inl x) end.
 Instance sum_inhabited_r {A B} (iB : Inhabited A) : Inhabited (A + B) :=
-  match iB with
-  | populate y => populate (inl y)
-  end.
+  match iB with populate y => populate (inl y) end.
 Instance option_inhabited {A} : Inhabited (option A) := populate None.
 
 (** ** Proof irrelevant types *)
@@ -187,8 +181,7 @@ Notation "(∪)" := union (only parsing) : C_scope.
 Notation "( x ∪)" := (union x) (only parsing) : C_scope.
 Notation "(∪ x )" := (λ y, union y x) (only parsing) : C_scope.
 
-Definition union_list `{Empty A}
-  `{Union A} : list A → A := fold_right (∪) ∅.
+Definition union_list `{Empty A} `{Union A} : list A → A := fold_right (∪) ∅.
 Arguments union_list _ _ _ !_ /.
 Notation "⋃ l" := (union_list l) (at level 20, format "⋃  l") : C_scope.
 
@@ -208,9 +201,14 @@ Notation "(∖ x )" := (λ y, difference y x) (only parsing) : C_scope.
 
 Class Singleton A B := singleton: A → B.
 Instance: Params (@singleton) 3.
-Notation "{[ x ]}" := (singleton x) : C_scope.
+Notation "{[ x ]}" := (singleton x) (at level 1) : C_scope.
 Notation "{[ x ; y ; .. ; z ]}" :=
-  (union .. (union (singleton x) (singleton y)) .. (singleton z)) : C_scope.
+  (union .. (union (singleton x) (singleton y)) .. (singleton z))
+  (at level 1) : C_scope.
+Notation "{[ x , y ]}" := (singleton (x,y))
+  (at level 1, y at next level) : C_scope.
+Notation "{[ x , y , z ]}" := (singleton (x,y,z))
+  (at level 1, y at next level, z at next level) : C_scope.
 
 Class SubsetEq A := subseteq: A → A → Prop.
 Instance: Params (@subseteq) 2.
@@ -222,6 +220,8 @@ Notation "X ⊈ Y" := (¬X ⊆ Y) (at level 70) : C_scope.
 Notation "(⊈)" := (λ X Y, X ⊈ Y) (only parsing) : C_scope.
 Notation "( X ⊈ )" := (λ Y, X ⊈ Y) (only parsing) : C_scope.
 Notation "( ⊈ X )" := (λ Y, Y ⊈ X) (only parsing) : C_scope.
+Infix "⊆*" := (Forall2 subseteq) (at level 70) : C_scope.
+Notation "(⊆*)" := (Forall2 subseteq) (only parsing) : C_scope.
 
 Hint Extern 0 (_ ⊆ _) => reflexivity.
 
@@ -251,43 +251,51 @@ Class Disjoint A := disjoint : A → A → Prop.
 Instance: Params (@disjoint) 2.
 Infix "⊥" := disjoint (at level 70) : C_scope.
 Notation "(⊥)" := disjoint (only parsing) : C_scope.
-Notation "( X ⊥)" := (disjoint X) (only parsing) : C_scope.
-Notation "(⊥ X )" := (λ Y, disjoint Y X) (only parsing) : C_scope.
-
-Inductive list_disjoint `{Empty A} `{Union A}
-      `{Disjoint A} : list A → Prop :=
-  | disjoint_nil :
-     list_disjoint []
-  | disjoint_cons X Xs :
-     X ⊥ ⋃ Xs →
-     list_disjoint Xs →
-     list_disjoint (X :: Xs).
-Lemma list_disjoint_cons_inv `{Empty A} `{Union A} `{Disjoint A} X Xs :
-  list_disjoint (X :: Xs) →
-  X ⊥ ⋃ Xs ∧ list_disjoint Xs.
-Proof. inversion_clear 1; auto. Qed.
-
-Class Filter A B :=
-  filter: ∀ (P : A → Prop) `{∀ x, Decision (P x)}, B → B.
-
-(* Arguments filter {_ _ _} _ {_} !_ / : simpl nomatch. *)
+Notation "( X ⊥.)" := (disjoint X) (only parsing) : C_scope.
+Notation "(.⊥ X )" := (λ Y, disjoint Y X) (only parsing) : C_scope.
+
+Class DisjointList A := disjoint_list : list A → Prop.
+Instance: Params (@disjoint_list) 2.
+Notation "⊥ l" := (disjoint_list l) (at level 20, format "⊥  l") : C_scope.
+
+Section default_disjoint_list.
+  Context `{Empty A} `{Union A} `{Disjoint A}.
+  Inductive default_disjoint_list : DisjointList A :=
+    | disjoint_nil_2 : ⊥ []
+    | disjoint_cons_2 X Xs : X ⊥ ⋃ Xs → ⊥ Xs → ⊥ (X :: Xs).
+  Global Existing Instance default_disjoint_list.
+
+  Lemma disjoint_list_nil : ⊥ @nil A ↔ True.
+  Proof. split; constructor. Qed.
+  Lemma disjoint_list_cons X Xs : ⊥ (X :: Xs) ↔ X ⊥ ⋃ Xs ∧ ⊥ Xs.
+  Proof. split. inversion_clear 1; auto. intros [??]. constructor; auto. Qed.
+End default_disjoint_list.
+
+Class Filter A B := filter: ∀ (P : A → Prop) `{∀ x, Decision (P x)}, B → B.
 
 (** We define variants of the relations [(≡)] and [(⊆)] that are indexed by
 an environment. *)
 Class EquivEnv A B := equiv_env : A → relation B.
 Notation "X ≡@{ E } Y" := (equiv_env E X Y)
   (at level 70, format "X  ≡@{ E }  Y") : C_scope.
-Notation "(≡@{ E } )" := (equiv_env E)
-  (E at level 1, only parsing) : C_scope.
+Notation "(≡@{ E } )" := (equiv_env E) (E at level 1, only parsing) : C_scope.
 Instance: Params (@equiv_env) 4.
 
 Class SubsetEqEnv A B := subseteq_env : A → relation B.
-Notation "X ⊆@{ E } Y" := (subseteq_env E X Y)
-  (at level 70, format "X  ⊆@{ E }  Y") : C_scope.
-Notation "(⊆@{ E } )" := (subseteq_env E)
+Instance: Params (@subseteq_env) 4.
+Notation "X ⊑@{ E } Y" := (subseteq_env E X Y)
+  (at level 70, format "X  ⊑@{ E }  Y") : C_scope.
+Notation "(⊑@{ E } )" := (subseteq_env E)
+  (E at level 1, only parsing) : C_scope.
+Notation "X ⊑@{ E }* Y" := (Forall2 (subseteq_env E) X Y)
+  (at level 70, format "X  ⊑@{ E }*  Y") : C_scope.
+Notation "(⊑@{ E }*)" := (Forall2 (subseteq_env E))
   (E at level 1, only parsing) : C_scope.
 Instance: Params (@subseteq_env) 4.
 
+Hint Extern 0 (_ ≡@{_} _) => reflexivity.
+Hint Extern 0 (_ ⊑@{_} _) => reflexivity.
+
 (** ** Monadic operations *)
 (** We define operational type classes for the monadic operations bind, join 
 and fmap. These type classes are defined in a non-standard way by taking the
@@ -314,16 +322,16 @@ Arguments mret {_ _ _} _.
 Class MBindD (M : Type → Type) {A B} (f : A → M B) := mbind: M A → M B.
 Notation MBind M := (∀ {A B} (f : A → M B), MBindD M f)%type.
 Instance: Params (@mbind) 5.
-Arguments mbind {_ _ _} _ {_} !_ / : simpl nomatch.
+Arguments mbind {_ _ _} _ {_} !_ /.
 
 Class MJoin (M : Type → Type) := mjoin: ∀ {A}, M (M A) → M A.
 Instance: Params (@mjoin) 3.
-Arguments mjoin {_ _ _} !_ / : simpl nomatch.
+Arguments mjoin {_ _ _} !_ /.
 
 Class FMapD (M : Type → Type) {A B} (f : A → B) := fmap: M A → M B.
 Notation FMap M := (∀ {A B} (f : A → B), FMapD M f)%type.
 Instance: Params (@fmap) 6.
-Arguments fmap {_ _ _} _ {_} !_ / : simpl nomatch.
+Arguments fmap {_ _ _} _ {_} !_ /.
 
 Notation "m ≫= f" := (mbind f m) (at level 60, right associativity) : C_scope.
 Notation "( m ≫=)" := (λ f, mbind f m) (only parsing) : C_scope.
@@ -331,21 +339,22 @@ Notation "(≫= f )" := (mbind f) (only parsing) : C_scope.
 Notation "(≫=)" := (λ m f, mbind f m) (only parsing) : C_scope.
 
 Notation "x ← y ; z" := (y ≫= (λ x : _, z))
-  (at level 65, only parsing, next at level 35, right associativity) : C_scope.
+  (at level 65, next at level 35, only parsing, right associativity) : C_scope.
 Infix "<$>" := fmap (at level 60, right associativity) : C_scope.
 
 Class MGuard (M : Type → Type) :=
-  mguard: ∀ P {dec : Decision P} {A}, M A → M A.
-Notation "'guard' P ; o" := (mguard P o)
-  (at level 65, only parsing, next at level 35, right associativity) : C_scope.
-Arguments mguard _ _ _ !_ _ !_ / : simpl nomatch.
+  mguard: ∀ P {dec : Decision P} {A}, (P → M A) → M A.
+Arguments mguard _ _ _ !_ _ _ /.
+Notation "'guard' P ; o" := (mguard P (λ _, o))
+  (at level 65, next at level 35, only parsing, right associativity) : C_scope.
+Notation "'guard' P 'as' H ; o" := (mguard P (λ H, o))
+  (at level 65, next at level 35, only parsing, right associativity) : C_scope.
 
 (** ** Operations on maps *)
 (** In this section we define operational type classes for the operations
 on maps. In the file [fin_maps] we will axiomatize finite maps.
 The function look up [m !! k] should yield the element at key [k] in [m]. *)
-Class Lookup (K A M : Type) :=
-  lookup: K → M → option A.
+Class Lookup (K A M : Type) := lookup: K → M → option A.
 Instance: Params (@lookup) 4.
 
 Notation "m !! i" := (lookup i m) (at level 20) : C_scope.
@@ -356,8 +365,7 @@ Arguments lookup _ _ _ _ !_ !_ / : simpl nomatch.
 
 (** The function insert [<[k:=a]>m] should update the element at key [k] with
 value [a] in [m]. *)
-Class Insert (K A M : Type) :=
-  insert: K → A → M → M.
+Class Insert (K A M : Type) := insert: K → A → M → M.
 Instance: Params (@insert) 4.
 Notation "<[ k := a ]>" := (insert k a)
   (at level 5, right associativity, format "<[ k := a ]>") : C_scope.
@@ -366,15 +374,13 @@ Arguments insert _ _ _ _ !_ _ !_ / : simpl nomatch.
 (** The function delete [delete k m] should delete the value at key [k] in
 [m]. If the key [k] is not a member of [m], the original map should be
 returned. *)
-Class Delete (K M : Type) :=
-  delete: K → M → M.
+Class Delete (K M : Type) := delete: K → M → M.
 Instance: Params (@delete) 3.
 Arguments delete _ _ _ !_ !_ / : simpl nomatch.
 
 (** The function [alter f k m] should update the value at key [k] using the
 function [f], which is called with the original value. *)
-Class AlterD (K A M : Type) (f : A → A) :=
-  alter: K → M → M.
+Class AlterD (K A M : Type) (f : A → A) := alter: K → M → M.
 Notation Alter K A M := (∀ (f : A → A), AlterD K A M f)%type.
 Instance: Params (@alter) 5.
 Arguments alter {_ _ _} _ {_} !_ !_ / : simpl nomatch.
@@ -409,9 +415,8 @@ Definition delete_list `{Delete K M} (l : list K) (m : M) : M :=
   fold_right delete m l.
 Instance: Params (@delete_list) 3.
 
-Definition insert_consecutive `{Insert nat A M}
-    (i : nat) (l : list A) (m : M) : M :=
-  fold_right (λ x f i, <[i:=x]>(f (S i))) (λ _, m) l i.
+Definition insert_consecutive `{Insert nat A M} (i : nat) (l : list A)
+  (m : M) : M := fold_right (λ x f i, <[i:=x]>(f (S i))) (λ _, m) l i.
 Instance: Params (@insert_consecutive) 3.
 
 (** The function [union_with f m1 m2] is supposed to yield the union of [m1]
@@ -441,8 +446,11 @@ Arguments intersection_with_list _ _ _ _ _ !_ /.
 (** These operational type classes allow us to refer to common mathematical
 properties in a generic way. For example, for injectivity of [(k ++)] it
 allows us to write [injective (k ++)] instead of [app_inv_head k]. *)
-Class Injective {A B} (R : relation A) S (f : A → B) : Prop :=
-  injective: ∀ x y : A, S (f x) (f y) → R x y.
+Class Injective {A B} (R : relation A) (S : relation B) (f : A → B) : Prop :=
+  injective: ∀ x y, S (f x) (f y) → R x y.
+Class Injective2 {A B C} (R1 : relation A) (R2 : relation B)
+    (S : relation C) (f : A → B → C) : Prop :=
+  injective2: ∀ x1 x2  y1 y2, S (f x1 x2) (f y1 y2) → R1 x1 y1 ∧ R2 x2 y2.
 Class Idempotent {A} (R : relation A) (f : A → A → A) : Prop :=
   idempotent: ∀ x, R (f x x) x.
 Class Commutative {A B} (R : relation A) (f : B → B → A) : Prop :=
@@ -461,11 +469,12 @@ Class LeftDistr {A} (R : relation A) (f g : A → A → A) : Prop :=
   left_distr: ∀ x y z, R (f x (g y z)) (g (f x y) (f x z)).
 Class RightDistr {A} (R : relation A) (f g : A → A → A) : Prop :=
   right_distr: ∀ y z x, R (f (g y z) x) (g (f y x) (f z x)).
-Class AntiSymmetric {A} (R : relation A) : Prop :=
-  anti_symmetric: ∀ x y, R x y → R y x → x = y.
+Class AntiSymmetric {A} (R S : relation A) : Prop :=
+  anti_symmetric: ∀ x y, S x y → S y x → R x y.
 
 Arguments irreflexivity {_} _ {_} _ _.
 Arguments injective {_ _ _ _} _ {_} _ _ _.
+Arguments injective2 {_ _ _ _ _ _} _ {_} _ _ _ _ _.
 Arguments idempotent {_ _} _ {_} _.
 Arguments commutative {_ _ _} _ {_} _ _.
 Arguments left_id {_ _} _ _ {_} _.
@@ -475,8 +484,10 @@ Arguments left_absorb {_ _} _ _ {_} _.
 Arguments right_absorb {_ _} _ _ {_} _.
 Arguments left_distr {_ _} _ _ {_} _ _ _.
 Arguments right_distr {_ _} _ _ {_} _ _ _.
-Arguments anti_symmetric {_} _ {_} _ _ _ _.
+Arguments anti_symmetric {_ _} _ {_} _ _ _ _.
 
+Lemma impl_transitive (P Q R : Prop) : (P → Q) → (Q → R) → (P → R).
+Proof. tauto. Qed.
 Instance: Commutative (↔) (@eq A).
 Proof. red. intuition. Qed.
 Instance: Commutative (↔) (λ x y, @eq A y x).
@@ -524,34 +535,31 @@ Proof. red. intuition. Qed.
 Instance: RightDistr (↔) (∨) (∧).
 Proof. red. intuition. Qed.
 
-(** The following lemmas are more specific versions of the projections of the
-above type classes. These lemmas allow us to enforce Coq not to use the setoid
-rewriting mechanism. *)
-Lemma idempotent_eq {A} (f : A → A → A) `{!Idempotent (=) f} x :
-  f x x = x.
+(** The following lemmas are specific versions of the projections of the above
+type classes for Leibniz equality. These lemmas allow us to enforce Coq not to
+use the setoid rewriting mechanism. *)
+Lemma idempotent_L {A} (f : A → A → A) `{!Idempotent (=) f} x : f x x = x.
 Proof. auto. Qed.
-Lemma commutative_eq {A B} (f : B → B → A) `{!Commutative (=) f} x y :
+Lemma commutative_L {A B} (f : B → B → A) `{!Commutative (=) f} x y :
   f x y = f y x.
 Proof. auto. Qed.
-Lemma left_id_eq {A} (i : A) (f : A → A → A) `{!LeftId (=) i f} x :
-  f i x = x.
+Lemma left_id_L {A} (i : A) (f : A → A → A) `{!LeftId (=) i f} x : f i x = x.
 Proof. auto. Qed.
-Lemma right_id_eq {A} (i : A) (f : A → A → A) `{!RightId (=) i f} x :
-  f x i = x.
+Lemma right_id_L {A} (i : A) (f : A → A → A) `{!RightId (=) i f} x : f x i = x.
 Proof. auto. Qed.
-Lemma associative_eq {A} (f : A → A → A) `{!Associative (=) f} x y z :
+Lemma associative_L {A} (f : A → A → A) `{!Associative (=) f} x y z :
   f x (f y z) = f (f x y) z.
 Proof. auto. Qed.
-Lemma left_absorb_eq {A} (i : A) (f : A → A → A) `{!LeftAbsorb (=) i f} x :
+Lemma left_absorb_L {A} (i : A) (f : A → A → A) `{!LeftAbsorb (=) i f} x :
   f i x = i.
 Proof. auto. Qed.
-Lemma right_absorb_eq {A} (i : A) (f : A → A → A) `{!RightAbsorb (=) i f} x :
+Lemma right_absorb_L {A} (i : A) (f : A → A → A) `{!RightAbsorb (=) i f} x :
   f x i = i.
 Proof. auto. Qed.
-Lemma left_distr_eq {A} (f g : A → A → A) `{!LeftDistr (=) f g} x y z :
+Lemma left_distr_L {A} (f g : A → A → A) `{!LeftDistr (=) f g} x y z :
   f x (g y z) = g (f x y) (f x z).
 Proof. auto. Qed.
-Lemma right_distr_eq {A} (f g : A → A → A) `{!RightDistr (=) f g} y z x :
+Lemma right_distr_L {A} (f g : A → A → A) `{!RightDistr (=) f g} y z x :
   f (g y z) x = g (f y x) (f z x).
 Proof. auto. Qed.
 
@@ -561,9 +569,9 @@ Class BoundedPreOrder A `{Empty A} `{SubsetEq A} : Prop := {
   bounded_preorder :>> PreOrder (⊆);
   subseteq_empty x : ∅ ⊆ x
 }.
-Class PartialOrder A `{SubsetEq A} : Prop := {
-  po_preorder :>> PreOrder (⊆);
-  po_antisym :> AntiSymmetric (⊆)
+Class PartialOrder {A} (R : relation A) : Prop := {
+  po_preorder :> PreOrder R;
+  po_antisym :> AntiSymmetric (=) R
 }.
 
 (** We do not include equality in the following interfaces so as to avoid the
@@ -663,12 +671,10 @@ Class CollectionMonad M `{∀ A, ElemOf A (M A)}
   collection_monad_simple A :> SimpleCollection A (M A);
   elem_of_bind {A B} (f : A → M B) (X : M A) (x : B) :
     x ∈ X ≫= f ↔ ∃ y, x ∈ f y ∧ y ∈ X;
-  elem_of_ret {A} (x y : A) :
-    x ∈ mret y ↔ x = y;
+  elem_of_ret {A} (x y : A) : x ∈ mret y ↔ x = y;
   elem_of_fmap {A B} (f : A → B) (X : M A) (x : B) :
     x ∈ f <$> X ↔ ∃ y, x = f y ∧ y ∈ X;
-  elem_of_join {A} (X : M (M A)) (x : A) :
-    x ∈ mjoin X ↔ ∃ Y, x ∈ Y ∧ Y ∈ X
+  elem_of_join {A} (X : M (M A)) (x : A) : x ∈ mjoin X ↔ ∃ Y, x ∈ Y ∧ Y ∈ X
 }.
 
 (** The function [fresh X] yields an element that is not contained in [X]. We
diff --git a/theories/collections.v b/theories/collections.v
index 7d4c52dbd32858473e49a9f7ccc557269c90a4db..754efa114f759c49e0477da4f3b01f1331f80073 100644
--- a/theories/collections.v
+++ b/theories/collections.v
@@ -42,8 +42,7 @@ Section simple_collection.
   Global Instance elem_of_proper: Proper ((=) ==> (≡) ==> iff) (∈) | 5.
   Proof. intros ???. subst. firstorder. Qed.
 
-  Lemma elem_of_union_list (Xs : list C) (x : A) :
-    x ∈ ⋃ Xs ↔ ∃ X, X ∈ Xs ∧ x ∈ X.
+  Lemma elem_of_union_list Xs x : x ∈ ⋃ Xs ↔ ∃ X, X ∈ Xs ∧ x ∈ X.
   Proof.
     split.
     * induction Xs; simpl; intros HXs.
@@ -249,13 +248,11 @@ Section collection.
 
     Lemma not_elem_of_intersection x X Y : x ∉ X ∩ Y ↔ x ∉ X ∨ x ∉ Y.
     Proof.
-      rewrite elem_of_intersection.
-      destruct (decide (x ∈ X)); tauto.
+      rewrite elem_of_intersection. destruct (decide (x ∈ X)); tauto.
     Qed.
     Lemma not_elem_of_difference x X Y : x ∉ X ∖ Y ↔ x ∉ X ∨ x ∈ Y.
     Proof.
-      rewrite elem_of_difference.
-      destruct (decide (x ∈ Y)); tauto.
+      rewrite elem_of_difference. destruct (decide (x ∈ Y)); tauto.
     Qed.
     Lemma union_difference X Y : X ⊆ Y → Y ≡ X ∪ Y ∖ X.
     Proof.
@@ -303,19 +300,18 @@ Section collection_ops.
     (∀ x y z, Q x → P y → f x y = Some z → P z) →
     ∀ x, x ∈ intersection_with_list f Y Xs → P x.
   Proof.
-    intros HY HXs Hf.
-    induction Xs; simplify_option_equality; [done |].
+    intros HY HXs Hf. induction Xs; simplify_option_equality; [done |].
     intros x Hx. rewrite elem_of_intersection_with in Hx.
     decompose_Forall. destruct Hx as (? & ? & ? & ? & ?). eauto.
   Qed.
 End collection_ops.
 
 (** * Sets without duplicates up to an equivalence *)
-Section no_dup.
+Section NoDup.
   Context `{SimpleCollection A B} (R : relation A) `{!Equivalence R}.
 
   Definition elem_of_upto (x : A) (X : B) := ∃ y, y ∈ X ∧ R x y.
-  Definition no_dup (X : B) := ∀ x y, x ∈ X → y ∈ X → R x y → x = y.
+  Definition set_NoDup (X : B) := ∀ x y, x ∈ X → y ∈ X → R x y → x = y.
 
   Global Instance: Proper ((≡) ==> iff) (elem_of_upto x).
   Proof. intros ??? E. unfold elem_of_upto. by setoid_rewrite E. Qed.
@@ -325,7 +321,7 @@ Section no_dup.
     * rewrite <-E1, <-E2; intuition.
     * rewrite E1, E2; intuition.
   Qed.
-  Global Instance: Proper ((≡) ==> iff) no_dup.
+  Global Instance: Proper ((≡) ==> iff) set_NoDup.
   Proof. firstorder. Qed.
 
   Lemma elem_of_upto_elem_of x X : x ∈ X → elem_of_upto x X.
@@ -341,60 +337,63 @@ Section no_dup.
   Lemma not_elem_of_upto x X : ¬elem_of_upto x X → ∀ y, y ∈ X → ¬R x y.
   Proof. unfold elem_of_upto. esolve_elem_of. Qed.
 
-  Lemma no_dup_empty: no_dup ∅.
-  Proof. unfold no_dup. solve_elem_of. Qed.
-  Lemma no_dup_add x X : ¬elem_of_upto x X → no_dup X → no_dup ({[ x ]} ∪ X).
-  Proof. unfold no_dup, elem_of_upto. esolve_elem_of. Qed.
-  Lemma no_dup_inv_add x X : x ∉ X → no_dup ({[ x ]} ∪ X) → ¬elem_of_upto x X.
+  Lemma set_NoDup_empty: set_NoDup ∅.
+  Proof. unfold set_NoDup. solve_elem_of. Qed.
+  Lemma set_NoDup_add x X :
+    ¬elem_of_upto x X → set_NoDup X → set_NoDup ({[ x ]} ∪ X).
+  Proof. unfold set_NoDup, elem_of_upto. esolve_elem_of. Qed.
+  Lemma set_NoDup_inv_add x X :
+    x ∉ X → set_NoDup ({[ x ]} ∪ X) → ¬elem_of_upto x X.
   Proof.
     intros Hin Hnodup [y [??]].
     rewrite (Hnodup x y) in Hin; solve_elem_of.
   Qed.
-  Lemma no_dup_inv_union_l X Y : no_dup (X ∪ Y) → no_dup X.
-  Proof. unfold no_dup. solve_elem_of. Qed.
-  Lemma no_dup_inv_union_r X Y : no_dup (X ∪ Y) → no_dup Y.
-  Proof. unfold no_dup. solve_elem_of. Qed.
-End no_dup.
+  Lemma set_NoDup_inv_union_l X Y : set_NoDup (X ∪ Y) → set_NoDup X.
+  Proof. unfold set_NoDup. solve_elem_of. Qed.
+  Lemma set_NoDup_inv_union_r X Y : set_NoDup (X ∪ Y) → set_NoDup Y.
+  Proof. unfold set_NoDup. solve_elem_of. Qed.
+End NoDup.
 
 (** * Quantifiers *)
 Section quantifiers.
   Context `{SimpleCollection A B} (P : A → Prop).
 
-  Definition cforall X := ∀ x, x ∈ X → P x.
-  Definition cexists X := ∃ x, x ∈ X ∧ P x.
-
-  Lemma cforall_empty : cforall ∅.
-  Proof. unfold cforall. solve_elem_of. Qed.
-  Lemma cforall_singleton x : cforall {[ x ]} ↔ P x.
-  Proof. unfold cforall. solve_elem_of. Qed.
-  Lemma cforall_union X Y : cforall X → cforall Y → cforall (X ∪ Y).
-  Proof. unfold cforall. solve_elem_of. Qed.
-  Lemma cforall_union_inv_1 X Y : cforall (X ∪ Y) → cforall X.
-  Proof. unfold cforall. solve_elem_of. Qed.
-  Lemma cforall_union_inv_2 X Y : cforall (X ∪ Y) → cforall Y.
-  Proof. unfold cforall. solve_elem_of. Qed.
-
-  Lemma cexists_empty : ¬cexists ∅.
-  Proof. unfold cexists. esolve_elem_of. Qed.
-  Lemma cexists_singleton x : cexists {[ x ]} ↔ P x.
-  Proof. unfold cexists. esolve_elem_of. Qed.
-  Lemma cexists_union_1 X Y : cexists X → cexists (X ∪ Y).
-  Proof. unfold cexists. esolve_elem_of. Qed.
-  Lemma cexists_union_2 X Y : cexists Y → cexists (X ∪ Y).
-  Proof. unfold cexists. esolve_elem_of. Qed.
-  Lemma cexists_union_inv X Y : cexists (X ∪ Y) → cexists X ∨ cexists Y.
-  Proof. unfold cexists. esolve_elem_of. Qed.
+  Definition set_Forall X := ∀ x, x ∈ X → P x.
+  Definition set_Exists X := ∃ x, x ∈ X ∧ P x.
+
+  Lemma set_Forall_empty : set_Forall ∅.
+  Proof. unfold set_Forall. solve_elem_of. Qed.
+  Lemma set_Forall_singleton x : set_Forall {[ x ]} ↔ P x.
+  Proof. unfold set_Forall. solve_elem_of. Qed.
+  Lemma set_Forall_union X Y : set_Forall X → set_Forall Y → set_Forall (X ∪ Y).
+  Proof. unfold set_Forall. solve_elem_of. Qed.
+  Lemma set_Forall_union_inv_1 X Y : set_Forall (X ∪ Y) → set_Forall X.
+  Proof. unfold set_Forall. solve_elem_of. Qed.
+  Lemma set_Forall_union_inv_2 X Y : set_Forall (X ∪ Y) → set_Forall Y.
+  Proof. unfold set_Forall. solve_elem_of. Qed.
+
+  Lemma set_Exists_empty : ¬set_Exists ∅.
+  Proof. unfold set_Exists. esolve_elem_of. Qed.
+  Lemma set_Exists_singleton x : set_Exists {[ x ]} ↔ P x.
+  Proof. unfold set_Exists. esolve_elem_of. Qed.
+  Lemma set_Exists_union_1 X Y : set_Exists X → set_Exists (X ∪ Y).
+  Proof. unfold set_Exists. esolve_elem_of. Qed.
+  Lemma set_Exists_union_2 X Y : set_Exists Y → set_Exists (X ∪ Y).
+  Proof. unfold set_Exists. esolve_elem_of. Qed.
+  Lemma set_Exists_union_inv X Y :
+    set_Exists (X ∪ Y) → set_Exists X ∨ set_Exists Y.
+  Proof. unfold set_Exists. esolve_elem_of. Qed.
 End quantifiers.
 
 Section more_quantifiers.
   Context `{Collection A B}.
 
-  Lemma cforall_weaken (P Q : A → Prop) (Hweaken : ∀ x, P x → Q x) X :
-    cforall P X → cforall Q X.
-  Proof. unfold cforall. naive_solver. Qed.
-  Lemma cexists_weaken (P Q : A → Prop) (Hweaken : ∀ x, P x → Q x) X :
-    cexists P X → cexists Q X.
-  Proof. unfold cexists. naive_solver. Qed.
+  Lemma set_Forall_weaken (P Q : A → Prop) (Hweaken : ∀ x, P x → Q x) X :
+    set_Forall P X → set_Forall Q X.
+  Proof. unfold set_Forall. naive_solver. Qed.
+  Lemma set_Exists_weaken (P Q : A → Prop) (Hweaken : ∀ x, P x → Q x) X :
+    set_Exists P X → set_Exists Q X.
+  Proof. unfold set_Exists. naive_solver. Qed.
 End more_quantifiers.
 
 (** * Fresh elements *)
@@ -417,8 +416,7 @@ Section fresh.
 
   Global Instance fresh_list_proper: Proper ((=) ==> (≡) ==> (=)) fresh_list.
   Proof.
-    intros ? n ?. subst.
-    induction n; simpl; intros ?? E; f_equal.
+    intros ? n ?. subst. induction n; simpl; intros ?? E; f_equal.
     * by rewrite E.
     * apply IHn. by rewrite E.
   Qed.
@@ -437,10 +435,8 @@ Section fresh.
 
   Lemma fresh_list_nodup n X : NoDup (fresh_list n X).
   Proof.
-    revert X.
-    induction n; simpl; constructor; auto.
-    intros Hin. apply fresh_list_is_fresh in Hin.
-    solve_elem_of.
+    revert X. induction n; simpl; constructor; auto.
+    intros Hin. apply fresh_list_is_fresh in Hin. solve_elem_of.
   Qed.
 End fresh.
 
@@ -455,7 +451,10 @@ Section collection_monad.
   Context `{CollectionMonad M}.
 
   Global Instance collection_guard: MGuard M := λ P dec A x,
-    if dec then x else ∅.
+    match dec with
+    | left H => x H
+    | _ => ∅
+    end.
 
   Global Instance collection_fmap_proper {A B} (f : A → B) :
     Proper ((≡) ==> (≡)) (fmap f).
@@ -495,8 +494,7 @@ Section collection_monad.
   Proof. revert l; induction k; esolve_elem_of. Qed.
 
   Lemma elem_of_mapM_fmap {A B} (f : A → B) (g : B → M A) l k :
-    Forall (λ x, ∀ y, y ∈ g x → f y = x) l →
-    k ∈ mapM g l → fmap f k = l.
+    Forall (λ x, ∀ y, y ∈ g x → f y = x) l → k ∈ mapM g l → fmap f k = l.
   Proof.
     intros Hl. revert k.
     induction Hl; simpl; intros;
@@ -504,14 +502,10 @@ Section collection_monad.
   Qed.
 
   Lemma elem_of_mapM_Forall {A B} (f : A → M B) (P : B → Prop) l k :
-    l ∈ mapM f k →
-    Forall (λ x, ∀ y, y ∈ f x → P y) k →
-    Forall P l.
+    l ∈ mapM f k → Forall (λ x, ∀ y, y ∈ f x → P y) k → Forall P l.
   Proof. rewrite elem_of_mapM. apply Forall2_Forall_l. Qed.
-  Lemma elem_of_mapM_Forall2_l {A B C} (f : A → M B)
-      (P : B → C → Prop) l1 l2 k :
-    l1 ∈ mapM f k →
-    Forall2 (λ x y, ∀ z, z ∈ f x → P z y) k l2 →
+  Lemma elem_of_mapM_Forall2_l {A B C} (f : A → M B) (P: B → C → Prop) l1 l2 k :
+    l1 ∈ mapM f k → Forall2 (λ x y, ∀ z, z ∈ f x → P z y) k l2 →
     Forall2 P l1 l2.
   Proof.
     rewrite elem_of_mapM. intros Hl1. revert l2.
diff --git a/theories/decidable.v b/theories/decidable.v
index ecce3512505be6a5fba14e48fd9b85a4d51307ee..25de0bf4e960f60669d3d54e53688950739a887c 100644
--- a/theories/decidable.v
+++ b/theories/decidable.v
@@ -76,6 +76,7 @@ Notation cast_if_and3 S1 S2 S3 := (if S1 then cast_if_and S2 S3 else right _).
 Notation cast_if_and4 S1 S2 S3 S4 :=
   (if S1 then cast_if_and3 S2 S3 S4 else right _).
 Notation cast_if_or S1 S2 := (if S1 then left _ else cast_if S2).
+Notation cast_if_or3 S1 S2 S3 := (if S1 then left _ else cast_if_or S2 S3).
 Notation cast_if_not_or S1 S2 := (if S1 then cast_if S2 else left _).
 Notation cast_if_not S := (if S then right _ else left _).
 
diff --git a/theories/fin_collections.v b/theories/fin_collections.v
index 469696128a9750806f3c1ce484ff934889ca8363..1cfb0a473b717b7cdf51cc8563041758b4f5a273 100644
--- a/theories/fin_collections.v
+++ b/theories/fin_collections.v
@@ -13,7 +13,7 @@ Definition collection_fold `{Elements A C} {B}
 Section fin_collection.
 Context `{FinCollection A C}.
 
-Global Instance elements_proper: Proper ((≡) ==> Permutation) elements.
+Global Instance elements_proper: Proper ((≡) ==> (≡ₚ)) elements.
 Proof.
   intros ?? E. apply NoDup_Permutation.
   * apply elements_nodup.
@@ -176,10 +176,8 @@ Proof.
     apply Hadd. solve_elem_of. apply IH. esolve_elem_of.
 Qed.
 
-Lemma collection_fold_proper {B} (R : relation B)
-    `{!Equivalence R}
-    (f : A → B → B) (b : B)
-    `{!Proper ((=) ==> R ==> R) f}
+Lemma collection_fold_proper {B} (R : relation B) `{!Equivalence R}
+    (f : A → B → B) (b : B) `{!Proper ((=) ==> R ==> R) f}
     (Hf : ∀ a1 a2 b, R (f a1 (f a2 b)) (f a2 (f a1 b))) :
   Proper ((≡) ==> R) (collection_fold f b).
 Proof.
@@ -188,22 +186,22 @@ Proof.
   * by rewrite E.
 Qed.
 
-Global Instance cforall_dec `(P : A → Prop)
-  `{∀ x, Decision (P x)} X : Decision (cforall P X) | 100.
+Global Instance set_Forall_dec `(P : A → Prop)
+  `{∀ x, Decision (P x)} X : Decision (set_Forall P X) | 100.
 Proof.
   refine (cast_if (decide (Forall P (elements X))));
-    abstract (unfold cforall; setoid_rewrite elements_spec;
+    abstract (unfold set_Forall; setoid_rewrite elements_spec;
       by rewrite <-Forall_forall).
 Defined.
 
-Global Instance cexists_dec `(P : A → Prop) `{∀ x, Decision (P x)} X :
-  Decision (cexists P X) | 100.
+Global Instance set_Exists_dec `(P : A → Prop) `{∀ x, Decision (P x)} X :
+  Decision (set_Exists P X) | 100.
 Proof.
   refine (cast_if (decide (Exists P (elements X))));
-    abstract (unfold cexists; setoid_rewrite elements_spec;
+    abstract (unfold set_Exists; setoid_rewrite elements_spec;
       by rewrite <-Exists_exists).
 Defined.
 
 Global Instance rel_elem_of_dec `{∀ x y, Decision (R x y)} x X :
-  Decision (elem_of_upto R x X) | 100 := decide (cexists (R x) X).
+  Decision (elem_of_upto R x X) | 100 := decide (set_Exists (R x) X).
 End fin_collection.
diff --git a/theories/fin_map_dom.v b/theories/fin_map_dom.v
index 2ca9a5836e1127e08d14b8d0fe0276fb0b3c2725..78ee121d325006c5861d64de53500526d9cdbcd2 100644
--- a/theories/fin_map_dom.v
+++ b/theories/fin_map_dom.v
@@ -6,46 +6,34 @@ function in a generic way, to allow more efficient implementations. *)
 Require Export collections fin_maps.
 
 Class FinMapDom K M D `{!FMap M}
-    `{∀ A, Lookup K A (M A)}
-    `{∀ A, Empty (M A)}
-    `{∀ A, PartialAlter K A (M A)}
-    `{!Merge M}
-    `{∀ A, FinMapToList K A (M A)}
+    `{∀ A, Lookup K A (M A)} `{∀ A, Empty (M A)} `{∀ A, PartialAlter K A (M A)}
+    `{!Merge M} `{∀ A, FinMapToList K A (M A)}
     `{∀ i j : K, Decision (i = j)}
-    `{∀ A, Dom (M A) D}
-    `{ElemOf K D}
-    `{Empty D}
-    `{Singleton K D}
-    `{Union D}
-    `{Intersection D}
-    `{Difference D} := {
+    `{∀ A, Dom (M A) D} `{ElemOf K D} `{Empty D} `{Singleton K D}
+    `{Union D}`{Intersection D} `{Difference D} := {
   finmap_dom_map :>> FinMap K M;
   finmap_dom_collection :>> Collection K D;
   elem_of_dom {A} (m : M A) i : i ∈ dom D m ↔ is_Some (m !! i)
 }.
 
-Section theorems.
+Section fin_map_dom.
 Context `{FinMapDom K M D}.
 
-Lemma not_elem_of_dom {A} (m : M A) i :
-  i ∉ dom D m ↔ m !! i = None.
+Lemma not_elem_of_dom {A} (m : M A) i : i ∉ dom D m ↔ m !! i = None.
 Proof. by rewrite elem_of_dom, eq_None_not_Some. Qed.
 
-Lemma subseteq_dom {A} (m1 m2 : M A) :
-  m1 ⊆ m2 → dom D m1 ⊆ dom D m2.
+Lemma subseteq_dom {A} (m1 m2 : M A) : m1 ⊆ m2 → dom D m1 ⊆ dom D m2.
 Proof.
   unfold subseteq, map_subseteq, collection_subseteq.
   intros ??. rewrite !elem_of_dom. inversion 1. eauto.
 Qed.
-Lemma subset_dom {A} (m1 m2 : M A) :
-  m1 ⊂ m2 → dom D m1 ⊂ dom D m2.
+Lemma subset_dom {A} (m1 m2 : M A) : m1 ⊂ m2 → dom D m1 ⊂ dom D m2.
 Proof.
   intros [Hss1 Hss2]. split.
   { by apply subseteq_dom. }
   intros Hdom. destruct Hss2. intros i x Hi.
   specialize (Hdom i). rewrite !elem_of_dom in Hdom.
-  feed inversion Hdom. eauto.
-  by erewrite (Hss1 i) in Hi by eauto.
+  feed inversion Hdom. eauto. by erewrite (Hss1 i) in Hi by eauto.
 Qed.
 
 Lemma dom_empty {A} : dom D (@empty (M A) _) ≡ ∅.
@@ -54,38 +42,32 @@ Proof.
   * rewrite elem_of_dom, lookup_empty. by inversion 1.
   * solve_elem_of.
 Qed.
-Lemma dom_empty_inv {A} (m : M A) :
-  dom D m ≡ ∅ → m = ∅.
+Lemma dom_empty_inv {A} (m : M A) : dom D m ≡ ∅ → m = ∅.
 Proof.
   intros E. apply map_empty. intros. apply not_elem_of_dom.
   rewrite E. solve_elem_of.
 Qed.
 
-Lemma dom_insert {A} (m : M A) i x :
-  dom D (<[i:=x]>m) ≡ {[ i ]} ∪ dom D m.
+Lemma dom_insert {A} (m : M A) i x : dom D (<[i:=x]>m) ≡ {[ i ]} ∪ dom D m.
 Proof.
   apply elem_of_equiv. intros j.
   rewrite elem_of_union, !elem_of_dom, !is_Some_alt.
   setoid_rewrite lookup_insert_Some.
   destruct (decide (i = j)); esolve_elem_of.
 Qed.
-Lemma dom_insert_subseteq {A} (m : M A) i x :
-  dom D m ⊆ dom D (<[i:=x]>m).
+Lemma dom_insert_subseteq {A} (m : M A) i x : dom D m ⊆ dom D (<[i:=x]>m).
 Proof. rewrite (dom_insert _). solve_elem_of. Qed.
 Lemma dom_insert_subseteq_compat_l {A} (m : M A) i x X :
-  X ⊆ dom D m →
-  X ⊆ dom D (<[i:=x]>m).
+  X ⊆ dom D m → X ⊆ dom D (<[i:=x]>m).
 Proof. intros. transitivity (dom D m); eauto using dom_insert_subseteq. Qed.
 
-Lemma dom_singleton {A} (i : K) (x : A) :
-  dom D {[(i, x)]} ≡ {[ i ]}.
+Lemma dom_singleton {A} (i : K) (x : A) : dom D {[(i, x)]} ≡ {[ i ]}.
 Proof.
   unfold singleton at 1, map_singleton.
   rewrite dom_insert, dom_empty. solve_elem_of.
 Qed.
 
-Lemma dom_delete {A} (m : M A) i :
-  dom D (delete i m) ≡ dom D m ∖ {[ i ]}.
+Lemma dom_delete {A} (m : M A) i : dom D (delete i m) ≡ dom D m ∖ {[ i ]}.
 Proof.
   apply elem_of_equiv. intros j.
   rewrite elem_of_difference, !elem_of_dom, !is_Some_alt.
@@ -99,27 +81,22 @@ Lemma delete_insert_dom {A} (m : M A) i x :
   i ∉ dom D m → delete i (<[i:=x]>m) = m.
 Proof. rewrite not_elem_of_dom. apply delete_insert. Qed.
 
-Lemma map_disjoint_dom {A} (m1 m2 : M A) :
-  m1 ⊥ m2 ↔ dom D m1 ∩ dom D m2 ≡ ∅.
+Lemma map_disjoint_dom {A} (m1 m2 : M A) : m1 ⊥ m2 ↔ dom D m1 ∩ dom D m2 ≡ ∅.
 Proof.
   unfold disjoint, map_disjoint, map_intersection_forall.
   rewrite elem_of_equiv_empty. setoid_rewrite elem_of_intersection.
   setoid_rewrite elem_of_dom. setoid_rewrite is_Some_alt. naive_solver.
 Qed.
-Lemma map_disjoint_dom_1 {A} (m1 m2 : M A) :
-  m1 ⊥ m2 → dom D m1 ∩ dom D m2 ≡ ∅.
+Lemma map_disjoint_dom_1 {A} (m1 m2 : M A) : m1 ⊥ m2 → dom D m1 ∩ dom D m2 ≡ ∅.
 Proof. apply map_disjoint_dom. Qed.
-Lemma map_disjoint_dom_2 {A} (m1 m2 : M A) :
-  dom D m1 ∩ dom D m2 ≡ ∅ → m1 ⊥ m2.
+Lemma map_disjoint_dom_2 {A} (m1 m2 : M A) : dom D m1 ∩ dom D m2 ≡ ∅ → m1 ⊥ m2.
 Proof. apply map_disjoint_dom. Qed.
 
-Lemma dom_union {A} (m1 m2 : M A) :
-  dom D (m1 ∪ m2) ≡ dom D m1 ∪ dom D m2.
+Lemma dom_union {A} (m1 m2 : M A) : dom D (m1 ∪ m2) ≡ dom D m1 ∪ dom D m2.
 Proof.
   apply elem_of_equiv. intros i.
   rewrite elem_of_union, !elem_of_dom, !is_Some_alt.
-  setoid_rewrite lookup_union_Some_raw.
-  destruct (m1 !! i); naive_solver.
+  setoid_rewrite lookup_union_Some_raw. destruct (m1 !! i); naive_solver.
 Qed.
 
 Lemma dom_intersection {A} (m1 m2 : M A) :
@@ -131,12 +108,10 @@ Proof.
   setoid_rewrite is_Some_alt. naive_solver.
 Qed.
 
-Lemma dom_difference {A} (m1 m2 : M A) :
-  dom D (m1 ∖ m2) ≡ dom D m1 ∖ dom D m2.
+Lemma dom_difference {A} (m1 m2 : M A) : dom D (m1 ∖ m2) ≡ dom D m1 ∖ dom D m2.
 Proof.
   apply elem_of_equiv. intros i.
   rewrite elem_of_difference, !elem_of_dom, !is_Some_alt.
-  setoid_rewrite lookup_difference_Some.
-  destruct (m2 !! i); naive_solver.
+  setoid_rewrite lookup_difference_Some. destruct (m2 !! i); naive_solver.
 Qed.
-End theorems.
+End fin_map_dom.
diff --git a/theories/fin_maps.v b/theories/fin_maps.v
index 5c4acc4e76632214cc8a0647fe4cb1a056f6acbe..0eb686e509247168962924302a09962807b371a9 100644
--- a/theories/fin_maps.v
+++ b/theories/fin_maps.v
@@ -26,24 +26,17 @@ which enables us to give a generic implementation of [union_with],
 Class FinMapToList K A M := map_to_list: M → list (K * A).
 
 Class FinMap K M `{!FMap M}
-    `{∀ A, Lookup K A (M A)}
-    `{∀ A, Empty (M A)}
-    `{∀ A, PartialAlter K A (M A)}
-    `{!Merge M}
-    `{∀ A, FinMapToList K A (M A)}
+    `{∀ A, Lookup K A (M A)} `{∀ A, Empty (M A)} `{∀ A, PartialAlter K A (M A)}
+    `{!Merge M} `{∀ A, FinMapToList K A (M A)}
     `{∀ i j : K, Decision (i = j)} := {
-  map_eq {A} (m1 m2 : M A) :
-    (∀ i, m1 !! i = m2 !! i) → m1 = m2;
-  lookup_empty {A} i :
-    (∅ : M A) !! i = None;
+  map_eq {A} (m1 m2 : M A) : (∀ i, m1 !! i = m2 !! i) → m1 = m2;
+  lookup_empty {A} i : (∅ : M A) !! i = None;
   lookup_partial_alter {A} f (m : M A) i :
     partial_alter f i m !! i = f (m !! i);
   lookup_partial_alter_ne {A} f (m : M A) i j :
     i ≠ j → partial_alter f i m !! j = m !! j;
-  lookup_fmap {A B} (f : A → B) (m : M A) i :
-    (f <$> m) !! i = f <$> m !! i;
-  map_to_list_nodup {A} (m : M A) :
-    NoDup (map_to_list m);
+  lookup_fmap {A B} (f : A → B) (m : M A) i : (f <$> m) !! i = f <$> m !! i;
+  map_to_list_nodup {A} (m : M A) : NoDup (map_to_list m);
   elem_of_map_to_list {A} (m : M A) i x :
     (i,x) ∈ map_to_list m ↔ m !! i = Some x;
   lookup_merge {A B C} (f : option A → option B → option C)
@@ -89,8 +82,7 @@ Instance map_disjoint `{∀ A, Lookup K A (M A)} : Disjoint (M A) :=
 that are disjoint. However, as working with partial functions is inconvenient
 in Coq, we define the union as a total function. In case both finite maps
 have a value at the same index, we take the value of the first map. *)
-Instance map_union `{Merge M} {A} : Union (M A) :=
-  union_with (λ x _, Some x).
+Instance map_union `{Merge M} {A} : Union (M A) := union_with (λ x _, Some x).
 Instance map_intersection `{Merge M} {A} : Intersection (M A) :=
   intersection_with (λ x _, Some x).
 
@@ -107,7 +99,7 @@ Global Instance map_subseteq {A} : SubsetEq (M A) := λ m1 m2,
   ∀ i x, m1 !! i = Some x → m2 !! i = Some x.
 Global Instance: BoundedPreOrder (M A).
 Proof. split; [firstorder |]. intros m i x. by rewrite lookup_empty. Qed.
-Global Instance : PartialOrder (M A).
+Global Instance : PartialOrder (@subseteq (M A) _).
 Proof.
   split; [apply _ |].
   intros ????. apply map_eq. intros i. apply option_eq. naive_solver.
@@ -127,14 +119,8 @@ Proof.
 Qed.
 
 Lemma lookup_weaken_inv {A} (m1 m2 : M A) i x y :
-  m1 !! i = Some x →
-  m1 ⊆ m2 →
-  m2 !! i = Some y →
-  x = y.
-Proof.
-  intros Hm1 ? Hm2. eapply lookup_weaken in Hm1; eauto.
-  congruence.
-Qed.
+  m1 !! i = Some x → m1 ⊆ m2 → m2 !! i = Some y → x = y.
+Proof. intros Hm1 ? Hm2. eapply lookup_weaken in Hm1; eauto. congruence. Qed.
 
 Lemma lookup_ne {A} (m : M A) i j : m !! i ≠ m !! j → i ≠ j.
 Proof. congruence. Qed.
@@ -157,12 +143,10 @@ Proof.
   * intros. by rewrite !lookup_partial_alter_ne.
 Qed.
 Lemma partial_alter_commute {A} (m : M A) i j f g :
-  i ≠ j →
-  partial_alter f i (partial_alter g j m) =
+  i ≠ j → partial_alter f i (partial_alter g j m) =
     partial_alter g j (partial_alter f i m).
 Proof.
-  intros. apply map_eq. intros jj.
-  destruct (decide (jj = j)).
+  intros. apply map_eq. intros jj. destruct (decide (jj = j)).
   * subst. by rewrite lookup_partial_alter_ne,
       !lookup_partial_alter, lookup_partial_alter_ne.
   * destruct (decide (jj = i)).
@@ -173,25 +157,18 @@ Qed.
 Lemma partial_alter_self_alt {A} (m : M A) i x :
   x = m !! i → partial_alter (λ _, x) i m = m.
 Proof.
-  intros. apply map_eq. intros ii.
-  destruct (decide (i = ii)).
+  intros. apply map_eq. intros ii. destruct (decide (i = ii)).
   * subst. by rewrite lookup_partial_alter.
   * by rewrite lookup_partial_alter_ne.
 Qed.
-Lemma partial_alter_self {A} (m : M A) i :
-  partial_alter (λ _, m !! i) i m = m.
+Lemma partial_alter_self {A} (m : M A) i : partial_alter (λ _, m !! i) i m = m.
 Proof. by apply partial_alter_self_alt. Qed.
 
 Lemma partial_alter_subseteq {A} (m : M A) i f :
-  m !! i = None →
-  m ⊆ partial_alter f i m.
-Proof.
-  intros Hi j x Hj. rewrite lookup_partial_alter_ne; congruence.
-Qed.
+  m !! i = None → m ⊆ partial_alter f i m.
+Proof. intros Hi j x Hj. rewrite lookup_partial_alter_ne; congruence. Qed.
 Lemma partial_alter_subset {A} (m : M A) i f :
-  m !! i = None →
-  is_Some (f (m !! i)) →
-  m ⊂ partial_alter f i m.
+  m !! i = None → is_Some (f (m !! i)) → m ⊂ partial_alter f i m.
 Proof.
   intros Hi Hfi. split.
   * by apply partial_alter_subseteq.
@@ -201,11 +178,9 @@ Proof.
 Qed.
 
 (** ** Properties of the [alter] operation *)
-Lemma lookup_alter {A} (f : A → A) m i :
-  alter f i m !! i = f <$> m !! i.
+Lemma lookup_alter {A} (f : A → A) m i : alter f i m !! i = f <$> m !! i.
 Proof. apply lookup_partial_alter. Qed.
-Lemma lookup_alter_ne {A} (f : A → A) m i j :
-  i ≠ j → alter f i m !! j = m !! j.
+Lemma lookup_alter_ne {A} (f : A → A) m i j : i ≠ j → alter f i m !! j = m !! j.
 Proof. apply lookup_partial_alter_ne. Qed.
 
 Lemma lookup_alter_Some {A} (f : A → A) m i j y :
@@ -224,8 +199,7 @@ Proof.
   * by rewrite lookup_alter_ne.
 Qed.
 
-Lemma alter_None {A} (f : A → A) m i :
-  m !! i = None → alter f i m = m.
+Lemma alter_None {A} (f : A → A) m i : m !! i = None → alter f i m = m.
 Proof.
   intros Hi. apply map_eq. intros j. destruct (decide (i = j)); subst.
   * by rewrite lookup_alter, !Hi.
@@ -256,7 +230,7 @@ Qed.
 
 Lemma delete_empty {A} i : delete i (∅ : M A) = ∅.
 Proof. rewrite <-(partial_alter_self ∅) at 2. by rewrite lookup_empty. Qed.
-Lemma delete_singleton {A} i (x : A) : delete i {[(i, x)]} = ∅.
+Lemma delete_singleton {A} i (x : A) : delete i {[i, x]} = ∅.
 Proof. setoid_rewrite <-partial_alter_compose. apply delete_empty. Qed.
 Lemma delete_commute {A} (m : M A) i j :
   delete i (delete j m) = delete j (delete i m).
@@ -265,11 +239,9 @@ Lemma delete_insert_ne {A} (m : M A) i j x :
   i ≠ j → delete i (<[j:=x]>m) = <[j:=x]>(delete i m).
 Proof. intro. by apply partial_alter_commute. Qed.
 
-Lemma delete_notin {A} (m : M A) i :
-  m !! i = None → delete i m = m.
+Lemma delete_notin {A} (m : M A) i : m !! i = None → delete i m = m.
 Proof.
-  intros. apply map_eq. intros j.
-  destruct (decide (i = j)).
+  intros. apply map_eq. intros j. destruct (decide (i = j)).
   * subst. by rewrite lookup_delete.
   * by apply lookup_delete_ne.
 Qed.
@@ -291,35 +263,27 @@ Proof.
   by apply partial_alter_self_alt.
 Qed.
 
-Lemma delete_subseteq {A} (m : M A) i :
-  delete i m ⊆ m.
+Lemma delete_subseteq {A} (m : M A) i : delete i m ⊆ m.
 Proof. intros j x. rewrite lookup_delete_Some. tauto. Qed.
 Lemma delete_subseteq_compat {A} (m1 m2 : M A) i :
-  m1 ⊆ m2 →
-  delete i m1 ⊆ delete i m2.
+  m1 ⊆ m2 → delete i m1 ⊆ delete i m2.
 Proof. intros ? j x. rewrite !lookup_delete_Some. intuition eauto. Qed.
-Lemma delete_subset_alt {A} (m : M A) i x :
-  m !! i = Some x →
-  delete i m ⊂ m.
+Lemma delete_subset_alt {A} (m : M A) i x : m !! i = Some x → delete i m ⊂ m.
 Proof.
   split.
   * apply delete_subseteq.
   * intros Hi. apply (None_ne_Some x).
     by rewrite <-(lookup_delete m i), (Hi i x).
 Qed.
-Lemma delete_subset {A} (m : M A) i :
-  is_Some (m !! i) →
-  delete i m ⊂ m.
+Lemma delete_subset {A} (m : M A) i : is_Some (m !! i) → delete i m ⊂ m.
 Proof. inversion 1. eauto using delete_subset_alt. Qed.
 
 (** ** Properties of the [insert] operation *)
 Lemma lookup_insert {A} (m : M A) i x : <[i:=x]>m !! i = Some x.
 Proof. unfold insert. apply lookup_partial_alter. Qed.
-Lemma lookup_insert_rev {A}  (m : M A) i x y :
-  <[i:=x]>m !! i = Some y → x = y.
+Lemma lookup_insert_rev {A}  (m : M A) i x y : <[i:=x]>m !! i = Some y → x = y.
 Proof. rewrite lookup_insert. congruence. Qed.
-Lemma lookup_insert_ne {A} (m : M A) i j x :
-  i ≠ j → <[i:=x]>m !! j = m !! j.
+Lemma lookup_insert_ne {A} (m : M A) i j x : i ≠ j → <[i:=x]>m !! j = m !! j.
 Proof. unfold insert. apply lookup_partial_alter_ne. Qed.
 Lemma insert_commute {A} (m : M A) i j x y :
   i ≠ j → <[i:=x]>(<[j:=y]>m) = <[j:=y]>(<[i:=x]>m).
@@ -344,18 +308,12 @@ Proof.
   * intros [??]. by rewrite lookup_insert_ne.
 Qed.
 
-Lemma insert_subseteq {A} (m : M A) i x :
-  m !! i = None →
-  m ⊆ <[i:=x]>m.
+Lemma insert_subseteq {A} (m : M A) i x : m !! i = None → m ⊆ <[i:=x]>m.
 Proof. apply partial_alter_subseteq. Qed.
-Lemma insert_subset {A} (m : M A) i x :
-  m !! i = None →
-  m ⊂ <[i:=x]>m.
+Lemma insert_subset {A} (m : M A) i x : m !! i = None → m ⊂ <[i:=x]>m.
 Proof. intro. apply partial_alter_subset; eauto. Qed.
 Lemma insert_subseteq_r {A} (m1 m2 : M A) i x :
-  m1 !! i = None →
-  m1 ⊆ m2 →
-  m1 ⊆ <[i:=x]>m2.
+  m1 !! i = None → m1 ⊆ m2 → m1 ⊆ <[i:=x]>m2.
 Proof.
   intros ?? j ?. destruct (decide (j = i)); subst.
   * congruence.
@@ -363,9 +321,7 @@ Proof.
 Qed.
 
 Lemma insert_delete_subseteq {A} (m1 m2 : M A) i x :
-  m1 !! i = None →
-  <[i:=x]> m1 ⊆ m2 →
-  m1 ⊆ delete i m2.
+  m1 !! i = None → <[i:=x]> m1 ⊆ m2 → m1 ⊆ delete i m2.
 Proof.
   intros Hi Hix j y Hj. destruct (decide (i = j)); subst.
   * congruence.
@@ -373,20 +329,15 @@ Proof.
     by rewrite lookup_insert_ne by done.
 Qed.
 Lemma delete_insert_subseteq {A} (m1 m2 : M A) i x :
-  m1 !! i = Some x →
-  delete i m1 ⊆ m2 →
-  m1 ⊆ <[i:=x]> m2.
+  m1 !! i = Some x → delete i m1 ⊆ m2 → m1 ⊆ <[i:=x]> m2.
 Proof.
   intros Hix Hi j y Hj. destruct (decide (i = j)); subst.
   * rewrite lookup_insert. congruence.
-  * rewrite lookup_insert_ne by done. apply Hi.
-    by rewrite lookup_delete_ne.
+  * rewrite lookup_insert_ne by done. apply Hi. by rewrite lookup_delete_ne.
 Qed.
 
 Lemma insert_delete_subset {A} (m1 m2 : M A) i x :
-  m1 !! i = None →
-  <[i:=x]> m1 ⊂ m2 →
-  m1 ⊂ delete i m2.
+  m1 !! i = None → <[i:=x]> m1 ⊂ m2 → m1 ⊂ delete i m2.
 Proof.
   intros ? [Hm12 Hm21]. split.
   * eauto using insert_delete_subseteq.
@@ -395,13 +346,11 @@ Proof.
 Qed.
 
 Lemma insert_subset_inv {A} (m1 m2 : M A) i x :
-  m1 !! i = None →
-  <[i:=x]> m1 ⊂ m2 →
+  m1 !! i = None → <[i:=x]> m1 ⊂ m2 →
   ∃ m2', m2 = <[i:=x]>m2' ∧ m1 ⊂ m2' ∧ m2' !! i = None.
 Proof.
   intros Hi Hm1m2. exists (delete i m2). split_ands.
-  * rewrite insert_delete. done.
-    eapply lookup_weaken, subset_subseteq; eauto.
+  * rewrite insert_delete. done. eapply lookup_weaken, subset_subseteq; eauto.
     by rewrite lookup_insert.
   * eauto using insert_delete_subset.
   * by rewrite lookup_delete.
@@ -409,37 +358,34 @@ Qed.
 
 (** ** Properties of the singleton maps *)
 Lemma lookup_singleton_Some {A} i j (x y : A) :
-  {[(i, x)]} !! j = Some y ↔ i = j ∧ x = y.
+  {[i, x]} !! j = Some y ↔ i = j ∧ x = y.
 Proof.
   unfold singleton, map_singleton.
-  rewrite lookup_insert_Some, lookup_empty. simpl.
-  intuition congruence.
+  rewrite lookup_insert_Some, lookup_empty. simpl. intuition congruence.
 Qed.
-Lemma lookup_singleton_None {A} i j (x : A) :
-  {[(i, x)]} !! j = None ↔ i ≠ j.
+Lemma lookup_singleton_None {A} i j (x : A) : {[i, x]} !! j = None ↔ i ≠ j.
 Proof.
   unfold singleton, map_singleton.
   rewrite lookup_insert_None, lookup_empty. simpl. tauto.
 Qed.
-Lemma lookup_singleton {A} i (x : A) : {[(i, x)]} !! i = Some x.
+Lemma lookup_singleton {A} i (x : A) : {[i, x]} !! i = Some x.
 Proof. by rewrite lookup_singleton_Some. Qed.
-Lemma lookup_singleton_ne {A} i j (x : A) : i ≠ j → {[(i, x)]} !! j = None.
+Lemma lookup_singleton_ne {A} i j (x : A) : i ≠ j → {[i, x]} !! j = None.
 Proof. by rewrite lookup_singleton_None. Qed.
 
-Lemma insert_singleton {A} i (x y : A) : <[i:=y]>{[(i, x)]} = {[(i, y)]}.
+Lemma insert_singleton {A} i (x y : A) : <[i:=y]>{[i, x]} = {[i, y]}.
 Proof.
   unfold singleton, map_singleton, insert, map_insert.
   by rewrite <-partial_alter_compose.
 Qed.
-Lemma alter_singleton {A} (f : A → A) i x :
-  alter f i {[ (i,x) ]} = {[ (i, f x) ]}.
+Lemma alter_singleton {A} (f : A → A) i x : alter f i {[i,x]} = {[i, f x]}.
 Proof.
   intros. apply map_eq. intros i'. destruct (decide (i = i')); subst.
   * by rewrite lookup_alter, !lookup_singleton.
   * by rewrite lookup_alter_ne, !lookup_singleton_ne.
 Qed.
 Lemma alter_singleton_ne {A} (f : A → A) i j x :
-  i ≠ j → alter f i {[ (j,x) ]} = {[ (j, x) ]}.
+  i ≠ j → alter f i {[j,x]} = {[j,x]}.
 Proof.
   intros. apply map_eq. intros i'. destruct (decide (i = i')); subst.
   * by rewrite lookup_alter, lookup_singleton_ne.
@@ -448,20 +394,13 @@ Qed.
 
 (** ** Properties of conversion to lists *)
 Lemma map_to_list_unique {A} (m : M A) i x y :
-  (i,x) ∈ map_to_list m →
-  (i,y) ∈ map_to_list m →
-  x = y.
+  (i,x) ∈ map_to_list m → (i,y) ∈ map_to_list m → x = y.
 Proof. rewrite !elem_of_map_to_list. congruence. Qed.
-Lemma map_to_list_key_nodup {A} (m : M A) :
-  NoDup (fst <$> map_to_list m).
-Proof.
-  eauto using NoDup_fmap_fst, map_to_list_unique, map_to_list_nodup.
-Qed.
+Lemma map_to_list_key_nodup {A} (m : M A) : NoDup (fst <$> map_to_list m).
+Proof. eauto using NoDup_fmap_fst, map_to_list_unique, map_to_list_nodup. Qed.
 
 Lemma elem_of_map_of_list_1 {A} (l : list (K * A)) i x :
-  NoDup (fst <$> l) →
-  (i,x) ∈ l →
-  map_of_list l !! i = Some x.
+  NoDup (fst <$> l) → (i,x) ∈ l → map_of_list l !! i = Some x.
 Proof.
   induction l as [|[j y] l IH]; simpl.
   { by rewrite elem_of_nil. }
@@ -473,8 +412,7 @@ Proof.
   * rewrite lookup_insert_ne; auto.
 Qed.
 Lemma elem_of_map_of_list_2 {A} (l : list (K * A)) i x :
-  map_of_list l !! i = Some x →
-  (i,x) ∈ l.
+  map_of_list l !! i = Some x → (i,x) ∈ l.
 Proof.
   induction l as [|[j y] l IH]; simpl.
   { by rewrite lookup_empty. }
@@ -483,23 +421,18 @@ Proof.
   * rewrite lookup_insert_ne; intuition congruence.
 Qed.
 Lemma elem_of_map_of_list {A} (l : list (K * A)) i x :
-  NoDup (fst <$> l) →
-  (i,x) ∈ l ↔ map_of_list l !! i = Some x.
-Proof.
-  split; auto using elem_of_map_of_list_1, elem_of_map_of_list_2.
-Qed.
+  NoDup (fst <$> l) → (i,x) ∈ l ↔ map_of_list l !! i = Some x.
+Proof. split; auto using elem_of_map_of_list_1, elem_of_map_of_list_2. Qed.
 
 Lemma not_elem_of_map_of_list_1 {A} (l : list (K * A)) i :
-  i ∉ fst <$> l →
-  map_of_list l !! i = None.
+  i ∉ fst <$> l → map_of_list l !! i = None.
 Proof.
   rewrite elem_of_list_fmap, eq_None_not_Some, is_Some_alt.
   intros Hi [x ?]. destruct Hi. exists (i,x). simpl.
   auto using elem_of_map_of_list_2.
 Qed.
 Lemma not_elem_of_map_of_list_2 {A} (l : list (K * A)) i :
-  map_of_list l !! i = None →
-  i ∉ fst <$> l.
+  map_of_list l !! i = None → i ∉ fst <$> l.
 Proof.
   induction l as [|[j y] l IH]; simpl.
   { rewrite elem_of_nil. tauto. }
@@ -510,86 +443,67 @@ Qed.
 Lemma not_elem_of_map_of_list {A} (l : list (K * A)) i :
   i ∉ fst <$> l ↔ map_of_list l !! i = None.
 Proof.
-  split; auto using not_elem_of_map_of_list_1,
-    not_elem_of_map_of_list_2.
+  split; auto using not_elem_of_map_of_list_1, not_elem_of_map_of_list_2.
 Qed.
 
 Lemma map_of_list_proper {A} (l1 l2 : list (K * A)) :
-  NoDup (fst <$> l1) →
-  Permutation l1 l2 →
-  map_of_list l1 = map_of_list l2.
+  NoDup (fst <$> l1) → l1 ≡ₚ l2 → map_of_list l1 = map_of_list l2.
 Proof.
   intros ? Hperm. apply map_eq. intros i. apply option_eq. intros x.
   by rewrite <-!elem_of_map_of_list; rewrite <-?Hperm.
 Qed.
 Lemma map_of_list_inj {A} (l1 l2 : list (K * A)) :
-  NoDup (fst <$> l1) →
-  NoDup (fst <$> l2) →
-  map_of_list l1 = map_of_list l2 →
-  Permutation l1 l2.
+  NoDup (fst <$> l1) → NoDup (fst <$> l2) →
+  map_of_list l1 = map_of_list l2 → l1 ≡ₚ l2.
 Proof.
-  intros ?? Hl1l2.
-  apply NoDup_Permutation; auto using (NoDup_fmap_1 fst).
+  intros ?? Hl1l2. apply NoDup_Permutation; auto using (NoDup_fmap_1 fst).
   intros [i x]. by rewrite !elem_of_map_of_list, Hl1l2.
 Qed.
-Lemma map_of_to_list {A} (m : M A) :
-  map_of_list (map_to_list m) = m.
+Lemma map_of_to_list {A} (m : M A) : map_of_list (map_to_list m) = m.
 Proof.
   apply map_eq. intros i. apply option_eq. intros x.
   by rewrite <-elem_of_map_of_list, elem_of_map_to_list
     by auto using map_to_list_key_nodup.
 Qed.
 Lemma map_to_of_list {A} (l : list (K * A)) :
-  NoDup (fst <$> l) →
-  Permutation (map_to_list (map_of_list l)) l.
-Proof.
-  auto using map_of_list_inj, map_to_list_key_nodup, map_of_to_list.
-Qed.
+  NoDup (fst <$> l) → map_to_list (map_of_list l) ≡ₚ l.
+Proof. auto using map_of_list_inj, map_to_list_key_nodup, map_of_to_list. Qed.
 Lemma map_to_list_inj {A} (m1 m2 : M A) :
-  Permutation (map_to_list m1) (map_to_list m2) →
-  m1 = m2.
+  map_to_list m1 ≡ₚ map_to_list m2 → m1 = m2.
 Proof.
-  intros.
-  rewrite <-(map_of_to_list m1), <-(map_of_to_list m2).
+  intros. rewrite <-(map_of_to_list m1), <-(map_of_to_list m2).
   auto using map_of_list_proper, map_to_list_key_nodup.
 Qed.
 
-Lemma map_to_list_empty {A} :
-  map_to_list ∅ = @nil (K * A).
+Lemma map_to_list_empty {A} : map_to_list ∅ = @nil (K * A).
 Proof.
   apply elem_of_nil_inv. intros [i x].
   rewrite elem_of_map_to_list. apply lookup_empty_Some.
 Qed.
 Lemma map_to_list_insert {A} (m : M A) i x :
-  m !! i = None →
-  Permutation (map_to_list (<[i:=x]>m)) ((i,x) :: map_to_list m).
+  m !! i = None → map_to_list (<[i:=x]>m) ≡ₚ (i,x) :: map_to_list m.
 Proof.
   intros. apply map_of_list_inj; simpl.
   * apply map_to_list_key_nodup.
   * constructor; auto using map_to_list_key_nodup.
-    rewrite elem_of_list_fmap.
-    intros [[??] [? Hlookup]]; subst; simpl in *.
+    rewrite elem_of_list_fmap. intros [[??] [? Hlookup]]; subst; simpl in *.
     rewrite elem_of_map_to_list in Hlookup. congruence.
   * by rewrite !map_of_to_list.
 Qed.
 
-Lemma map_of_list_nil {A} :
-  map_of_list (@nil (K * A)) = ∅.
+Lemma map_of_list_nil {A} : map_of_list (@nil (K * A)) = ∅.
 Proof. done. Qed.
 Lemma map_of_list_cons {A} (l : list (K * A)) i x :
   map_of_list ((i, x) :: l) = <[i:=x]>(map_of_list l).
 Proof. done. Qed.
 
-Lemma map_to_list_empty_inv_alt {A}  (m : M A) :
-  Permutation (map_to_list m) [] → m = ∅.
+Lemma map_to_list_empty_inv_alt {A}  (m : M A) : map_to_list m ≡ₚ [] → m = ∅.
 Proof. rewrite <-map_to_list_empty. apply map_to_list_inj. Qed.
-Lemma map_to_list_empty_inv {A} (m : M A) :
-  map_to_list m = [] → m = ∅.
+Lemma map_to_list_empty_inv {A} (m : M A) : map_to_list m = [] → m = ∅.
 Proof. intros Hm. apply map_to_list_empty_inv_alt. by rewrite Hm. Qed.
 
 Lemma map_to_list_insert_inv {A} (m : M A) l i x :
-  Permutation (map_to_list m) ((i,x) :: l) →
-  m = <[i:=x]>(map_of_list l).
+  map_to_list m ≡ₚ (i,x) :: l → m = <[i:=x]>(map_of_list l).
 Proof.
   intros Hperm. apply map_to_list_inj.
   assert (NoDup (fst <$> (i, x) :: l)) as Hnodup.
@@ -601,12 +515,10 @@ Qed.
 
 (** * Induction principles *)
 Lemma map_ind {A} (P : M A → Prop) :
-  P ∅ →
-  (∀ i x m, m !! i = None → P m → P (<[i:=x]>m)) →
-  ∀ m, P m.
+  P ∅ → (∀ i x m, m !! i = None → P m → P (<[i:=x]>m)) → ∀ m, P m.
 Proof.
   intros Hemp Hins.
-  cut (∀ l, NoDup (fst <$> l) → ∀ m, Permutation (map_to_list m) l → P m).
+  cut (∀ l, NoDup (fst <$> l) → ∀ m, map_to_list m ≡ₚ l → P m).
   { intros help m.
     apply (help (map_to_list m)); auto using map_to_list_key_nodup. }
   induction l as [|[i x] l IH]; intros Hnodup m Hml.
@@ -618,8 +530,7 @@ Proof.
 Qed.
 
 Lemma map_to_list_length {A} (m1 m2 : M A) :
-  m1 ⊂ m2 →
-  length (map_to_list m1) < length (map_to_list m2).
+  m1 ⊂ m2 → length (map_to_list m1) < length (map_to_list m2).
 Proof.
   revert m2. induction m1 as [|i x m ? IH] using map_ind.
   { intros m2 Hm2. rewrite map_to_list_empty. simpl.
@@ -642,14 +553,11 @@ Qed.
 Section map_forall.
 Context {A} (P : K → A → Prop).
 
-Lemma map_forall_to_list m :
-  map_forall P m ↔ Forall (curry P) (map_to_list m).
+Lemma map_forall_to_list m : map_forall P m ↔ Forall (curry P) (map_to_list m).
 Proof.
   rewrite Forall_forall. split.
-  * intros Hforall [i x].
-    rewrite elem_of_map_to_list. by apply (Hforall i x).
-  * intros Hforall i x.
-    rewrite <-elem_of_map_to_list. by apply (Hforall (i,x)).
+  * intros Hforall [i x]. rewrite elem_of_map_to_list. by apply (Hforall i x).
+  * intros Hforall i x. rewrite <-elem_of_map_to_list. by apply (Hforall (i,x)).
 Qed.
 
 Context `{∀ i x, Decision (P i x)}.
@@ -676,8 +584,7 @@ Lemma merge_Some {A B C} (f : option A → option B → option C)
   (∀ i, m !! i = f (m1 !! i) (m2 !! i)) ↔ merge f m1 m2 = m.
 Proof.
   split; [| intro; subst; apply (lookup_merge _) ].
-  intros Hlookup. apply map_eq. intros. rewrite Hlookup.
-  apply (lookup_merge _).
+  intros Hlookup. apply map_eq. intros. rewrite Hlookup. apply (lookup_merge _).
 Qed.
 
 Section merge.
@@ -686,12 +593,12 @@ Context {A} (f : option A → option A → option A).
 Global Instance: LeftId (=) None f → LeftId (=) ∅ (merge f).
 Proof.
   intros ??. apply map_eq. intros.
-  by rewrite !(lookup_merge f), lookup_empty, (left_id None f).
+  by rewrite !(lookup_merge f), lookup_empty, (left_id_L None f).
 Qed.
 Global Instance: RightId (=) None f → RightId (=) ∅ (merge f).
 Proof.
   intros ??. apply map_eq. intros.
-  by rewrite !(lookup_merge f), lookup_empty, (right_id None f).
+  by rewrite !(lookup_merge f), lookup_empty, (right_id_L None f).
 Qed.
 
 Context `{!PropHolds (f None None = None)}.
@@ -711,16 +618,13 @@ Lemma merge_associative m1 m2 m3 :
 Proof. intros. apply map_eq. intros. by rewrite !(lookup_merge f). Qed.
 Global Instance: Associative (=) f → Associative (=) (merge f).
 Proof.
-  intros ????. apply merge_associative. intros. by apply (associative f).
+  intros ????. apply merge_associative. intros. by apply (associative_L f).
 Qed.
 Lemma merge_idempotent m1 :
-  (∀ i, f (m1 !! i) (m1 !! i) = m1 !! i) →
-  merge f m1 m1 = m1.
+  (∀ i, f (m1 !! i) (m1 !! i) = m1 !! i) → merge f m1 m1 = m1.
 Proof. intros. apply map_eq. intros. by rewrite !(lookup_merge f). Qed.
 Global Instance: Idempotent (=) f → Idempotent (=) (merge f).
-Proof.
-  intros ??. apply merge_idempotent. intros. by apply (idempotent f).
-Qed.
+Proof. intros ??. apply merge_idempotent. intros. by apply (idempotent f). Qed.
 
 Lemma partial_alter_merge (g g1 g2 : option A → option A) m1 m2 i :
   g (f (m1 !! i) (m2 !! i)) = f (g1 (m1 !! i)) (g2 (m2 !! i)) →
@@ -771,11 +675,9 @@ Context {A} (R : relation A).
 Global Instance map_intersection_forall_sym:
   Symmetric R → Symmetric (map_intersection_forall R).
 Proof. firstorder auto. Qed.
-Lemma map_intersection_forall_empty_l (m : M A) :
-  map_intersection_forall R ∅ m.
+Lemma map_intersection_forall_empty_l (m : M A) : map_intersection_forall R ∅ m.
 Proof. intros ???. by rewrite lookup_empty. Qed.
-Lemma map_intersection_forall_empty_r (m : M A) :
-  map_intersection_forall R m ∅.
+Lemma map_intersection_forall_empty_r (m : M A) : map_intersection_forall R m ∅.
 Proof. intros ???. by rewrite lookup_empty. Qed.
 
 Lemma map_intersection_forall_alt (m1 m2 : M A) :
@@ -819,8 +721,7 @@ Qed.
 Lemma map_not_disjoint {A} (m1 m2 : M A) :
   ¬m1 ⊥ m2 ↔ ∃ i x1 x2, m1 !! i = Some x1 ∧ m2 !! i = Some x2.
 Proof.
-  unfold disjoint, map_disjoint.
-  rewrite map_not_intersection_forall.
+  unfold disjoint, map_disjoint. rewrite map_not_intersection_forall.
   * naive_solver.
   * right. auto.
 Qed.
@@ -833,9 +734,7 @@ Lemma map_disjoint_empty_r {A} (m : M A) : m ⊥ ∅.
 Proof. apply map_intersection_forall_empty_r. Qed.
 
 Lemma map_disjoint_weaken {A} (m1 m1' m2 m2' : M A) :
-  m1' ⊥ m2' →
-  m1 ⊆ m1' → m2 ⊆ m2' →
-  m1 ⊥ m2.
+  m1' ⊥ m2' → m1 ⊆ m1' → m2 ⊆ m2' → m1 ⊥ m2.
 Proof.
   intros Hdisjoint Hm1 Hm2 i x1 x2 Hx1 Hx2.
   destruct (Hdisjoint i x1 x2); auto.
@@ -848,49 +747,42 @@ Lemma map_disjoint_weaken_r {A} (m1 m2 m2' : M A) :
 Proof. eauto using map_disjoint_weaken. Qed.
 
 Lemma map_disjoint_Some_l {A} (m1 m2 : M A) i x:
-  m1 ⊥ m2 →
-  m1 !! i = Some x →
-  m2 !! i = None.
+  m1 ⊥ m2 → m1 !! i = Some x → m2 !! i = None.
 Proof.
   intros Hdisjoint ?. rewrite eq_None_not_Some, is_Some_alt.
   intros [x2 ?]. by apply (Hdisjoint i x x2).
 Qed.
 Lemma map_disjoint_Some_r {A} (m1 m2 : M A) i x:
-  m1 ⊥ m2 →
-  m2 !! i = Some x →
-  m1 !! i = None.
+  m1 ⊥ m2 → m2 !! i = Some x → m1 !! i = None.
 Proof. rewrite (symmetry_iff (⊥)). apply map_disjoint_Some_l. Qed.
 
-Lemma map_disjoint_singleton_l {A} (m : M A) i x :
-  {[(i, x)]} ⊥ m ↔ m !! i = None.
+Lemma map_disjoint_singleton_l {A} (m : M A) i x : {[i, x]} ⊥ m ↔ m !! i = None.
 Proof.
   split.
-  * intro. apply (map_disjoint_Some_l {[(i, x)]} _ _ x);
+  * intro. apply (map_disjoint_Some_l {[i, x]} _ _ x);
       auto using lookup_singleton.
   * intros ? j y1 y2. destruct (decide (i = j)); subst.
     + rewrite lookup_singleton. intuition congruence.
     + by rewrite lookup_singleton_ne.
 Qed.
 Lemma map_disjoint_singleton_r {A} (m : M A) i x :
-  m ⊥ {[(i, x)]} ↔ m !! i = None.
+  m ⊥ {[i, x]} ↔ m !! i = None.
 Proof. by rewrite (symmetry_iff (⊥)), map_disjoint_singleton_l. Qed.
 
 Lemma map_disjoint_singleton_l_2 {A} (m : M A) i x :
-  m !! i = None → {[(i, x)]} ⊥ m.
+  m !! i = None → {[i, x]} ⊥ m.
 Proof. by rewrite map_disjoint_singleton_l. Qed.
 Lemma map_disjoint_singleton_r_2 {A} (m : M A) i x :
-  m !! i = None → m ⊥ {[(i, x)]}.
+  m !! i = None → m ⊥ {[i, x]}.
 Proof. by rewrite map_disjoint_singleton_r. Qed.
 
-Lemma map_disjoint_delete_l {A} (m1 m2 : M A) i :
-  m1 ⊥ m2 → delete i m1 ⊥ m2.
+Lemma map_disjoint_delete_l {A} (m1 m2 : M A) i : m1 ⊥ m2 → delete i m1 ⊥ m2.
 Proof.
   rewrite !map_disjoint_alt.
   intros Hdisjoint j. destruct (Hdisjoint j); auto.
   rewrite lookup_delete_None. tauto.
 Qed.
-Lemma map_disjoint_delete_r {A} (m1 m2 : M A) i :
-  m1 ⊥ m2 → m1 ⊥ delete i m2.
+Lemma map_disjoint_delete_r {A} (m1 m2 : M A) i : m1 ⊥ m2 → m1 ⊥ delete i m2.
 Proof. symmetry. by apply map_disjoint_delete_l. Qed.
 
 (** ** Properties of the [union_with] operation *)
@@ -920,20 +812,14 @@ Lemma lookup_union_with_None m1 m2 i :
 Proof. rewrite lookup_union_with. naive_solver. Qed.
 
 Lemma lookup_union_with_Some_lr m1 m2 i x y z :
-  m1 !! i = Some x →
-  m2 !! i = Some y →
-  f x y = Some z →
+  m1 !! i = Some x → m2 !! i = Some y → f x y = Some z →
   union_with f m1 m2 !! i = Some z.
 Proof. rewrite lookup_union_with. naive_solver. Qed.
 Lemma lookup_union_with_Some_l m1 m2 i x :
-  m1 !! i = Some x →
-  m2 !! i = None →
-  union_with f m1 m2 !! i = Some x.
+  m1 !! i = Some x → m2 !! i = None → union_with f m1 m2 !! i = Some x.
 Proof. rewrite lookup_union_with. naive_solver. Qed.
 Lemma lookup_union_with_Some_r m1 m2 i y :
-  m1 !! i = None →
-  m2 !! i = Some y →
-  union_with f m1 m2 !! i = Some y.
+  m1 !! i = None → m2 !! i = Some y → union_with f m1 m2 !! i = Some y.
 Proof. rewrite lookup_union_with. naive_solver. Qed.
 
 Global Instance: LeftId (@eq (M A)) ∅ (union_with f).
@@ -952,8 +838,7 @@ Global Instance: Commutative (=) f → Commutative (@eq (M A)) (union_with f).
 Proof. intros ???. apply union_with_commutative. eauto. Qed.
 
 Lemma union_with_idempotent m :
-  (∀ i x, m !! i = Some x → f x x = Some x) →
-  union_with f m m = m.
+  (∀ i x, m !! i = Some x → f x x = Some x) → union_with f m m = m.
 Proof.
   intros. apply (merge_idempotent _). intros i.
   destruct (m !! i) eqn:?; simpl; eauto.
@@ -998,15 +883,13 @@ Lemma insert_union_with m1 m2 i x :
   <[i:=x]>(union_with f m1 m2) = union_with f (<[i:=x]>m1) (<[i:=x]>m2).
 Proof. intros. apply (partial_alter_merge _). simpl. auto. Qed.
 Lemma insert_union_with_l m1 m2 i x :
-  m2 !! i = None →
-  <[i:=x]>(union_with f m1 m2) = union_with f (<[i:=x]>m1) m2.
+  m2 !! i = None → <[i:=x]>(union_with f m1 m2) = union_with f (<[i:=x]>m1) m2.
 Proof.
   intros Hm2. unfold union_with, map_union_with.
   rewrite (insert_merge_l _). done. by rewrite Hm2.
 Qed.
 Lemma insert_union_with_r m1 m2 i x :
-  m1 !! i = None →
-  <[i:=x]>(union_with f m1 m2) = union_with f m1 (<[i:=x]>m2).
+  m1 !! i = None → <[i:=x]>(union_with f m1 m2) = union_with f m1 (<[i:=x]>m2).
 Proof.
   intros Hm1. unfold union_with, map_union_with.
   rewrite (insert_merge_r _). done. by rewrite Hm1.
@@ -1029,86 +912,62 @@ Lemma lookup_union_Some_raw {A} (m1 m2 : M A) i x :
   (m1 ∪ m2) !! i = Some x ↔
     m1 !! i = Some x ∨ (m1 !! i = None ∧ m2 !! i = Some x).
 Proof.
-  unfold union, map_union, union_with, map_union_with.
-  rewrite (lookup_merge _).
+  unfold union, map_union, union_with, map_union_with. rewrite (lookup_merge _).
   destruct (m1 !! i), (m2 !! i); compute; intuition congruence.
 Qed.
 Lemma lookup_union_None {A} (m1 m2 : M A) i :
   (m1 ∪ m2) !! i = None ↔ m1 !! i = None ∧ m2 !! i = None.
 Proof.
-  unfold union, map_union, union_with, map_union_with.
-  rewrite (lookup_merge _).
+  unfold union, map_union, union_with, map_union_with. rewrite (lookup_merge _).
   destruct (m1 !! i), (m2 !! i); compute; intuition congruence.
 Qed.
 
 Lemma lookup_union_Some {A} (m1 m2 : M A) i x :
-  m1 ⊥ m2 →
-  (m1 ∪ m2) !! i = Some x ↔ m1 !! i = Some x ∨ m2 !! i = Some x.
+  m1 ⊥ m2 → (m1 ∪ m2) !! i = Some x ↔ m1 !! i = Some x ∨ m2 !! i = Some x.
 Proof.
   intros Hdisjoint. rewrite lookup_union_Some_raw.
   intuition eauto using map_disjoint_Some_r.
 Qed.
 
 Lemma lookup_union_Some_l {A} (m1 m2 : M A) i x :
-  m1 !! i = Some x →
-  (m1 ∪ m2) !! i = Some x.
+  m1 !! i = Some x → (m1 ∪ m2) !! i = Some x.
 Proof. intro. rewrite lookup_union_Some_raw; intuition. Qed.
 Lemma lookup_union_Some_r {A} (m1 m2 : M A) i x :
-  m1 ⊥ m2 →
-  m2 !! i = Some x →
-  (m1 ∪ m2) !! i = Some x.
+  m1 ⊥ m2 → m2 !! i = Some x → (m1 ∪ m2) !! i = Some x.
 Proof. intro. rewrite lookup_union_Some; intuition. Qed.
 
-Lemma map_union_commutative {A} (m1 m2 : M A) :
-  m1 ⊥ m2 →
-  m1 ∪ m2 = m2 ∪ m1.
+Lemma map_union_commutative {A} (m1 m2 : M A) : m1 ⊥ m2 → m1 ∪ m2 = m2 ∪ m1.
 Proof.
   intros Hdisjoint. apply (merge_commutative (union_with (λ x _, Some x))).
   intros i. specialize (Hdisjoint i).
   destruct (m1 !! i), (m2 !! i); compute; naive_solver.
 Qed.
 
-Lemma map_subseteq_union {A} (m1 m2 : M A) :
-  m1 ⊆ m2 →
-  m1 ∪ m2 = m2.
+Lemma map_subseteq_union {A} (m1 m2 : M A) : m1 ⊆ m2 → m1 ∪ m2 = m2.
 Proof.
-  intros Hm1m2.
-  apply map_eq. intros i. apply option_eq. intros x.
+  intros Hm1m2. apply map_eq. intros i. apply option_eq. intros x.
   rewrite lookup_union_Some_raw. split; [by intuition |].
-  intros Hm2. specialize (Hm1m2 i).
-  destruct (m1 !! i) as [y|]; [| by auto].
+  intros Hm2. specialize (Hm1m2 i). destruct (m1 !! i) as [y|]; [| by auto].
   rewrite (Hm1m2 y eq_refl) in Hm2. intuition congruence.
 Qed.
 
-Lemma map_union_subseteq_l {A} (m1 m2 : M A) :
-  m1 ⊆ m1 ∪ m2.
+Lemma map_union_subseteq_l {A} (m1 m2 : M A) : m1 ⊆ m1 ∪ m2.
 Proof. intros ? i x. rewrite lookup_union_Some_raw. intuition. Qed.
-Lemma map_union_subseteq_r {A} (m1 m2 : M A) :
-  m1 ⊥ m2 →
-  m2 ⊆ m1 ∪ m2.
+Lemma map_union_subseteq_r {A} (m1 m2 : M A) : m1 ⊥ m2 → m2 ⊆ m1 ∪ m2.
 Proof.
-  intros. rewrite map_union_commutative by done.
-  by apply map_union_subseteq_l.
+  intros. rewrite map_union_commutative by done. by apply map_union_subseteq_l.
 Qed.
 
-Lemma map_union_subseteq_l_alt {A} (m1 m2 m3 : M A) :
-  m1 ⊆ m2 →
-  m1 ⊆ m2 ∪ m3.
+Lemma map_union_subseteq_l_alt {A} (m1 m2 m3 : M A) : m1 ⊆ m2 → m1 ⊆ m2 ∪ m3.
 Proof. intros. transitivity m2; auto using map_union_subseteq_l. Qed.
 Lemma map_union_subseteq_r_alt {A} (m1 m2 m3 : M A) :
-  m2 ⊥ m3 →
-  m1 ⊆ m3 →
-  m1 ⊆ m2 ∪ m3.
+  m2 ⊥ m3 → m1 ⊆ m3 → m1 ⊆ m2 ∪ m3.
 Proof. intros. transitivity m3; auto using map_union_subseteq_r. Qed.
 
-Lemma map_union_preserving_l {A} (m1 m2 m3 : M A) :
-  m1 ⊆ m2 →
-  m3 ∪ m1 ⊆ m3 ∪ m2.
+Lemma map_union_preserving_l {A} (m1 m2 m3 : M A) : m1 ⊆ m2 → m3 ∪ m1 ⊆ m3 ∪ m2.
 Proof. intros ???. rewrite !lookup_union_Some_raw. naive_solver. Qed.
 Lemma map_union_preserving_r {A} (m1 m2 m3 : M A) :
-  m2 ⊥ m3 →
-  m1 ⊆ m2 →
-  m1 ∪ m3 ⊆ m2 ∪ m3.
+  m2 ⊥ m3 → m1 ⊆ m2 → m1 ∪ m3 ⊆ m2 ∪ m3.
 Proof.
   intros. rewrite !(map_union_commutative _ m3)
     by eauto using map_disjoint_weaken_l.
@@ -1116,39 +975,27 @@ Proof.
 Qed.
 
 Lemma map_union_reflecting_l {A} (m1 m2 m3 : M A) :
-  m3 ⊥ m1 →
-  m3 ⊥ m2 →
-  m3 ∪ m1 ⊆ m3 ∪ m2 →
-  m1 ⊆ m2.
+  m3 ⊥ m1 → m3 ⊥ m2 → m3 ∪ m1 ⊆ m3 ∪ m2 → m1 ⊆ m2.
 Proof.
-  intros Hm3m1 Hm3m2 E b x ?.
-  specialize (E b x). rewrite !lookup_union_Some in E by done.
+  intros Hm3m1 Hm3m2 E b x ?. specialize (E b x).
+  rewrite !lookup_union_Some in E by done.
   destruct E; auto. by destruct (Hm3m1 b x x).
 Qed.
 Lemma map_union_reflecting_r {A} (m1 m2 m3 : M A) :
-  m1 ⊥ m3 →
-  m2 ⊥ m3 →
-  m1 ∪ m3 ⊆ m2 ∪ m3 →
-  m1 ⊆ m2.
+  m1 ⊥ m3 → m2 ⊥ m3 → m1 ∪ m3 ⊆ m2 ∪ m3 → m1 ⊆ m2.
 Proof.
   intros ??. rewrite !(map_union_commutative _ m3) by done.
   by apply map_union_reflecting_l.
 Qed.
 
 Lemma map_union_cancel_l {A} (m1 m2 m3 : M A) :
-  m1 ⊥ m3 →
-  m2 ⊥ m3 →
-  m3 ∪ m1 = m3 ∪ m2 →
-  m1 = m2.
+  m1 ⊥ m3 → m2 ⊥ m3 → m3 ∪ m1 = m3 ∪ m2 → m1 = m2.
 Proof.
   intros. by apply (anti_symmetric _);
     apply map_union_reflecting_l with m3; auto with congruence.
 Qed.
 Lemma map_union_cancel_r {A} (m1 m2 m3 : M A) :
-  m1 ⊥ m3 →
-  m2 ⊥ m3 →
-  m1 ∪ m3 = m2 ∪ m3 →
-  m1 = m2.
+  m1 ⊥ m3 → m2 ⊥ m3 → m1 ∪ m3 = m2 ∪ m3 → m1 = m2.
 Proof.
   intros. apply (anti_symmetric _);
     apply map_union_reflecting_r with m3; auto with congruence.
@@ -1157,14 +1004,12 @@ Qed.
 Lemma map_disjoint_union_l {A} (m1 m2 m3 : M A) :
   m1 ∪ m2 ⊥ m3 ↔ m1 ⊥ m3 ∧ m2 ⊥ m3.
 Proof.
-  rewrite !map_disjoint_alt.
-  setoid_rewrite lookup_union_None. naive_solver.
+  rewrite !map_disjoint_alt. setoid_rewrite lookup_union_None. naive_solver.
 Qed.
 Lemma map_disjoint_union_r {A} (m1 m2 m3 : M A) :
   m1 ⊥ m2 ∪ m3 ↔ m1 ⊥ m2 ∧ m1 ⊥ m3.
 Proof.
-  rewrite !map_disjoint_alt.
-  setoid_rewrite lookup_union_None. naive_solver.
+  rewrite !map_disjoint_alt. setoid_rewrite lookup_union_None. naive_solver.
 Qed.
 Lemma map_disjoint_union_l_2 {A} (m1 m2 m3 : M A) :
   m1 ⊥ m3 → m2 ⊥ m3 → m1 ∪ m2 ⊥ m3.
@@ -1173,8 +1018,7 @@ Lemma map_disjoint_union_r_2 {A} (m1 m2 m3 : M A) :
   m1 ⊥ m2 → m1 ⊥ m3 → m1 ⊥ m2 ∪ m3.
 Proof. by rewrite map_disjoint_union_r. Qed.
 
-Lemma insert_union_singleton_l {A} (m : M A) i x :
-  <[i:=x]>m = {[(i,x)]} ∪ m.
+Lemma insert_union_singleton_l {A} (m : M A) i x : <[i:=x]>m = {[i,x]} ∪ m.
 Proof.
   apply map_eq. intros j. apply option_eq. intros y.
   rewrite lookup_union_Some_raw.
@@ -1183,8 +1027,7 @@ Proof.
   * rewrite !lookup_singleton_ne, lookup_insert_ne; intuition congruence.
 Qed.
 Lemma insert_union_singleton_r {A} (m : M A) i x :
-  m !! i = None →
-  <[i:=x]>m = m ∪ {[(i,x)]}.
+  m !! i = None → <[i:=x]>m = m ∪ {[i,x]}.
 Proof.
   intro. rewrite insert_union_singleton_l, map_union_commutative; [done |].
   by apply map_disjoint_singleton_l.
@@ -1212,21 +1055,19 @@ Proof. by rewrite map_disjoint_insert_r. Qed.
 
 Lemma insert_union_l {A} (m1 m2 : M A) i x :
   <[i:=x]>(m1 ∪ m2) = <[i:=x]>m1 ∪ m2.
-Proof. by rewrite !insert_union_singleton_l, (associative (∪)). Qed.
+Proof. by rewrite !insert_union_singleton_l, (associative_L (∪)). Qed.
 Lemma insert_union_r {A} (m1 m2 : M A) i x :
-  m1 !! i = None →
-  <[i:=x]>(m1 ∪ m2) = m1 ∪ <[i:=x]>m2.
+  m1 !! i = None → <[i:=x]>(m1 ∪ m2) = m1 ∪ <[i:=x]>m2.
 Proof.
-  intro. rewrite !insert_union_singleton_l, !(associative (∪)).
+  intro. rewrite !insert_union_singleton_l, !(associative_L (∪)).
   rewrite (map_union_commutative m1); [done |].
   by apply map_disjoint_singleton_r.
 Qed.
 
-Lemma insert_list_union {A} (m : M A) l :
-  insert_list l m = map_of_list l ∪ m.
+Lemma insert_list_union {A} (m : M A) l : insert_list l m = map_of_list l ∪ m.
 Proof.
   induction l; simpl.
-  * by rewrite (left_id _ _).
+  * by rewrite (left_id_L _ _).
   * by rewrite IHl, insert_union_l.
 Qed.
 
@@ -1236,7 +1077,7 @@ Proof. apply delete_union_with. Qed.
 
 (** ** Properties of the [union_list] operation *)
 Lemma map_disjoint_union_list_l {A} (ms : list (M A)) (m : M A) :
-  ⋃ ms ⊥ m ↔ Forall (⊥ m) ms.
+  ⋃ ms ⊥ m ↔ Forall (.⊥ m) ms.
 Proof.
   split.
   * induction ms; simpl; rewrite ?map_disjoint_union_l; intuition.
@@ -1245,20 +1086,18 @@ Proof.
     + by rewrite map_disjoint_union_l.
 Qed.
 Lemma map_disjoint_union_list_r {A} (ms : list (M A)) (m : M A) :
-  m ⊥ ⋃ ms ↔ Forall (⊥ m) ms.
+  m ⊥ ⋃ ms ↔ Forall (.⊥ m) ms.
 Proof. by rewrite (symmetry_iff (⊥)), map_disjoint_union_list_l. Qed.
 
 Lemma map_disjoint_union_list_l_2 {A} (ms : list (M A)) (m : M A) :
-  Forall (⊥ m) ms → ⋃ ms ⊥ m.
+  Forall (.⊥ m) ms → ⋃ ms ⊥ m.
 Proof. by rewrite map_disjoint_union_list_l. Qed.
 Lemma map_disjoint_union_list_r_2 {A} (ms : list (M A)) (m : M A) :
-  Forall (⊥ m) ms → m ⊥ ⋃ ms.
+  Forall (.⊥ m) ms → m ⊥ ⋃ ms.
 Proof. by rewrite map_disjoint_union_list_r. Qed.
 
 Lemma map_union_sublist {A} (ms1 ms2 : list (M A)) :
-  list_disjoint ms2 →
-  sublist ms1 ms2 →
-  ⋃ ms1 ⊆ ⋃ ms2.
+  ⊥ ms2 → ms1 `sublist` ms2 → ⋃ ms1 ⊆ ⋃ ms2.
 Proof.
   intros Hms2. revert ms1.
   induction Hms2 as [|m2 ms2]; intros ms1; [by inversion 1|].
@@ -1291,22 +1130,19 @@ Qed.
 Lemma lookup_delete_list_not_elem_of {A} (m : M A) is j :
   j ∉ is → delete_list is m !! j = m !! j.
 Proof.
-  induction is; simpl; [done |].
-  rewrite elem_of_cons. intros.
+  induction is; simpl; [done |]. rewrite elem_of_cons. intros.
   intros. rewrite lookup_delete_ne; intuition.
 Qed.
 Lemma delete_list_notin {A} (m : M A) is :
   Forall (λ i, m !! i = None) is → delete_list is m = m.
 Proof.
-  induction 1; simpl; [done |].
-  rewrite delete_notin; congruence.
+  induction 1; simpl; [done |]. rewrite delete_notin; congruence.
 Qed.
 
 Lemma delete_list_insert_ne {A} (m : M A) is j x :
   j ∉ is → delete_list is (<[j:=x]>m) = <[j:=x]>(delete_list is m).
 Proof.
-  induction is; simpl; [done |].
-  rewrite elem_of_cons. intros.
+  induction is; simpl; [done |]. rewrite elem_of_cons. intros.
   rewrite IHis, delete_insert_ne; intuition.
 Qed.
 
@@ -1336,38 +1172,34 @@ Lemma map_disjoint_of_list_r {A} (m : M A) ixs :
 Proof. by rewrite (symmetry_iff (⊥)), map_disjoint_of_list_l. Qed.
 
 Lemma map_disjoint_of_list_zip_l {A} (m : M A) is xs :
-  same_length is xs →
+  is `same_length` xs →
   map_of_list (zip is xs) ⊥ m ↔ Forall (λ i, m !! i = None) is.
 Proof.
   intro. rewrite map_disjoint_of_list_l.
-  rewrite <-(zip_fst is xs) at 2 by done.
-  by rewrite Forall_fmap.
+  rewrite <-(zip_fst is xs) at 2 by done. by rewrite Forall_fmap.
 Qed.
 Lemma map_disjoint_of_list_zip_r {A} (m : M A) is xs :
-  same_length is xs →
+  is `same_length` xs →
   m ⊥ map_of_list (zip is xs) ↔ Forall (λ i, m !! i = None) is.
 Proof.
   intro. by rewrite (symmetry_iff (⊥)), map_disjoint_of_list_zip_l.
 Qed.
 Lemma map_disjoint_of_list_zip_l_2 {A} (m : M A) is xs :
-  same_length is xs →
-  Forall (λ i, m !! i = None) is →
+  is `same_length` xs → Forall (λ i, m !! i = None) is →
   map_of_list (zip is xs) ⊥ m.
 Proof. intro. by rewrite map_disjoint_of_list_zip_l. Qed.
 Lemma map_disjoint_of_list_zip_r_2 {A} (m : M A) is xs :
-  same_length is xs →
-  Forall (λ i, m !! i = None) is →
+  is `same_length` xs → Forall (λ i, m !! i = None) is →
   m ⊥ map_of_list (zip is xs).
 Proof. intro. by rewrite map_disjoint_of_list_zip_r. Qed.
 
 (** ** Properties with respect to vectors *)
 Lemma union_delete_vec {A n} (ms : vec (M A) n) (i : fin n) :
-  list_disjoint ms →
-  ms !!! i ∪ ⋃ delete (fin_to_nat i) (vec_to_list ms) = ⋃ ms.
+  ⊥ ms → ms !!! i ∪ ⋃ delete (fin_to_nat i) (vec_to_list ms) = ⋃ ms.
 Proof.
   induction ms as [|m ? ms]; inversion_clear 1;
     inv_fin i; simpl; [done | intros i].
-  rewrite (map_union_commutative m), (associative_eq _ _), IHms.
+  rewrite (map_union_commutative m), (associative_L (∪)), IHms.
   * by rewrite map_union_commutative.
   * done.
   * apply map_disjoint_weaken_r with (⋃ ms); [done |].
@@ -1378,10 +1210,9 @@ Lemma union_insert_vec {A n} (ms : vec (M A) n) (i : fin n) m :
   m ⊥ ⋃ delete (fin_to_nat i) (vec_to_list ms) →
   ⋃ vinsert i m ms = m ∪ ⋃ delete (fin_to_nat i) (vec_to_list ms).
 Proof.
-  induction ms as [|m' ? ms IH];
-    inv_fin i; simpl; [done | intros i Hdisjoint].
+  induction ms as [|m' ? ms IH]; inv_fin i; simpl; [done | intros i Hdisjoint].
   rewrite map_disjoint_union_r in Hdisjoint.
-  rewrite IH, !(associative_eq (∪)), (map_union_commutative m); intuition.
+  rewrite IH, !(associative_L (∪)), (map_union_commutative m); intuition.
 Qed.
 
 (** ** Properties of the [difference_with] operation *)
@@ -1409,21 +1240,15 @@ Lemma lookup_difference_with_None m1 m2 i :
 Proof. rewrite lookup_difference_with. naive_solver. Qed.
 
 Lemma lookup_difference_with_Some_lr m1 m2 i x y z :
-  m1 !! i = Some x →
-  m2 !! i = Some y →
-  f x y = Some z →
+  m1 !! i = Some x → m2 !! i = Some y → f x y = Some z →
   difference_with f m1 m2 !! i = Some z.
 Proof. rewrite lookup_difference_with. naive_solver. Qed.
 Lemma lookup_difference_with_None_lr m1 m2 i x y :
-  m1 !! i = Some x →
-  m2 !! i = Some y →
-  f x y = None →
+  m1 !! i = Some x → m2 !! i = Some y → f x y = None →
   difference_with f m1 m2 !! i = None.
 Proof. rewrite lookup_difference_with. naive_solver. Qed.
 Lemma lookup_difference_with_Some_l m1 m2 i x :
-  m1 !! i = Some x →
-  m2 !! i = None →
-  difference_with f m1 m2 !! i = Some x.
+  m1 !! i = Some x → m2 !! i = None → difference_with f m1 m2 !! i = Some x.
 Proof. rewrite lookup_difference_with. naive_solver. Qed.
 End difference_with.
 
@@ -1436,19 +1261,14 @@ Proof.
   destruct (m1 !! i), (m2 !! i); compute; intuition congruence.
 Qed.
 
-Lemma map_disjoint_difference_l {A} (m1 m2 : M A) :
-  m1 ⊆ m2 →
-  m2 ∖ m1 ⊥ m1.
+Lemma map_disjoint_difference_l {A} (m1 m2 : M A) : m1 ⊆ m2 → m2 ∖ m1 ⊥ m1.
 Proof.
-  intros E i. specialize (E i).
-  unfold difference, map_difference. intros x1 x2.
+  intros E i. specialize (E i). unfold difference, map_difference. intros x1 x2.
   rewrite lookup_difference_with_Some. intros [?| (?&?&?&?&?)] ?.
   * specialize (E x2). intuition congruence.
   * done.
 Qed.
-Lemma map_disjoint_difference_r {A} (m1 m2 : M A) :
-  m1 ⊆ m2 →
-  m1 ⊥ m2 ∖ m1.
+Lemma map_disjoint_difference_r {A} (m1 m2 : M A) : m1 ⊆ m2 → m1 ⊥ m2 ∖ m1.
 Proof. intros. symmetry. by apply map_disjoint_difference_l. Qed.
 
 Lemma map_difference_union {A} (m1 m2 : M A) :
@@ -1456,8 +1276,7 @@ Lemma map_difference_union {A} (m1 m2 : M A) :
 Proof.
   intro Hm1m2. apply map_eq. intros i.
   apply option_eq. intros v. specialize (Hm1m2 i).
-  unfold difference, map_difference,
-    difference_with, map_difference_with.
+  unfold difference, map_difference, difference_with, map_difference_with.
   rewrite lookup_union_Some_raw, (lookup_merge _).
   destruct (m1 !! i) as [v'|], (m2 !! i);
     try specialize (Hm1m2 v'); compute; intuition congruence.
@@ -1472,30 +1291,23 @@ maps. This tactic does not yield any information loss as all simplifications
 performed are reversible. *)
 Ltac decompose_map_disjoint := repeat
   match goal with
-  | H : _ ∪ _ ⊥ _ |- _ =>
-    apply map_disjoint_union_l in H; destruct H
-  | H : _ ⊥ _ ∪ _ |- _ =>
-    apply map_disjoint_union_r in H; destruct H
+  | H : _ ∪ _ ⊥ _ |- _ => apply map_disjoint_union_l in H; destruct H
+  | H : _ ⊥ _ ∪ _ |- _ => apply map_disjoint_union_r in H; destruct H
   | H : {[ _ ]} ⊥ _ |- _ => apply map_disjoint_singleton_l in H
   | H : _ ⊥ {[ _ ]} |- _ =>  apply map_disjoint_singleton_r in H
-  | H : <[_:=_]>_ ⊥ _ |- _ =>
-    apply map_disjoint_insert_l in H; destruct H
-  | H : _ ⊥ <[_:=_]>_ |- _ =>
-    apply map_disjoint_insert_r in H; destruct H
+  | H : <[_:=_]>_ ⊥ _ |- _ => apply map_disjoint_insert_l in H; destruct H
+  | H : _ ⊥ <[_:=_]>_ |- _ => apply map_disjoint_insert_r in H; destruct H
   | H : ⋃ _ ⊥ _ |- _ => apply map_disjoint_union_list_l in H
   | H : _ ⊥ ⋃ _ |- _ => apply map_disjoint_union_list_r in H
   | H : ∅ ⊥ _ |- _ => clear H
   | H : _ ⊥ ∅ |- _ => clear H
-  | H : list_disjoint [] |- _ => clear H
-  | H : list_disjoint [_] |- _ => clear H
-  | H : list_disjoint (_ :: _) |- _ =>
-    apply list_disjoint_cons_inv in H; destruct H
-  | H : Forall (⊥ _) _ |- _ => rewrite Forall_vlookup in H
-  | H : Forall (⊥ _) [] |- _ => clear H
-  | H : Forall (⊥ _) (_ :: _) |- _ =>
-    rewrite Forall_cons in H; destruct H
-  | H : Forall (⊥ _) (_ :: _) |- _ =>
-    rewrite Forall_app in H; destruct H
+  | H : ⊥ [] |- _ => clear H
+  | H : ⊥ [_] |- _ => clear H
+  | H : ⊥ (_ :: _) |- _ => apply disjoint_list_cons in H; destruct H
+  | H : Forall (.⊥ _) _ |- _ => rewrite Forall_vlookup in H
+  | H : Forall (.⊥ _) [] |- _ => clear H
+  | H : Forall (.⊥ _) (_ :: _) |- _ => rewrite Forall_cons in H; destruct H
+  | H : Forall (.⊥ _) (_ :: _) |- _ => rewrite Forall_app in H; destruct H
   end.
 
 (** To prove a disjointness property, we first decompose all hypotheses, and
@@ -1513,8 +1325,8 @@ Hint Extern 2 ({[ _ ]} ⊥ _) =>
   apply map_disjoint_singleton_l_2 : map_disjoint.
 Hint Extern 2 (_ ⊥ {[ _ ]}) =>
   apply map_disjoint_singleton_r_2 : map_disjoint.
-Hint Extern 2 (list_disjoint []) => apply disjoint_nil : map_disjoint.
-Hint Extern 2 (list_disjoint (_ :: _)) => apply disjoint_cons : map_disjoint.
+Hint Extern 2 (⊥ []) => apply disjoint_nil_2 : map_disjoint.
+Hint Extern 2 (⊥ (_ :: _)) => apply disjoint_cons_2 : map_disjoint.
 Hint Extern 2 (_ ∪ _ ⊥ _) => apply map_disjoint_union_l_2 : map_disjoint.
 Hint Extern 2 (_ ⊥ _ ∪ _) => apply map_disjoint_union_r_2 : map_disjoint.
 Hint Extern 2 (<[_:=_]>_ ⊥ _) => apply map_disjoint_insert_l_2 : map_disjoint.
@@ -1532,7 +1344,7 @@ Hint Extern 2 (delete_list _ _ ⊥ _) =>
 Hint Extern 2 (_ ⊥ delete_list _ _) =>
   apply map_disjoint_delete_list_r : map_disjoint.
 
-(** The tactic [simpl_map by tac] simplifies occurrences of finite map look 
+(** The tactic [simpl_map by tac] simplifies occurrences of finite map look
 ups. It uses [tac] to discharge generated inequalities. Look ups in unions do
 not have nice equational properties, hence it invokes [tac] to prove that such
 look ups yield [Some]. *)
@@ -1572,19 +1384,17 @@ Tactic Notation "simpl_map" "by" tactic3(tac) := repeat
 Create HintDb simpl_map.
 Tactic Notation "simpl_map" := simpl_map by eauto with simpl_map map_disjoint.
 
-Hint Extern 80 ((_ ∪ _) !! _ = Some _) =>
-  apply lookup_union_Some_l : simpl_map.
-Hint Extern 81 ((_ ∪ _) !! _ = Some _) =>
-  apply lookup_union_Some_r : simpl_map.
-Hint Extern 80 ({[ _ ]} !! _ = Some _) =>
-  apply lookup_singleton : simpl_map.
-Hint Extern 80 (<[_:=_]> _ !! _ = Some _) =>
-  apply lookup_insert : simpl_map.
+Hint Extern 80 ((_ ∪ _) !! _ = Some _) => apply lookup_union_Some_l : simpl_map.
+Hint Extern 81 ((_ ∪ _) !! _ = Some _) => apply lookup_union_Some_r : simpl_map.
+Hint Extern 80 ({[ _ ]} !! _ = Some _) => apply lookup_singleton : simpl_map.
+Hint Extern 80 (<[_:=_]> _ !! _ = Some _) => apply lookup_insert : simpl_map.
 
 (** Now we take everything together and also discharge conflicting look ups,
 simplify overlapping look ups, and perform cancellations of equalities
 involving unions. *)
-Tactic Notation "simplify_map_equality" "by" tactic3(tac) := repeat
+Tactic Notation "simplify_map_equality" "by" tactic3(tac) :=
+  decompose_map_disjoint;
+  repeat
   match goal with
   | _ => progress simpl_map by tac
   | _ => progress simplify_equality
@@ -1606,5 +1416,4 @@ Tactic Notation "simplify_map_equality" "by" tactic3(tac) := repeat
     apply map_union_cancel_r in H; [| solve[tac] | solve [tac]]
   end.
 Tactic Notation "simplify_map_equality" :=
-  decompose_map_disjoint;
   simplify_map_equality by eauto with simpl_map map_disjoint.
diff --git a/theories/fresh_numbers.v b/theories/fresh_numbers.v
index 84f0ec2c669bad6ba68dee21bacc8ddbfcef5e57..1e8c5d85d525c74dded4b577c32f0201421cf02d 100644
--- a/theories/fresh_numbers.v
+++ b/theories/fresh_numbers.v
@@ -28,9 +28,7 @@ Proof.
   split.
   * apply _.
   * intros. unfold fresh, Nfresh.
-    setoid_replace X with Y; [done |].
-    by apply elem_of_equiv.
+    setoid_replace X with Y; [done |]. by apply elem_of_equiv.
   * intros X E. assert (1 ≤ 0)%N as []; [| done].
-    apply N.add_le_mono_r with (Nmax X).
-    by apply Nmax_max.
+    apply N.add_le_mono_r with (Nmax X). by apply Nmax_max.
 Qed.
diff --git a/theories/list.v b/theories/list.v
index 4aa9139452f6f33d1e65ef14e9ef0a958c356be9..d874ece4d315886b1540e9abe03caa839d27cbff 100644
--- a/theories/list.v
+++ b/theories/list.v
@@ -2,8 +2,7 @@
 (* This file is distributed under the terms of the BSD license. *)
 (** This file collects general purpose definitions and theorems on lists that
 are not in the Coq standard library. *)
-
-Require Import Permutation.
+Require Export Permutation.
 Require Export numbers base decidable option.
 
 Arguments length {_} _.
@@ -12,13 +11,10 @@ Arguments app {_} _ _.
 Arguments Permutation {_} _ _.
 Arguments Forall_cons {_} _ _ _ _ _.
 
-Notation Forall_nil_2 := Forall_nil.
-Notation Forall_cons_2 := Forall_cons.
-
 Notation tail := tl.
 Notation take := firstn.
 Notation drop := skipn.
-Notation take_drop := firstn_skipn.
+
 Arguments take {_} !_ !_ /.
 Arguments drop {_} !_ !_ /.
 
@@ -29,6 +25,15 @@ Notation "(++)" := app (only parsing) : C_scope.
 Notation "( l ++)" := (app l) (only parsing) : C_scope.
 Notation "(++ k )" := (λ l, app l k) (only parsing) : C_scope.
 
+Infix "≡ₚ" := Permutation (at level 70, no associativity) : C_scope.
+Notation "(≡ₚ)" := Permutation (only parsing) : C_scope.
+Notation "( x ≡ₚ)" := (Permutation x) (only parsing) : C_scope.
+Notation "(≡ₚ x )" := (λ y, y ≡ₚ x) (only parsing) : C_scope.
+Notation "(≢ₚ)" := (λ x y, ¬x ≡ₚ y) (only parsing) : C_scope.
+Notation "x ≢ₚ y":= (¬x ≡ₚ y) (at level 70, no associativity) : C_scope.
+Notation "( x ≢ₚ)" := (λ y, x ≢ₚ y) (only parsing) : C_scope.
+Notation "(≢ₚ x )" := (λ y, y ≢ₚ x) (only parsing) : C_scope.
+
 (** * Definitions *)
 (** The operation [l !! i] gives the [i]th element of the list [l], or [None]
 in case [i] is out of bounds. *)
@@ -36,11 +41,7 @@ Instance list_lookup {A} : Lookup nat A (list A) :=
   fix go (i : nat) (l : list A) {struct l} : option A :=
   match l with
   | [] => None
-  | x :: l =>
-    match i with
-    | 0 => Some x
-    | S i => @lookup _ _ _ go i l
-    end
+  | x :: l => match i with 0 => Some x | S i => @lookup _ _ _ go i l end
   end.
 
 (** The operation [alter f i l] applies the function [f] to the [i]th element
@@ -49,11 +50,7 @@ Instance list_alter {A} (f : A → A) : AlterD nat A (list A) f :=
   fix go (i : nat) (l : list A) {struct l} :=
   match l with
   | [] => []
-  | x :: l =>
-    match i with
-    | 0 => f x :: l
-    | S i => x :: @alter _ _ _ f go i l
-    end
+  | x :: l => match i with 0 => f x :: l | S i => x :: @alter _ _ _ f go i l end
   end.
 
 (** The operation [delete i l] removes the [i]th element of [l] and moves
@@ -63,17 +60,12 @@ Instance list_delete {A} : Delete nat (list A) :=
   fix go (i : nat) (l : list A) {struct l} : list A :=
   match l with
   | [] => []
-  | x :: l =>
-    match i with
-    | 0 => l
-    | S i => x :: @delete _ _ go i l
-    end
+  | x :: l => match i with 0 => l | S i => x :: @delete _ _ go i l end
   end.
 
 (** The operation [<[i:=x]> l] overwrites the element at position [i] with the
 value [x]. In case [i] is out of bounds, the list is returned unchanged. *)
-Instance list_insert {A} : Insert nat A (list A) := λ i x,
-  alter (λ _, x) i.
+Instance list_insert {A} : Insert nat A (list A) := λ i x, alter (λ _, x) i.
 
 (** The function [option_list o] converts an element [Some x] into the
 singleton list [[x]], and [None] into the empty list [[]]. *)
@@ -86,32 +78,23 @@ Instance list_filter {A} : Filter A (list A) :=
   match l with
   | [] => []
   | x :: l =>
-     if decide (P x)
-     then x :: @filter _ _ (@go) _ _ l
-     else @filter _ _ (@go) _ _ l
+    if decide (P x)
+    then x :: @filter _ _ (@go) _ _ l
+    else @filter _ _ (@go) _ _ l
   end.
 
 (** The function [replicate n x] generates a list with length [n] of elements
 with value [x]. *)
 Fixpoint replicate {A} (n : nat) (x : A) : list A :=
-  match n with
-  | 0 => []
-  | S n => x :: replicate n x
-  end.
+  match n with 0 => [] | S n => x :: replicate n x end.
 
 (** The function [reverse l] returns the elements of [l] in reverse order. *)
 Definition reverse {A} (l : list A) : list A := rev_append l [].
 
 Fixpoint last' {A} (x : A) (l : list A) : A :=
-  match l with
-  | [] => x
-  | x :: l => last' x l
-  end.
+  match l with [] => x | x :: l => last' x l end.
 Definition last {A} (l : list A) : option A :=
-  match l with
-  | [] => None
-  | x :: l => Some (last' x l)
-  end.
+  match l with [] => None | x :: l => Some (last' x l) end.
 
 (** The function [resize n y l] takes the first [n] elements of [l] in case
 [length l ≤ n], and otherwise appends elements with value [x] to [l] to obtain
@@ -119,11 +102,7 @@ a list of length [n]. *)
 Fixpoint resize {A} (n : nat) (y : A) (l : list A) : list A :=
   match l with
   | [] => replicate n y
-  | x :: l =>
-    match n with
-    | 0 => []
-    | S n => x :: resize n y l
-    end
+  | x :: l => match n with 0 => [] | S n => x :: resize n y l end
   end.
 Arguments resize {_} !_ _ !_.
 
@@ -142,48 +121,41 @@ Definition foldl {A B} (f : A → B → A) : A → list B → A :=
 Instance list_ret: MRet list := λ A x, x :: @nil A.
 Instance list_fmap {A B} (f : A → B) : FMapD list f :=
   fix go (l : list A) :=
-  match l with
-  | [] => []
-  | x :: l => f x :: @fmap _ _ _ f go l
-  end.
+  match l with [] => [] | x :: l => f x :: @fmap _ _ _ f go l end.
 Instance list_bind {A B} (f : A → list B) : MBindD list f :=
   fix go (l : list A) :=
-  match l with
-  | [] => []
-  | x :: l => f x ++ @mbind _ _ _ f go l
-  end.
+  match l with [] => [] | x :: l => f x ++ @mbind _ _ _ f go l end.
 Instance list_join: MJoin list :=
   fix go A (ls : list (list A)) : list A :=
-  match ls with
-  | [] => []
-  | l :: ls => l ++ @mjoin _ go _ ls
-  end.
+  match ls with [] => [] | l :: ls => l ++ @mjoin _ go _ ls end.
 
 (** We define stronger variants of map and fold that allow the mapped
 function to use the index of the elements. *)
 Definition imap_go {A B} (f : nat → A → B) : nat → list A → list B :=
   fix go (n : nat) (l : list A) :=
-  match l with
-  | [] => []
-  | x :: l => f n x :: go (S n) l
-  end.
+  match l with [] => [] | x :: l => f n x :: go (S n) l end.
 Definition imap {A B} (f : nat → A → B) : list A → list B := imap_go f 0.
 
-Definition ifoldr {A B} (f : nat → B → A → A)
-    (a : nat → A) : nat → list B → A :=
-  fix go (n : nat) (l : list B) : A :=
-  match l with
-  | nil => a n
-  | b :: l => f n b (go (S n) l)
-  end.
+Definition ifoldr {A B} (f : nat → B → A → A) (a : nat → A) :
+  nat → list B → A := fix go n l :=
+  match l with [] => a n | b :: l => f n b (go (S n) l) end.
+
+Definition zipped_map {A B} (f : list A → list A → A → B) :
+  list A → list A → list B := fix go l k :=
+  match k with [] => [] | x :: k => f l k x :: go (x :: l) k end.
+
+Inductive zipped_Forall {A} (P : list A → list A → A → Prop) :
+    list A → list A → Prop :=
+  | zipped_Forall_nil l : zipped_Forall P l []
+  | zipped_Forall_cons l k x :
+     P l k x → zipped_Forall P (x :: l) k → zipped_Forall P l (x :: k).
+Arguments zipped_Forall_nil {_ _} _.
+Arguments zipped_Forall_cons {_ _} _ _ _ _ _.
 
 (** Zipping lists. *)
 Definition zip_with {A B C} (f : A → B → C) : list A → list B → list C :=
   fix go l1 l2 :=
-  match l1, l2 with
-  | x1 :: l1, x2 :: l2 => f x1 x2 :: go l1 l2
-  | _ , _ => []
-  end.
+  match l1, l2 with x1 :: l1, x2 :: l2 => f x1 x2 :: go l1 l2 | _ , _ => [] end.
 Notation zip := (zip_with pair).
 
 (** The function [permutations l] yields all permutations of [l]. *)
@@ -202,6 +174,8 @@ Fixpoint permutations {A} (l : list A) : list (list A) :=
 The predicate [prefix_of] holds if the first list is a prefix of the second. *)
 Definition suffix_of {A} : relation (list A) := λ l1 l2, ∃ k, l2 = k ++ l1.
 Definition prefix_of {A} : relation (list A) := λ l1 l2, ∃ k, l2 = l1 ++ k.
+Infix "`suffix_of`" := suffix_of (at level 70) : C_scope.
+Infix "`prefix_of`" := prefix_of (at level 70) : C_scope.
 
 Section prefix_suffix_ops.
   Context `{∀ x y : A, Decision (x = y)}.
@@ -212,9 +186,9 @@ Section prefix_suffix_ops.
     | [], l2 => ([], l2, [])
     | l1, [] => (l1, [], [])
     | x1 :: l1, x2 :: l2 =>
-       if decide_rel (=) x1 x2
-       then snd_map (x1 ::) (go l1 l2)
-       else (x1 :: l1, x2 :: l2, [])
+      if decide_rel (=) x1 x2
+      then snd_map (x1 ::) (go l1 l2)
+      else (x1 :: l1, x2 :: l2, [])
     end.
   Definition max_suffix_of (l1 l2 : list A) : list A * list A * list A :=
     match max_prefix_of (reverse l1) (reverse l2) with
@@ -229,15 +203,89 @@ End prefix_suffix_ops.
 from [l1] without changing the order. *)
 Inductive sublist {A} : relation (list A) :=
   | sublist_nil : sublist [] []
-  | sublist_cons x l1 l2 : sublist l1 l2 → sublist (x :: l1) (x :: l2)
-  | sublist_cons_skip x l1 l2 : sublist l1 l2 → sublist l1 (x :: l2).
+  | sublist_skip x l1 l2 : sublist l1 l2 → sublist (x :: l1) (x :: l2)
+  | sublist_insert x l1 l2 : sublist l1 l2 → sublist l1 (x :: l2).
+Infix "`sublist`" := sublist (at level 70) : C_scope.
+
+(** A list [l2] contains a list [l1] if [l2] is obtained by removing elements
+from [l1] without changing the order. *)
+Inductive contains {A} : relation (list A) :=
+  | contains_nil : contains [] []
+  | contains_skip x l1 l2 : contains l1 l2 → contains (x :: l1) (x :: l2)
+  | contains_swap x y l : contains (y :: x :: l) (x :: y :: l)
+  | contains_insert x l1 l2 : contains l1 l2 → contains l1 (x :: l2)
+  | contains_trans l1 l2 l3 : contains l1 l2 → contains l2 l3 → contains l1 l3.
+Infix "`contains`" := contains (at level 70) : C_scope.
+
+Section contains_dec_help.
+  Context {A} {dec : ∀ x y : A, Decision (x = y)}.
+
+  Fixpoint list_remove (x : A) (l : list A) : option (list A) :=
+    match l with
+    | [] => None
+    | y :: l => if decide (x = y) then Some l else (y ::) <$> list_remove x l
+    end.
+  Fixpoint list_remove_list (k : list A) (l : list A) : option (list A) :=
+    match k with
+    | [] => Some l
+    | x :: k => list_remove x l ≫= list_remove_list k
+    end.
+End contains_dec_help.
 
 (** The [same_length] view allows convenient induction over two lists with the
 same length. *)
 Inductive same_length {A B} : list A → list B → Prop :=
   | same_length_nil : same_length [] []
-  | same_length_cons x y l k :
-     same_length l k → same_length (x :: l) (y :: k).
+  | same_length_cons x1 x2 l1 l2 :
+     same_length l1 l2 → same_length (x1 :: l1) (x2 :: l2).
+Infix "`same_length`" := same_length (at level 70) : C_scope.
+
+(** Set operations on lists *)
+Section list_set.
+  Context {A} {dec : ∀ x y : A, Decision (x = y)}.
+
+  Global Instance elem_of_list_dec {dec : ∀ x y : A, Decision (x = y)}
+    (x : A) : ∀ l, Decision (x ∈ l).
+  Proof.
+   refine (
+    fix go l :=
+    match l return Decision (x ∈ l) with
+    | [] => right _
+    | y :: l => cast_if_or (decide (x = y)) (go l)
+    end); clear go dec; subst; try (by constructor); abstract by inversion 1.
+  Defined.
+
+  Fixpoint remove_dups (l : list A) : list A :=
+    match l with
+    | [] => []
+    | x :: l =>
+      if decide_rel (∈) x l then remove_dups l else x :: remove_dups l
+    end.
+
+  Fixpoint list_difference (l k : list A) : list A :=
+    match l with
+    | [] => []
+    | x :: l =>
+      if decide_rel (∈) x k
+      then list_difference l k
+      else x :: list_difference l k
+    end.
+  Fixpoint list_intersection (l k : list A) : list A :=
+    match l with
+    | [] => []
+    | x :: l =>
+      if decide_rel (∈) x k
+      then x :: list_intersection l k
+      else list_intersection l k
+    end.
+  Definition list_intersection_with (f : A → A → option A) :
+    list A → list A → list A := fix go l k :=
+    match l with
+    | [] => []
+    | x :: l => foldr (λ y,
+        match f x y with None => id | Some z => (z ::) end) (go l k) k
+    end.
+End list_set.
 
 (** * Basic tactics on lists *)
 (** The tactic [discriminate_list_equality] discharges a goal if it contains
@@ -245,44 +293,41 @@ a list equality involving [(::)] and [(++)] of two lists that have a different
 length as one of its hypotheses. *)
 Tactic Notation "discriminate_list_equality" hyp(H) :=
   apply (f_equal length) in H;
-  repeat (simpl in H || rewrite app_length in H);
-  exfalso; lia.
+  repeat (simpl in H || rewrite app_length in H); exfalso; lia.
 Tactic Notation "discriminate_list_equality" :=
-  solve [repeat_on_hyps (fun H => discriminate_list_equality H)].
+  match goal with
+  | H : @eq (list _) _ _ |- _ => discriminate_list_equality H
+  end.
 
 (** The tactic [simplify_list_equality] simplifies hypotheses involving
 equalities on lists using injectivity of [(::)] and [(++)]. Also, it simplifies
 lookups in singleton lists. *)
-Lemma cons_inv {A} (l1 l2 : list A) x1 x2 :
-  x1 :: l1 = x2 :: l2 → x1 = x2 ∧ l1 = l2.
-Proof. by injection 1. Qed.
-
-Ltac simplify_list_equality := repeat
-  match goal with
-  | H : _ :: _ = _ :: _ |- _ =>
-     apply cons_inv in H; destruct H
-     (* to circumvent bug #2939 in some situations *)
+Ltac simplify_list_equality :=
+  repeat match goal with
+  | _ => progress simplify_equality
   | H : _ ++ _ = _ ++ _ |- _ => first
-     [ apply app_inj_tail in H; destruct H
-     | apply app_inv_head in H
-     | apply app_inv_tail in H ]
+    [ apply app_inj_tail in H; destruct H
+    | apply app_inv_head in H | apply app_inv_tail in H ]
   | H : [?x] !! ?i = Some ?y |- _ =>
-     destruct i; [change (Some x = Some y) in H|discriminate]
-  | _ => progress simplify_equality
-  | H : _ |- _ => discriminate_list_equality H
-  end.
+    destruct i; [change (Some x = Some y) in H | discriminate]
+  end;
+  try discriminate_list_equality.
 
 (** * General theorems *)
 Section general_properties.
 Context {A : Type}.
+Implicit Types x y z : A.
+Implicit Types l k : list A.
 
-Global Instance: ∀ x : A, Injective (=) (=) (x ::).
+Global Instance: Injective2 (=) (=) (=) (@cons A).
+Proof. by injection 1. Qed.
+Global Instance: ∀ x, Injective (=) (=) (x ::).
 Proof. by injection 1. Qed.
-Global Instance: ∀ l : list A, Injective (=) (=) (:: l).
+Global Instance: ∀ l, Injective (=) (=) (:: l).
 Proof. by injection 1. Qed.
-Global Instance: ∀ k : list A, Injective (=) (=) (k ++).
+Global Instance: ∀ k, Injective (=) (=) (k ++).
 Proof. intros ???. apply app_inv_head. Qed.
-Global Instance: ∀ k : list A, Injective (=) (=) (++ k).
+Global Instance: ∀ k, Injective (=) (=) (++ k).
 Proof. intros ???. apply app_inv_tail. Qed.
 Global Instance: Associative (=) (@app A).
 Proof. intros ???. apply app_assoc. Qed.
@@ -291,49 +336,44 @@ Proof. done. Qed.
 Global Instance: RightId (=) [] (@app A).
 Proof. intro. apply app_nil_r. Qed.
 
-Lemma app_inj (l1 k1 l2 k2 : list A) :
-  length l1 = length k1 →
-  l1 ++ l2 = k1 ++ k2 → l1 = k1 ∧ l2 = k2.
+Lemma app_nil l1 l2 : l1 ++ l2 = [] ↔ l1 = [] ∧ l2 = [].
+Proof. split. apply app_eq_nil. by intros [??]; subst. Qed.
+Lemma app_singleton l1 l2 x :
+  l1 ++ l2 = [x] ↔ l1 = [] ∧ l2 = [x] ∨ l1 = [x] ∧ l2 = [].
+Proof. split. apply app_eq_unit. by intros [[??]|[??]]; subst. Qed.
+
+Lemma cons_middle x l1 l2 : l1 ++ x :: l2 = l1 ++ [x] ++ l2.
+Proof. done. Qed.
+Lemma app_inj l1 k1 l2 k2 :
+  length l1 = length k1 → l1 ++ l2 = k1 ++ k2 → l1 = k1 ∧ l2 = k2.
 Proof. revert k1. induction l1; intros [|??]; naive_solver. Qed.
 
-Lemma list_eq (l1 l2 : list A) : (∀ i, l1 !! i = l2 !! i)%C → l1 = l2.
+Lemma list_eq l1 l2 : (∀ i, l1 !! i = l2 !! i) → l1 = l2.
 Proof.
   revert l2. induction l1; intros [|??] H.
   * done.
   * discriminate (H 0).
   * discriminate (H 0).
-  * f_equal; [by injection (H 0) |].
-    apply IHl1. intro. apply (H (S _)).
+  * f_equal; [by injection (H 0) |]. apply IHl1. intro. apply (H (S _)).
 Qed.
-Lemma list_eq_nil (l : list A) : (∀ i, l !! i = None) → l = nil.
+Lemma list_eq_nil l : (∀ i, l !! i = None) → l = nil.
 Proof. intros. by apply list_eq. Qed.
 
-Global Instance list_eq_dec {dec : ∀ x y : A, Decision (x = y)} : ∀ l k,
+Global Instance list_eq_dec {dec : ∀ x y, Decision (x = y)} : ∀ l k,
   Decision (l = k) := list_eq_dec dec.
-Definition list_singleton_dec (l : list A) :
-  { x | l = [x] } + { length l ≠ 1 }.
-Proof.
- by refine (
-  match l with
-  | [x] => inleft (x ↾ _)
-  | _ => inright _
-  end).
-Defined.
+Definition list_singleton_dec l : { x | l = [x] } + { length l ≠ 1 }.
+Proof. by refine match l with [x] => inleft (x↾_) | _ => inright _ end. Defined.
 
-Global Instance: Proper (Permutation ==> (=)) (@length A).
-Proof. induction 1; simpl; auto with lia. Qed.
-
-Lemma nil_or_length_pos (l : list A) : l = [] ∨ length l ≠ 0.
+Lemma nil_or_length_pos l : l = [] ∨ length l ≠ 0.
 Proof. destruct l; simpl; auto with lia. Qed.
-Lemma nil_length (l : list A) : length l = 0 → l = [].
+Lemma nil_length l : length l = 0 → l = [].
 Proof. by destruct l. Qed.
 Lemma lookup_nil i : @nil A !! i = None.
 Proof. by destruct i. Qed.
-Lemma lookup_tail (l : list A) i : tail l !! i = l !! S i.
+Lemma lookup_tail l i : tail l !! i = l !! S i.
 Proof. by destruct l. Qed.
 
-Lemma lookup_lt_length (l : list A) i :
-  is_Some (l !! i) ↔ i < length l.
+Lemma lookup_lt_length l i : is_Some (l !! i) ↔ i < length l.
 Proof.
   revert i. induction l.
   * split; by inversion 1.
@@ -341,115 +381,85 @@ Proof.
     + split; eauto with arith.
     + by rewrite <-NPeano.Nat.succ_lt_mono.
 Qed.
-Lemma lookup_lt_length_1 (l : list A) i :
-  is_Some (l !! i) → i < length l.
+Lemma lookup_lt_length_1 l i : is_Some (l !! i) → i < length l.
 Proof. apply lookup_lt_length. Qed.
-Lemma lookup_lt_length_alt (l : list A) i x :
-  l !! i = Some x → i < length l.
+Lemma lookup_lt_length_alt l i x : l !! i = Some x → i < length l.
 Proof. intros Hl. by rewrite <-lookup_lt_length, Hl. Qed.
-Lemma lookup_lt_length_2 (l : list A) i :
-  i < length l → is_Some (l !! i).
+Lemma lookup_lt_length_2 l i : i < length l → is_Some (l !! i).
 Proof. apply lookup_lt_length. Qed.
 
-Lemma lookup_ge_length (l : list A) i :
-  l !! i = None ↔ length l ≤ i.
+Lemma lookup_ge_length l i : l !! i = None ↔ length l ≤ i.
 Proof. rewrite eq_None_not_Some, lookup_lt_length. lia. Qed.
-Lemma lookup_ge_length_1 (l : list A) i :
-  l !! i = None → length l ≤ i.
+Lemma lookup_ge_length_1 l i : l !! i = None → length l ≤ i.
 Proof. by rewrite lookup_ge_length. Qed.
-Lemma lookup_ge_length_2 (l : list A) i :
-  length l ≤ i → l !! i = None.
+Lemma lookup_ge_length_2 l i : length l ≤ i → l !! i = None.
 Proof. by rewrite lookup_ge_length. Qed.
 
-Lemma list_eq_length_eq (l1 l2 : list A) :
+Lemma list_eq_length_eq l1 l2 :
   length l2 = length l1 →
-  (∀ i x y, l1 !! i = Some x → l2 !! i = Some y → x = y) →
-  l1 = l2.
+  (∀ i x y, l1 !! i = Some x → l2 !! i = Some y → x = y) → l1 = l2.
 Proof.
   intros Hlength Hlookup. apply list_eq. intros i.
   destruct (l2 !! i) as [x|] eqn:E.
-  * feed inversion (lookup_lt_length_2 l1 i) as [y].
-    { pose proof (lookup_lt_length_alt l2 i x E). lia. }
-    f_equal. eauto.
+  * feed inversion (lookup_lt_length_2 l1 i) as [y]; [|eauto with f_equal].
+    pose proof (lookup_lt_length_alt l2 i x E). lia.
   * rewrite lookup_ge_length in E |- *. lia.
 Qed.
 
-Lemma lookup_app_l (l1 l2 : list A) i :
-  i < length l1 →
-  (l1 ++ l2) !! i = l1 !! i.
+Lemma lookup_app_l l1 l2 i :
+  i < length l1 → (l1 ++ l2) !! i = l1 !! i.
 Proof. revert i. induction l1; intros [|?]; simpl; auto with lia. Qed.
-Lemma lookup_app_l_Some (l1 l2 : list A) i x :
-  l1 !! i = Some x →
-  (l1 ++ l2) !! i = Some x.
+Lemma lookup_app_l_Some l1 l2 i x :
+  l1 !! i = Some x → (l1 ++ l2) !! i = Some x.
 Proof. intros. rewrite lookup_app_l; eauto using lookup_lt_length_alt. Qed.
-
-Lemma lookup_app_r (l1 l2 : list A) i :
-  (l1 ++ l2) !! (length l1 + i) = l2 !! i.
+Lemma lookup_app_r l1 l2 i : (l1 ++ l2) !! (length l1 + i) = l2 !! i.
 Proof.
-  revert i.
-  induction l1; intros [|i]; simpl in *; simplify_equality; auto.
+  revert i. induction l1; intros [|i]; simpl in *; simplify_equality; auto.
 Qed.
-Lemma lookup_app_r_alt (l1 l2 : list A) i :
-  length l1 ≤ i →
-  (l1 ++ l2) !! i = l2 !! (i - length l1).
+Lemma lookup_app_r_alt l1 l2 i :
+  length l1 ≤ i → (l1 ++ l2) !! i = l2 !! (i - length l1).
 Proof.
   intros. assert (i = length l1 + (i - length l1)) as Hi by lia.
   rewrite Hi at 1. by apply lookup_app_r.
 Qed.
-Lemma lookup_app_r_Some (l1 l2 : list A) i x :
-  l2 !! i = Some x →
-  (l1 ++ l2) !! (length l1 + i) = Some x.
+Lemma lookup_app_r_Some l1 l2 i x :
+  l2 !! i = Some x → (l1 ++ l2) !! (length l1 + i) = Some x.
 Proof. by rewrite lookup_app_r. Qed.
-Lemma lookup_app_r_Some_alt (l1 l2 : list A) i x :
-  length l1 ≤ i →
-  l2 !! (i - length l1) = Some x →
-  (l1 ++ l2) !! i = Some x.
+Lemma lookup_app_r_Some_alt l1 l2 i x :
+  length l1 ≤ i → l2 !! (i - length l1) = Some x → (l1 ++ l2) !! i = Some x.
 Proof. intro. by rewrite lookup_app_r_alt. Qed.
-
-Lemma lookup_app_inv (l1 l2 : list A) i x :
-  (l1 ++ l2) !! i = Some x →
-  l1 !! i = Some x ∨ l2 !! (i - length l1) = Some x.
+Lemma lookup_app_inv l1 l2 i x :
+  (l1 ++ l2) !! i = Some x → l1 !! i = Some x ∨ l2 !! (i - length l1) = Some x.
 Proof.
-  revert i.
-  induction l1; intros [|i] ?; simpl in *; simplify_equality; auto.
+  revert i. induction l1; intros [|i] ?; simpl in *; simplify_equality; auto.
 Qed.
-
-Lemma list_lookup_middle (l1 l2 : list A) (x : A) :
-  (l1 ++ x :: l2) !! length l1 = Some x.
+Lemma list_lookup_middle l1 l2 x : (l1 ++ x :: l2) !! length l1 = Some x.
 Proof. by induction l1; simpl. Qed.
 
-Lemma alter_length (f : A → A) l i :
-  length (alter f i l) = length l.
+Lemma alter_length f l i : length (alter f i l) = length l.
 Proof. revert i. induction l; intros [|?]; simpl; auto with lia. Qed.
-Lemma insert_length (l : list A) i x :
-  length (<[i:=x]>l) = length l.
+Lemma insert_length l i x : length (<[i:=x]>l) = length l.
 Proof. apply alter_length. Qed.
 
-Lemma list_lookup_alter (f : A → A) l i :
-  alter f i l !! i = f <$> l !! i.
+Lemma list_lookup_alter f l i : alter f i l !! i = f <$> l !! i.
 Proof. revert i. induction l. done. intros [|i]. done. apply (IHl i). Qed.
-Lemma list_lookup_alter_ne (f : A → A) l i j :
+Lemma list_lookup_alter_ne f l i j :
   i ≠ j → alter f i l !! j = l !! j.
 Proof.
   revert i j. induction l; [done|].
   intros [|i] [|j] ?; try done. apply (IHl i). congruence.
 Qed.
-Lemma list_lookup_insert (l : list A) i x :
-  i < length l →
-  <[i:=x]>l !! i = Some x.
+Lemma list_lookup_insert l i x : i < length l → <[i:=x]>l !! i = Some x.
 Proof.
-  intros Hi. unfold insert, list_insert.
-  rewrite list_lookup_alter.
+  intros Hi. unfold insert, list_insert. rewrite list_lookup_alter.
   by feed inversion (lookup_lt_length_2 l i).
 Qed.
-Lemma list_lookup_insert_ne (l : list A) i j x :
+Lemma list_lookup_insert_ne l i j x :
   i ≠ j → <[i:=x]>l !! j = l !! j.
 Proof. apply list_lookup_alter_ne. Qed.
 
-Lemma list_lookup_other (l : list A) i x :
-  length l ≠ 1 →
-  l !! i = Some x →
-  ∃ j y, j ≠ i ∧ l !! j = Some y.
+Lemma list_lookup_other l i x :
+  length l ≠ 1 → l !! i = Some x → ∃ j y, j ≠ i ∧ l !! j = Some y.
 Proof.
   intros Hl Hi.
   destruct i; destruct l as [|x0 [|x1 l]]; simpl in *; simplify_equality.
@@ -457,146 +467,165 @@ Proof.
   * by exists 0 x0.
 Qed.
 
-Lemma alter_app_l (f : A → A) (l1 l2 : list A) i :
-  i < length l1 →
-  alter f i (l1 ++ l2) = alter f i l1 ++ l2.
+Lemma alter_app_l f l1 l2 i :
+  i < length l1 → alter f i (l1 ++ l2) = alter f i l1 ++ l2.
 Proof.
-  revert i.
-  induction l1; intros [|?] ?; simpl in *; f_equal; auto with lia.
+  revert i. induction l1; intros [|?] ?; simpl in *; f_equal; auto with lia.
 Qed.
-Lemma alter_app_r (f : A → A) (l1 l2 : list A) i :
+Lemma alter_app_r f l1 l2 i :
   alter f (length l1 + i) (l1 ++ l2) = l1 ++ alter f i l2.
-Proof.
-  revert i.
-  induction l1; intros [|?]; simpl in *; f_equal; auto.
-Qed.
-Lemma alter_app_r_alt (f : A → A) (l1 l2 : list A) i :
-  length l1 ≤ i →
-  alter f i (l1 ++ l2) = l1 ++ alter f (i - length l1) l2.
+Proof. revert i. induction l1; intros [|?]; simpl in *; f_equal; auto. Qed.
+Lemma alter_app_r_alt f l1 l2 i :
+  length l1 ≤ i → alter f i (l1 ++ l2) = l1 ++ alter f (i - length l1) l2.
 Proof.
   intros. assert (i = length l1 + (i - length l1)) as Hi by lia.
   rewrite Hi at 1. by apply alter_app_r.
 Qed.
 
-Lemma insert_app_l (l1 l2 : list A) i x :
-  i < length l1 →
-  <[i:=x]>(l1 ++ l2) = <[i:=x]>l1 ++ l2.
+Lemma insert_app_l l1 l2 i x :
+  i < length l1 → <[i:=x]>(l1 ++ l2) = <[i:=x]>l1 ++ l2.
 Proof. apply alter_app_l. Qed.
-Lemma insert_app_r (l1 l2 : list A) i x :
-  <[length l1 + i:=x]>(l1 ++ l2) = l1 ++ <[i:=x]>l2.
+Lemma insert_app_r l1 l2 i x : <[length l1+i:=x]>(l1 ++ l2) = l1 ++ <[i:=x]>l2.
 Proof. apply alter_app_r. Qed.
-Lemma insert_app_r_alt (l1 l2 : list A) i x :
-  length l1 ≤ i →
-  <[i:=x]>(l1 ++ l2) = l1 ++ <[i - length l1:=x]>l2.
+Lemma insert_app_r_alt l1 l2 i x :
+  length l1 ≤ i → <[i:=x]>(l1 ++ l2) = l1 ++ <[i - length l1:=x]>l2.
 Proof. apply alter_app_r_alt. Qed.
 
-Lemma insert_consecutive_length (l : list A) i k :
+Lemma insert_consecutive_length l i k :
   length (insert_consecutive i k l) = length l.
 Proof. revert i. by induction k; intros; simpl; rewrite ?insert_length. Qed.
 
-Lemma delete_middle (l1 l2 : list A) x :
-  delete (length l1) (l1 ++ x :: l2) = l1 ++ l2.
+Lemma delete_middle l1 l2 x : delete (length l1) (l1 ++ x :: l2) = l1 ++ l2.
 Proof. induction l1; simpl; f_equal; auto. Qed.
 
 (** ** Properties of the [elem_of] predicate *)
-Lemma not_elem_of_nil (x : A) : x ∉ [].
+Lemma not_elem_of_nil x : x ∉ [].
 Proof. by inversion 1. Qed.
-Lemma elem_of_nil (x : A) : x ∈ [] ↔ False.
+Lemma elem_of_nil x : x ∈ [] ↔ False.
 Proof. intuition. by destruct (not_elem_of_nil x). Qed.
-Lemma elem_of_nil_inv (l : list A) : (∀ x, x ∉ l) → l = [].
+Lemma elem_of_nil_inv l : (∀ x, x ∉ l) → l = [].
 Proof. destruct l. done. by edestruct 1; constructor. Qed.
-Lemma elem_of_cons (l : list A) x y :
-  x ∈ y :: l ↔ x = y ∨ x ∈ l.
+Lemma elem_of_cons l x y : x ∈ y :: l ↔ x = y ∨ x ∈ l.
 Proof.
   split.
   * inversion 1; subst. by left. by right.
   * intros [?|?]; subst. by left. by right.
 Qed.
-Lemma not_elem_of_cons (l : list A) x y :
-  x ∉ y :: l ↔ x ≠ y ∧ x ∉ l.
+Lemma not_elem_of_cons l x y : x ∉ y :: l ↔ x ≠ y ∧ x ∉ l.
 Proof. rewrite elem_of_cons. tauto. Qed.
-Lemma elem_of_app (l1 l2 : list A) x :
-  x ∈ l1 ++ l2 ↔ x ∈ l1 ∨ x ∈ l2.
+Lemma elem_of_app l1 l2 x : x ∈ l1 ++ l2 ↔ x ∈ l1 ∨ x ∈ l2.
 Proof.
   induction l1.
-  * split; [by right|]. intros [Hx|]; [|done].
-    by destruct (elem_of_nil x).
+  * split; [by right|]. intros [Hx|]; [|done]. by destruct (elem_of_nil x).
   * simpl. rewrite !elem_of_cons, IHl1. tauto.
 Qed.
-Lemma not_elem_of_app (l1 l2 : list A) x :
-  x ∉ l1 ++ l2 ↔ x ∉ l1 ∧ x ∉ l2.
+Lemma not_elem_of_app l1 l2 x : x ∉ l1 ++ l2 ↔ x ∉ l1 ∧ x ∉ l2.
 Proof. rewrite elem_of_app. tauto. Qed.
-
-Lemma elem_of_list_singleton (x y : A) : x ∈ [y] ↔ x = y.
+Lemma elem_of_list_singleton x y : x ∈ [y] ↔ x = y.
 Proof. rewrite elem_of_cons, elem_of_nil. tauto. Qed.
 
-Global Instance elem_of_list_permutation_proper (x : A) :
-  Proper (Permutation ==> iff) (x ∈).
+Global Instance elem_of_list_permutation_proper x :
+  Proper ((≡ₚ) ==> iff) (x ∈).
 Proof. induction 1; rewrite ?elem_of_nil, ?elem_of_cons; intuition. Qed.
 
-Lemma elem_of_list_split (l : list A) x :
-  x ∈ l → ∃ l1 l2, l = l1 ++ x :: l2.
+Lemma elem_of_list_split l x : x ∈ l → ∃ l1 l2, l = l1 ++ x :: l2.
 Proof.
   induction 1 as [x l|x y l ? [l1 [l2 ?]]].
   * by eexists [], l.
   * subst. by exists (y :: l1) l2.
 Qed.
 
-Global Instance elem_of_list_dec {dec : ∀ x y : A, Decision (x = y)} :
-  ∀ (x : A) l, Decision (x ∈ l).
+Lemma elem_of_list_lookup_1 l x : x ∈ l → ∃ i, l !! i = Some x.
 Proof.
- intros x. refine (
-  fix go l :=
-  match l return Decision (x ∈ l) with
-  | [] => right (not_elem_of_nil _)
-  | y :: l => cast_if_or (decide_rel (=) x y) (go l)
-  end); clear go dec; subst; try (by constructor); by inversion 1.
-Defined.
-
-Lemma elem_of_list_lookup_1 (l : list A) x :
-  x ∈ l → ∃ i, l !! i = Some x.
-Proof.
-  induction 1 as [|???? IH].
-  * by exists 0.
-  * destruct IH as [i ?]; auto. by exists (S i).
+  induction 1 as [|???? IH]; [by exists 0 |].
+  destruct IH as [i ?]; auto. by exists (S i).
 Qed.
-Lemma elem_of_list_lookup_2 (l : list A) i x :
-  l !! i = Some x → x ∈ l.
+Lemma elem_of_list_lookup_2 l i x : l !! i = Some x → x ∈ l.
 Proof.
   revert i. induction l; intros [|i] ?;
     simpl; simplify_equality; constructor; eauto.
 Qed.
-Lemma elem_of_list_lookup (l : list A) x :
-  x ∈ l ↔ ∃ i, l !! i = Some x.
-Proof.
-  firstorder eauto using
-    elem_of_list_lookup_1, elem_of_list_lookup_2.
-Qed.
+Lemma elem_of_list_lookup l x : x ∈ l ↔ ∃ i, l !! i = Some x.
+Proof. firstorder eauto using elem_of_list_lookup_1, elem_of_list_lookup_2. Qed.
+
+(** ** Set operations on lists *)
+Section list_set.
+  Context {dec : ∀ x y, Decision (x = y)}.
+
+  Lemma elem_of_list_difference l k x :
+    x ∈ list_difference l k ↔ x ∈ l ∧ x ∉ k.
+  Proof.
+    split; induction l; simpl; try case_decide;
+      rewrite ?elem_of_nil, ?elem_of_cons; intuition congruence.
+  Qed.
+  Lemma list_difference_nodup l k : NoDup l → NoDup (list_difference l k).
+  Proof.
+    induction 1; simpl; try case_decide.
+    * constructor.
+    * done.
+    * constructor. rewrite elem_of_list_difference; intuition. done.
+  Qed.
+
+  Lemma elem_of_list_intersection l k x :
+    x ∈ list_intersection l k ↔ x ∈ l ∧ x ∈ k.
+  Proof.
+    split; induction l; simpl; repeat case_decide;
+      rewrite ?elem_of_nil, ?elem_of_cons; intuition congruence.
+  Qed.
+  Lemma list_intersection_nodup l k : NoDup l → NoDup (list_intersection l k).
+  Proof.
+    induction 1; simpl; try case_decide.
+    * constructor.
+    * constructor. rewrite elem_of_list_intersection; intuition. done.
+    * done.
+  Qed.
+
+  Lemma elem_of_list_intersection_with f l k x :
+    x ∈ list_intersection_with f l k ↔ ∃ x1 x2,
+      x1 ∈ l ∧ x2 ∈ k ∧ f x1 x2 = Some x.
+  Proof.
+    split.
+    * induction l as [|x1 l IH]; simpl.
+      + by rewrite elem_of_nil.
+      + intros Hx. setoid_rewrite elem_of_cons.
+        cut ((∃ x2, x2 ∈ k ∧ f x1 x2 = Some x)
+          ∨ x ∈ list_intersection_with f l k); [naive_solver|].
+        clear IH. revert Hx. generalize (list_intersection_with f l k).
+        induction k; simpl; [by auto|].
+        case_match; setoid_rewrite elem_of_cons; naive_solver.
+    * intros (x1 & x2 & Hx1 & Hx2 & Hx).
+      induction Hx1 as [x1 | x1 ? l ? IH]; simpl.
+      + generalize (list_intersection_with f l k).
+        induction Hx2; simpl; [by rewrite Hx; left |].
+        case_match; simpl; try setoid_rewrite elem_of_cons; auto.
+      + generalize (IH Hx). clear Hx IH Hx2.
+        generalize (list_intersection_with f l k).
+        induction k; simpl; intros; [done |].
+        case_match; simpl; rewrite ?elem_of_cons; auto.
+  Qed.
+End list_set.
 
 (** ** Properties of the [NoDup] predicate *)
 Lemma NoDup_nil : NoDup (@nil A) ↔ True.
 Proof. split; constructor. Qed.
-Lemma NoDup_cons (x : A) l : NoDup (x :: l) ↔ x ∉ l ∧ NoDup l.
+Lemma NoDup_cons x l : NoDup (x :: l) ↔ x ∉ l ∧ NoDup l.
 Proof. split. by inversion 1. intros [??]. by constructor. Qed.
-Lemma NoDup_cons_11 (x : A) l : NoDup (x :: l) → x ∉ l.
+Lemma NoDup_cons_11 x l : NoDup (x :: l) → x ∉ l.
 Proof. rewrite NoDup_cons. by intros [??]. Qed.
-Lemma NoDup_cons_12 (x : A) l : NoDup (x :: l) → NoDup l.
+Lemma NoDup_cons_12 x l : NoDup (x :: l) → NoDup l.
 Proof. rewrite NoDup_cons. by intros [??]. Qed.
-Lemma NoDup_singleton (x : A) : NoDup [x].
+Lemma NoDup_singleton x : NoDup [x].
 Proof. constructor. apply not_elem_of_nil. constructor. Qed.
 
-Lemma NoDup_app (l k : list A) :
-  NoDup (l ++ k) ↔ NoDup l ∧ (∀ x, x ∈ l → x ∉ k) ∧ NoDup k.
+Lemma NoDup_app l k : NoDup (l ++ k) ↔ NoDup l ∧ (∀ x, x ∈ l → x ∉ k) ∧ NoDup k.
 Proof.
   induction l; simpl.
-  * rewrite NoDup_nil.
-    setoid_rewrite elem_of_nil. naive_solver.
+  * rewrite NoDup_nil. setoid_rewrite elem_of_nil. naive_solver.
   * rewrite !NoDup_cons.
     setoid_rewrite elem_of_cons. setoid_rewrite elem_of_app. naive_solver.
 Qed.
 
-Global Instance NoDup_proper:
-  Proper (Permutation ==> iff) (@NoDup A).
+Global Instance NoDup_proper: Proper ((≡ₚ) ==> iff) (@NoDup A).
 Proof.
   induction 1 as [|x l k Hlk IH | |].
   * by rewrite !NoDup_nil.
@@ -605,559 +634,1043 @@ Proof.
   * intuition.
 Qed.
 
-Lemma NoDup_Permutation (l k : list A) :
-  NoDup l → NoDup k → (∀ x, x ∈ l ↔ x ∈ k) → Permutation l k.
+Lemma NoDup_Permutation l k : NoDup l → NoDup k → (∀ x, x ∈ l ↔ x ∈ k) → l ≡ₚ k.
 Proof.
   intros Hl. revert k. induction Hl as [|x l Hin ? IH].
-  * intros k _ Hk.
-    rewrite (elem_of_nil_inv k); [done |].
+  * intros k _ Hk. rewrite (elem_of_nil_inv k); [done |].
     intros x. rewrite <-Hk, elem_of_nil. intros [].
-  * intros k Hk Hlk.
-    destruct (elem_of_list_split k x) as [l1 [l2 ?]]; subst.
+  * intros k Hk Hlk. destruct (elem_of_list_split k x) as [l1 [l2 ?]]; subst.
     { rewrite <-Hlk. by constructor. }
     rewrite <-Permutation_middle, NoDup_cons in Hk.
-    destruct Hk as [??].
-    apply Permutation_cons_app, IH; [done |].
+    destruct Hk as [??]. apply Permutation_cons_app, IH; [done |].
     intros y. specialize (Hlk y).
-    rewrite <-Permutation_middle, !elem_of_cons in Hlk.
-    naive_solver.
+    rewrite <-Permutation_middle, !elem_of_cons in Hlk. naive_solver.
 Qed.
 
-Global Instance NoDup_dec {dec : ∀ x y : A, Decision (x = y)} :
-    ∀ (l : list A), Decision (NoDup l) :=
-  fix NoDup_dec l :=
-  match l return Decision (NoDup l) with
-  | [] => left NoDup_nil_2
-  | x :: l =>
-    match decide_rel (∈) x l with
-    | left Hin => right (λ H, NoDup_cons_11 _ _ H Hin)
-    | right Hin =>
-      match NoDup_dec l with
-      | left H => left (NoDup_cons_2 _ _ Hin H)
-      | right H => right (H ∘ NoDup_cons_12 _ _)
-      end
-    end
-  end.
-
-Section remove_dups.
-  Context `{!∀ x y : A, Decision (x = y)}.
+Section no_dup_dec.
+  Context `{!∀ x y, Decision (x = y)}.
 
-  Fixpoint remove_dups (l : list A) : list A :=
-    match l with
-    | [] => []
+  Global Instance NoDup_dec: ∀ l, Decision (NoDup l) :=
+    fix NoDup_dec l :=
+    match l return Decision (NoDup l) with
+    | [] => left NoDup_nil_2
     | x :: l =>
-      if decide_rel (∈) x l then remove_dups l else x :: remove_dups l
+      match decide_rel (∈) x l with
+      | left Hin => right (λ H, NoDup_cons_11 _ _ H Hin)
+      | right Hin =>
+        match NoDup_dec l with
+        | left H => left (NoDup_cons_2 _ _ Hin H)
+        | right H => right (H ∘ NoDup_cons_12 _ _)
+        end
+      end
     end.
 
-  Lemma elem_of_remove_dups l x :
-    x ∈ remove_dups l ↔ x ∈ l.
+  Lemma elem_of_remove_dups l x : x ∈ remove_dups l ↔ x ∈ l.
   Proof.
     split; induction l; simpl; repeat case_decide;
       rewrite ?elem_of_cons; intuition (simplify_equality; auto).
   Qed.
-
   Lemma remove_dups_nodup l : NoDup (remove_dups l).
   Proof.
     induction l; simpl; repeat case_decide; try constructor; auto.
     by rewrite elem_of_remove_dups.
   Qed.
-End remove_dups.
+End no_dup_dec.
 
 (** ** Properties of the [filter] function *)
-Lemma elem_of_list_filter `{∀ x : A, Decision (P x)} l x :
-  x ∈ filter P l ↔ P x ∧ x ∈ l.
-Proof.
-  unfold filter. induction l; simpl; repeat case_decide;
-     rewrite ?elem_of_nil, ?elem_of_cons; naive_solver.
-Qed.
-Lemma filter_nodup P `{∀ x : A, Decision (P x)} l :
-  NoDup l → NoDup (filter P l).
-Proof.
-  unfold filter. induction 1; simpl; repeat case_decide;
-    rewrite ?NoDup_nil, ?NoDup_cons, ?elem_of_list_filter; tauto.
-Qed.
+Section filter.
+  Context (P : A → Prop) `{∀ x, Decision (P x)}.
+
+  Lemma elem_of_list_filter l x : x ∈ filter P l ↔ P x ∧ x ∈ l.
+  Proof.
+    unfold filter. induction l; simpl; repeat case_decide;
+       rewrite ?elem_of_nil, ?elem_of_cons; naive_solver.
+  Qed.
+  Lemma filter_nodup l : NoDup l → NoDup (filter P l).
+  Proof.
+    unfold filter. induction 1; simpl; repeat case_decide;
+      rewrite ?NoDup_nil, ?NoDup_cons, ?elem_of_list_filter; tauto.
+  Qed.
+End filter.
 
 (** ** Properties of the [reverse] function *)
 Lemma reverse_nil : reverse [] = @nil A.
 Proof. done. Qed.
-Lemma reverse_singleton (x : A) : reverse [x] = [x].
+Lemma reverse_singleton x : reverse [x] = [x].
 Proof. done. Qed.
-Lemma reverse_cons (l : list A) x : reverse (x :: l) = reverse l ++ [x].
+Lemma reverse_cons l x : reverse (x :: l) = reverse l ++ [x].
 Proof. unfold reverse. by rewrite <-!rev_alt. Qed.
-Lemma reverse_snoc (l : list A) x : reverse (l ++ [x]) = x :: reverse l.
+Lemma reverse_snoc l x : reverse (l ++ [x]) = x :: reverse l.
 Proof. unfold reverse. by rewrite <-!rev_alt, rev_unit. Qed.
-Lemma reverse_app (l1 l2 : list A) :
-  reverse (l1 ++ l2) = reverse l2 ++ reverse l1.
+Lemma reverse_app l1 l2 : reverse (l1 ++ l2) = reverse l2 ++ reverse l1.
 Proof. unfold reverse. rewrite <-!rev_alt. apply rev_app_distr. Qed.
-Lemma reverse_length (l : list A) : length (reverse l) = length l.
+Lemma reverse_length l : length (reverse l) = length l.
 Proof. unfold reverse. rewrite <-!rev_alt. apply rev_length. Qed.
-Lemma reverse_involutive (l : list A) : reverse (reverse l) = l.
+Lemma reverse_involutive l : reverse (reverse l) = l.
 Proof. unfold reverse. rewrite <-!rev_alt. apply rev_involutive. Qed.
 
 (** ** Properties of the [take] function *)
-Lemma take_nil n :
-  take n (@nil A) = [].
+Definition take_drop := @firstn_skipn A.
+
+Lemma take_nil n : take n (@nil A) = [].
 Proof. by destruct n. Qed.
-Lemma take_app (l k : list A) :
-  take (length l) (l ++ k) = l.
+Lemma take_app l k : take (length l) (l ++ k) = l.
 Proof. induction l; simpl; f_equal; auto. Qed.
-Lemma take_app_alt (l k : list A) n :
-  n = length l →
-  take n (l ++ k) = l.
+Lemma take_app_alt l k n : n = length l → take n (l ++ k) = l.
 Proof. intros Hn. by rewrite Hn, take_app. Qed.
-Lemma take_app_le (l k : list A) n :
-  n ≤ length l →
-  take n (l ++ k) = take n l.
+Lemma take_app_le l k n : n ≤ length l → take n (l ++ k) = take n l.
 Proof.
-  revert n;
-  induction l; intros [|?] ?; simpl in *; f_equal; auto with lia.
+  revert n. induction l; intros [|?] ?; simpl in *; f_equal; auto with lia.
 Qed.
-Lemma take_app_ge (l k : list A) n :
-  length l ≤ n →
-  take n (l ++ k) = l ++ take (n - length l) k.
+Lemma take_app_ge l k n :
+  length l ≤ n → take n (l ++ k) = l ++ take (n - length l) k.
 Proof.
-  revert n;
-  induction l; intros [|?] ?; simpl in *; f_equal; auto with lia.
+  revert n. induction l; intros [|?] ?; simpl in *; f_equal; auto with lia.
 Qed.
-Lemma take_ge (l : list A) n :
-  length l ≤ n →
-  take n l = l.
+Lemma take_ge l n : length l ≤ n → take n l = l.
 Proof.
-  revert n.
-  induction l; intros [|?] ?; simpl in *; f_equal; auto with lia.
+  revert n. induction l; intros [|?] ?; simpl in *; f_equal; auto with lia.
 Qed.
 
-Lemma take_take (l : list A) n m :
-  take n (take m l) = take (min n m) l.
+Lemma take_take l n m : take n (take m l) = take (min n m) l.
 Proof. revert n m. induction l; intros [|?] [|?]; simpl; f_equal; auto. Qed.
-Lemma take_idempotent (l : list A) n :
-  take n (take n l) = take n l.
+Lemma take_idempotent l n : take n (take n l) = take n l.
 Proof. by rewrite take_take, Min.min_idempotent. Qed.
 
-Lemma take_length (l : list A) n :
-  length (take n l) = min n (length l).
+Lemma take_length l n : length (take n l) = min n (length l).
 Proof. revert n. induction l; intros [|?]; simpl; f_equal; done. Qed.
-Lemma take_length_alt (l : list A) n :
-  n ≤ length l →
-  length (take n l) = n.
+Lemma take_length_alt l n : n ≤ length l → length (take n l) = n.
 Proof. rewrite take_length. apply Min.min_l. Qed.
 
-Lemma lookup_take (l : list A) n i :
-  i < n → take n l !! i = l !! i.
+Lemma lookup_take l n i : i < n → take n l !! i = l !! i.
 Proof.
   revert n i. induction l; intros [|n] i ?; trivial.
   * auto with lia.
   * destruct i; simpl; auto with arith.
 Qed.
-Lemma lookup_take_ge (l : list A) n i :
-  n ≤ i → take n l !! i = None.
+Lemma lookup_take_ge l n i : n ≤ i → take n l !! i = None.
 Proof.
-  revert n i.
-  induction l; intros [|?] [|?] ?; simpl; auto with lia.
+  revert n i. induction l; intros [|?] [|?] ?; simpl; auto with lia.
 Qed.
-Lemma take_alter (f : A → A) l n i :
-  n ≤ i → take n (alter f i l) = take n l.
+Lemma take_alter f l n i : n ≤ i → take n (alter f i l) = take n l.
 Proof.
   intros. apply list_eq. intros j. destruct (le_lt_dec n j).
   * by rewrite !lookup_take_ge.
   * by rewrite !lookup_take, !list_lookup_alter_ne by lia.
 Qed.
-Lemma take_insert (l : list A) n i x :
-  n ≤ i → take n (<[i:=x]>l) = take n l.
-Proof take_alter _ _ _ _.
+Lemma take_insert l n i x : n ≤ i → take n (<[i:=x]>l) = take n l.
+Proof. apply take_alter. Qed.
 
 (** ** Properties of the [drop] function *)
-Lemma drop_nil n :
-  drop n (@nil A) = [].
+Lemma drop_nil n : drop n (@nil A) = [].
 Proof. by destruct n. Qed.
-Lemma drop_app (l k : list A) :
-  drop (length l) (l ++ k) = k.
+Lemma drop_app l k : drop (length l) (l ++ k) = k.
 Proof. induction l; simpl; f_equal; auto. Qed.
-Lemma drop_app_alt (l k : list A) n :
-  n = length l →
-  drop n (l ++ k) = k.
+Lemma drop_app_alt l k n : n = length l → drop n (l ++ k) = k.
 Proof. intros Hn. by rewrite Hn, drop_app. Qed.
-Lemma drop_length (l : list A) n :
-  length (drop n l) = length l - n.
-Proof.
-  revert n. by induction l; intros [|i]; simpl; f_equal.
-Qed.
-Lemma drop_all (l : list A) :
-  drop (length l) l = [].
+Lemma drop_length l n : length (drop n l) = length l - n.
+Proof. revert n. by induction l; intros [|i]; simpl; f_equal. Qed.
+Lemma drop_all l : drop (length l) l = [].
 Proof. induction l; simpl; auto. Qed.
-Lemma drop_all_alt (l : list A) n :
-  n = length l →
-  drop n l = [].
+Lemma drop_all_alt l n : n = length l → drop n l = [].
 Proof. intros. subst. by rewrite drop_all. Qed.
 
-Lemma lookup_drop (l : list A) n i :
-  drop n l !! i = l !! (n + i).
+Lemma lookup_drop l n i : drop n l !! i = l !! (n + i).
 Proof. revert n i. induction l; intros [|i] ?; simpl; auto. Qed.
-Lemma drop_alter (f : A → A) l n i  :
-  i < n → drop n (alter f i l) = drop n l.
+Lemma drop_alter f l n i : i < n → drop n (alter f i l) = drop n l.
 Proof.
   intros. apply list_eq. intros j.
   by rewrite !lookup_drop, !list_lookup_alter_ne by lia.
 Qed.
-Lemma drop_insert (l : list A) n i x :
-  i < n → drop n (<[i:=x]>l) = drop n l.
-Proof drop_alter _ _ _ _.
+Lemma drop_insert l n i x : i < n → drop n (<[i:=x]>l) = drop n l.
+Proof. apply drop_alter. Qed.
 
-Lemma delete_take_drop (l : list A) i :
-  delete i l = take i l ++ drop (S i) l.
+Lemma delete_take_drop l i : delete i l = take i l ++ drop (S i) l.
 Proof. revert i. induction l; intros [|?]; simpl; auto using f_equal. Qed.
 
 (** ** Properties of the [replicate] function *)
-Lemma replicate_length n (x : A) : length (replicate n x) = n.
+Lemma replicate_length n x : length (replicate n x) = n.
 Proof. induction n; simpl; auto. Qed.
-Lemma lookup_replicate n (x : A) i :
-  i < n →
-  replicate n x !! i = Some x.
-Proof.
-  revert i.
-  induction n; intros [|?]; naive_solver auto with lia.
-Qed.
-Lemma lookup_replicate_inv n (x y : A) i :
+Lemma lookup_replicate n x i : i < n → replicate n x !! i = Some x.
+Proof. revert i. induction n; intros [|?]; naive_solver auto with lia. Qed.
+Lemma lookup_replicate_inv n x y i :
   replicate n x !! i = Some y → y = x ∧ i < n.
-Proof.
-  revert i.
-  induction n; intros [|?]; naive_solver auto with lia.
-Qed.
-Lemma replicate_S n (x : A) :
-  replicate (S n) x = x :: replicate  n x.
+Proof. revert i. induction n; intros [|?]; naive_solver auto with lia. Qed.
+Lemma replicate_S n x : replicate (S n) x = x :: replicate  n x.
 Proof. done. Qed.
-Lemma replicate_plus n m (x : A) :
+Lemma replicate_plus n m x :
   replicate (n + m) x = replicate n x ++ replicate m x.
 Proof. induction n; simpl; f_equal; auto. Qed.
 
-Lemma take_replicate n m (x : A) :
-  take n (replicate m x) = replicate (min n m) x.
+Lemma take_replicate n m x : take n (replicate m x) = replicate (min n m) x.
 Proof. revert m. by induction n; intros [|?]; simpl; f_equal. Qed.
-Lemma take_replicate_plus n m (x : A) :
-  take n (replicate (n + m) x) = replicate n x.
+Lemma take_replicate_plus n m x : take n (replicate (n + m) x) = replicate n x.
 Proof. by rewrite take_replicate, min_l by lia. Qed.
-Lemma drop_replicate n m (x : A) :
-  drop n (replicate m x) = replicate (m - n) x.
+Lemma drop_replicate n m x : drop n (replicate m x) = replicate (m - n) x.
 Proof. revert m. by induction n; intros [|?]; simpl; f_equal. Qed.
-Lemma drop_replicate_plus n m (x : A) :
-  drop n (replicate (n + m) x) = replicate m x.
+Lemma drop_replicate_plus n m x : drop n (replicate (n + m) x) = replicate m x.
 Proof. rewrite drop_replicate. f_equal. lia. Qed.
 
-Lemma reverse_replicate n (x : A) :
-  reverse (replicate n x) = replicate n x.
+Lemma reverse_replicate n x : reverse (replicate n x) = replicate n x.
 Proof.
-  induction n as [|n IH]; [done|].
-  simpl. rewrite reverse_cons, IH. change [x] with (replicate 1 x).
-  by rewrite <-replicate_plus, plus_comm.
+  induction n as [|n IH]; [done|]. simpl. rewrite reverse_cons, IH.
+  change [x] with (replicate 1 x). by rewrite <-replicate_plus, plus_comm.
 Qed.
 
 (** ** Properties of the [resize] function *)
-Lemma resize_spec (l : list A) n x :
-  resize n x l = take n l ++ replicate (n - length l) x.
-Proof.
-  revert n.
-  induction l; intros [|?]; simpl; f_equal; auto.
-Qed.
-Lemma resize_0 (l : list A) x :
-  resize 0 x l = [].
+Lemma resize_spec l n x : resize n x l = take n l ++ replicate (n - length l) x.
+Proof. revert n. induction l; intros [|?]; simpl; f_equal; auto. Qed.
+Lemma resize_0 l x : resize 0 x l = [].
 Proof. by destruct l. Qed.
-Lemma resize_nil n (x : A) :
-  resize n x [] = replicate n x.
+Lemma resize_nil n x : resize n x [] = replicate n x.
 Proof. rewrite resize_spec. rewrite take_nil. simpl. f_equal. lia. Qed.
-Lemma resize_ge (l : list A) n x :
-  length l ≤ n →
-  resize n x l = l ++ replicate (n - length l) x.
+Lemma resize_ge l n x :
+  length l ≤ n → resize n x l = l ++ replicate (n - length l) x.
 Proof. intros. by rewrite resize_spec, take_ge. Qed.
-Lemma resize_le (l : list A) n x :
-  n ≤ length l →
-  resize n x l = take n l.
+Lemma resize_le l n x : n ≤ length l → resize n x l = take n l.
 Proof.
   intros. rewrite resize_spec, (proj2 (NPeano.Nat.sub_0_le _ _)) by done.
-  simpl. by rewrite (right_id [] (++)).
+  simpl. by rewrite (right_id_L [] (++)).
 Qed.
 
-Lemma resize_all (l : list A) x :
-  resize (length l) x l = l.
+Lemma resize_all l x : resize (length l) x l = l.
 Proof. intros. by rewrite resize_le, take_ge. Qed.
-Lemma resize_all_alt (l : list A) n x :
-  n = length l →
-  resize n x l = l.
+Lemma resize_all_alt l n x : n = length l → resize n x l = l.
 Proof. intros. subst. by rewrite resize_all. Qed.
 
-Lemma resize_plus (l : list A) n m x :
+Lemma resize_plus l n m x :
   resize (n + m) x l = resize n x l ++ resize m x (drop n l).
 Proof.
-  revert n m.
-  induction l; intros [|?] [|?]; simpl; f_equal; auto.
-  * by rewrite plus_0_r, (right_id [] (++)).
+  revert n m. induction l; intros [|?] [|?]; simpl; f_equal; auto.
+  * by rewrite plus_0_r, (right_id_L [] (++)).
   * by rewrite replicate_plus.
 Qed.
-Lemma resize_plus_eq (l : list A) n m x :
-  length l = n →
-  resize (n + m) x l = l ++ replicate m x.
+Lemma resize_plus_eq l n m x :
+  length l = n → resize (n + m) x l = l ++ replicate m x.
 Proof.
-  intros. subst.
-  by rewrite resize_plus, resize_all, drop_all, resize_nil.
+  intros. subst. by rewrite resize_plus, resize_all, drop_all, resize_nil.
 Qed.
 
-Lemma resize_app_le (l1 l2 : list A) n x :
-  n ≤ length l1 →
-  resize n x (l1 ++ l2) = resize n x l1.
+Lemma resize_app_le l1 l2 n x :
+  n ≤ length l1 → resize n x (l1 ++ l2) = resize n x l1.
 Proof.
-  intros.
-  by rewrite !resize_le, take_app_le by (rewrite ?app_length; lia).
+  intros. by rewrite !resize_le, take_app_le by (rewrite ?app_length; lia).
 Qed.
-Lemma resize_app_ge (l1 l2 : list A) n x :
-  length l1 ≤ n →
-  resize n x (l1 ++ l2) = l1 ++ resize (n - length l1) x l2.
+Lemma resize_app_ge l1 l2 n x :
+  length l1 ≤ n → resize n x (l1 ++ l2) = l1 ++ resize (n - length l1) x l2.
 Proof.
-  intros.
-  rewrite !resize_spec, take_app_ge, (associative (++)) by done.
+  intros. rewrite !resize_spec, take_app_ge, (associative_L (++)) by done.
   do 2 f_equal. rewrite app_length. lia.
 Qed.
 
-Lemma resize_length (l : list A) n x : length (resize n x l) = n.
-Proof.
-  rewrite resize_spec, app_length, replicate_length, take_length. lia.
-Qed.
-Lemma resize_replicate (x : A) n m :
-  resize n x (replicate m x) = replicate n x.
+Lemma resize_length l n x : length (resize n x l) = n.
+Proof. rewrite resize_spec, app_length, replicate_length, take_length. lia. Qed.
+Lemma resize_replicate x n m : resize n x (replicate m x) = replicate n x.
 Proof. revert m. induction n; intros [|?]; simpl; f_equal; auto. Qed.
 
-Lemma resize_resize (l : list A) n m x :
-  n ≤ m →
-  resize n x (resize m x l) = resize n x l.
+Lemma resize_resize l n m x : n ≤ m → resize n x (resize m x l) = resize n x l.
 Proof.
   revert n m. induction l; simpl.
   * intros. by rewrite !resize_nil, resize_replicate.
   * intros [|?] [|?] ?; simpl; f_equal; auto with lia.
 Qed.
-Lemma resize_idempotent (l : list A) n x :
-  resize n x (resize n x l) = resize n x l.
+Lemma resize_idempotent l n x : resize n x (resize n x l) = resize n x l.
 Proof. by rewrite resize_resize. Qed.
 
-Lemma resize_take_le (l : list A) n m x :
-  n ≤ m →
-  resize n x (take m l) = resize n x l.
+Lemma resize_take_le l n m x : n ≤ m → resize n x (take m l) = resize n x l.
 Proof.
-  revert n m.
-  induction l; intros [|?] [|?] ?; simpl; f_equal; auto with lia.
+  revert n m. induction l; intros [|?] [|?] ?; simpl; f_equal; auto with lia.
 Qed.
-Lemma resize_take_eq (l : list A) n x :
-  resize n x (take n l) = resize n x l.
+Lemma resize_take_eq l n x : resize n x (take n l) = resize n x l.
 Proof. by rewrite resize_take_le. Qed.
 
-Lemma take_resize (l : list A) n m x :
-  take n (resize m x l) = resize (min n m) x l.
+Lemma take_resize l n m x : take n (resize m x l) = resize (min n m) x l.
 Proof.
   revert n m.
   induction l; intros [|?] [|?]; simpl; f_equal; auto using take_replicate.
 Qed.
-Lemma take_resize_le (l : list A) n m x :
-  n ≤ m →
-  take n (resize m x l) = resize n x l.
+Lemma take_resize_le l n m x : n ≤ m → take n (resize m x l) = resize n x l.
 Proof. intros. by rewrite take_resize, Min.min_l. Qed.
-Lemma take_resize_eq (l : list A) n x :
-  take n (resize n x l) = resize n x l.
+Lemma take_resize_eq l n x : take n (resize n x l) = resize n x l.
 Proof. intros. by rewrite take_resize, Min.min_l. Qed.
-Lemma take_length_resize (l : list A) n x :
-  length l ≤ n →
-  take (length l) (resize n x l) = l.
+Lemma take_length_resize l n x :
+  length l ≤ n → take (length l) (resize n x l) = l.
 Proof. intros. by rewrite take_resize_le, resize_all. Qed.
-Lemma take_length_resize_alt (l : list A) n m x :
-  m = length l →
-  m ≤ n →
-  take m (resize n x l) = l.
+Lemma take_length_resize_alt l n m x :
+  m = length l → m ≤ n → take m (resize n x l) = l.
 Proof. intros. subst. by apply take_length_resize. Qed.
-Lemma take_resize_plus (l : list A) n m x :
-  take n (resize (n + m) x l) = resize n x l.
+Lemma take_resize_plus l n m x : take n (resize (n + m) x l) = resize n x l.
 Proof. by rewrite take_resize, min_l by lia. Qed.
 
-Lemma drop_resize_le (l : list A) n m x :
-  n ≤ m →
-  drop n (resize m x l) = resize (m - n) x (drop n l).
+Lemma drop_resize_le l n m x :
+  n ≤ m → drop n (resize m x l) = resize (m - n) x (drop n l).
 Proof.
   revert n m. induction l; simpl.
   * intros. by rewrite drop_nil, !resize_nil, drop_replicate.
   * intros [|?] [|?] ?; simpl; try case_match; auto with lia.
 Qed.
-Lemma drop_resize_plus (l : list A) n m x :
+Lemma drop_resize_plus l n m x :
   drop n (resize (n + m) x l) = resize m x (drop n l).
 Proof. rewrite drop_resize_le by lia. f_equal. lia. Qed.
 
-(** ** Properties of the [sublist] predicate *)
-Lemma sublist_nil_l (l : list A) :
-  sublist [] l.
-Proof. induction l; try constructor; auto. Qed.
-Lemma sublist_nil_r (l : list A) :
-  sublist l [] ↔ l = [].
-Proof. split. by inversion 1. intros. subst. constructor. Qed.
-
-Lemma sublist_app_skip_l (k : list A) l1 l2 :
-  sublist l1 l2 →
-  sublist l1 (k ++ l2).
-Proof. induction k; try constructor; auto. Qed.
-Lemma sublist_app_skip_r (k : list A) l1 l2 :
-  sublist l1 l2 →
-  sublist l1 (l2 ++ k).
-Proof. induction 1; simpl; try constructor; auto using sublist_nil_l. Qed.
-
-Lemma sublist_cons_r (x : A) l k :
-  sublist l (x :: k) ↔ sublist l k ∨ ∃ l', l = x :: l' ∧ sublist l' k.
+(** ** Properties of the [Permutation] predicate *)
+Lemma Permutation_nil l : l ≡ₚ [] ↔ l = [].
+Proof. split. by intro; apply Permutation_nil. by intro; subst. Qed.
+Lemma Permutation_singleton l x : l ≡ₚ [x] ↔ l = [x].
+Proof. split. by intro; apply Permutation_length_1_inv. by intro; subst. Qed.
+Definition Permutation_skip := @perm_skip A.
+Definition Permutation_swap := @perm_swap A.
+Definition Permutation_singleton_inj := @Permutation_length_1 A.
+
+Global Existing Instance Permutation_app'_Proper.
+Global Instance: Proper ((≡ₚ) ==> (=)) (@length A).
+Proof. induction 1; simpl; auto with lia. Qed.
+Global Instance: Commutative (≡ₚ) (@app A).
 Proof.
-  split.
-  * inversion 1; eauto.
-  * intros [?|(?&?&?)]; subst; constructor; auto.
+  intros l1. induction l1 as [|x l1 IH]; intros l2; simpl.
+  * by rewrite (right_id_L [] (++)).
+  * rewrite Permutation_middle, IH. simpl. by rewrite Permutation_middle.
 Qed.
-Lemma sublist_cons_l (x : A) l k :
-  sublist (x :: l) k ↔ ∃ k1 k2, k = k1 ++ x :: k2 ∧ sublist l k2.
+Global Instance: ∀ x : A, Injective (≡ₚ) (≡ₚ) (x ::).
+Proof. red. eauto using Permutation_cons_inv. Qed.
+Global Instance: ∀ k : list A, Injective (≡ₚ) (≡ₚ) (k ++).
 Proof.
-  split.
-  * intros Hlk. induction k as [|y k IH]; inversion Hlk.
-    + eexists [], k. by repeat constructor.
-    + destruct IH as (k1 & k2 & ? & ?); subst; auto.
-      by exists (y :: k1) k2.
-  * intros (k1 & k2 & ? & ?). subst.
-    by apply sublist_app_skip_l, sublist_cons.
+  red. induction k as [|x k IH]; intros l1 l2; simpl; auto.
+  intros. by apply IH, (injective (x ::)).
 Qed.
-
-Lemma sublist_app_compat (l1 l2 k1 k2 : list A) :
-  sublist l1 l2 → sublist k1 k2 →
-  sublist (l1 ++ k1) (l2 ++ k2).
-Proof. induction 1; simpl; try constructor; auto. Qed.
-
-Lemma sublist_app_r (l k1 k2 : list A) :
-  sublist l (k1 ++ k2) ↔ ∃ l1 l2,
-    l = l1 ++ l2 ∧ sublist l1 k1 ∧ sublist l2 k2.
+Global Instance: ∀ k : list A, Injective (≡ₚ) (≡ₚ) (++ k).
 Proof.
-  split.
-  * revert l k2. induction k1 as [|y k1 IH]; intros l k2; simpl.
-    { eexists [], l. by repeat constructor. }
-    rewrite sublist_cons_r. intros [?|(l' & ? &?)]; subst.
-    + destruct (IH l k2) as (l1&l2&?&?&?); trivial; subst.
-      exists l1 l2. auto using sublist_cons_skip.
-    + destruct (IH l' k2) as (l1&l2&?&?&?); trivial; subst.
-      exists (y :: l1) l2. auto using sublist_cons.
-  * intros (?&?&?&?&?); subst. auto using sublist_app_compat.
+  intros k l1 l2. rewrite !(commutative (++) _ k).
+  by apply (injective (k ++)).
 Qed.
-Lemma sublist_app_l (l1 l2 k : list A) :
-  sublist (l1 ++ l2) k ↔ ∃ k1 k2,
-    k = k1 ++ k2 ∧ sublist l1 k1 ∧ sublist l2 k2.
+
+(** ** Properties of the [prefix_of] and [suffix_of] predicates *)
+Global Instance: PreOrder (@prefix_of A).
 Proof.
   split.
-  * revert l2 k. induction l1 as [|x l1 IH]; intros l2 k; simpl.
-    { eexists [], k. by repeat constructor. }
-    rewrite sublist_cons_l. intros (k1 & k2 &?&?); subst.
-    destruct (IH l2 k2) as (h1 & h2 &?&?&?); trivial; subst.
-    exists (k1 ++ x :: h1) h2. rewrite <-(associative (++)).
-    auto using sublist_app_skip_l, sublist_cons.
-  * intros (?&?&?&?&?); subst. auto using sublist_app_compat.
+  * intros ?. eexists []. by rewrite (right_id_L [] (++)).
+  * intros ??? [k1 ?] [k2 ?].
+    exists (k1 ++ k2). subst. by rewrite (associative_L (++)).
 Qed.
 
-Global Instance: PreOrder (@sublist A).
+Lemma prefix_of_nil l : [] `prefix_of` l.
+Proof. by exists l. Qed.
+Lemma prefix_of_nil_not x l : ¬x :: l `prefix_of` [].
+Proof. by intros [k E]. Qed.
+Lemma prefix_of_cons x l1 l2 : l1 `prefix_of` l2 → x :: l1 `prefix_of` x :: l2.
+Proof. intros [k E]. exists k. by subst. Qed.
+Lemma prefix_of_cons_alt x y l1 l2 :
+  x = y → l1 `prefix_of` l2 → x :: l1 `prefix_of` y :: l2.
+Proof. intro. subst. apply prefix_of_cons. Qed.
+Lemma prefix_of_cons_inv_1 x y l1 l2 : x :: l1 `prefix_of` y :: l2 → x = y.
+Proof. intros [k E]. by injection E. Qed.
+Lemma prefix_of_cons_inv_2 x y l1 l2 :
+  x :: l1 `prefix_of` y :: l2 → l1 `prefix_of` l2.
+Proof. intros [k E]. exists k. by injection E. Qed.
+
+Lemma prefix_of_app k l1 l2 : l1 `prefix_of` l2 → k ++ l1 `prefix_of` k ++ l2.
+Proof. intros [k' ?]. subst. exists k'. by rewrite (associative_L (++)). Qed.
+Lemma prefix_of_app_alt k1 k2 l1 l2 :
+  k1 = k2 → l1 `prefix_of` l2 → k1 ++ l1 `prefix_of` k2 ++ l2.
+Proof. intro. subst. apply prefix_of_app. Qed.
+Lemma prefix_of_app_l l1 l2 l3 : l1 ++ l3 `prefix_of` l2 → l1 `prefix_of` l2.
 Proof.
-  split.
-  * intros l. induction l; constructor; auto.
-  * intros l1 l2 l3 Hl12. revert l3. induction Hl12.
-    + auto using sublist_nil_l.
-    + intros ?. rewrite sublist_cons_l. intros (?&?&?&?); subst.
-      eauto using sublist_app_skip_l, sublist_cons.
-    + intros ?. rewrite sublist_cons_l. intros (?&?&?&?); subst.
-      eauto using sublist_app_skip_l, sublist_cons_skip.
+  intros [k ?]. red. exists (l3 ++ k). subst. by rewrite <-(associative_L (++)).
 Qed.
-
-Lemma sublist_length (l1 l2 : list A) :
-  sublist l1 l2 → length l1 ≤ length l2.
-Proof. induction 1; simpl; auto with arith. Qed.
-
-Lemma sublist_take (l : list A) i :
-  sublist (take i l) l.
-Proof. rewrite <-(take_drop i l) at 2. by apply sublist_app_skip_r. Qed.
-Lemma sublist_drop (l : list A) i :
-  sublist (drop i l) l.
-Proof. rewrite <-(take_drop i l) at 2. by apply sublist_app_skip_l. Qed.
-Lemma sublist_delete (l : list A) i :
-  sublist (delete i l) l.
-Proof. revert i. by induction l; intros [|?]; simpl; constructor. Qed.
-Lemma sublist_delete_list (l : list A) is :
-  sublist (delete_list is l) l.
+Lemma prefix_of_app_r l1 l2 l3 : l1 `prefix_of` l2 → l1 `prefix_of` l2 ++ l3.
 Proof.
-  induction is as [|i is IH]; simpl; [done |].
-  transitivity (delete_list is l); auto using sublist_delete.
+  intros [k ?]. exists (k ++ l3). subst. by rewrite (associative_L (++)).
 Qed.
 
-Lemma sublist_alt (l1 l2 : list A) :
-  sublist l1 l2 ↔ ∃ is, l1 = delete_list is l2.
+Lemma prefix_of_length l1 l2 : l1 `prefix_of` l2 → length l1 ≤ length l2.
+Proof. intros [??]. subst. rewrite app_length. lia. Qed.
+Lemma prefix_of_snoc_not l x : ¬l ++ [x] `prefix_of` l.
+Proof. intros [??]. discriminate_list_equality. Qed.
+
+Global Instance: PreOrder (@suffix_of A).
 Proof.
   split.
-  * intros Hl12.
-    cut (∀ k, ∃ is, k ++ l1 = delete_list is (k ++ l2)).
-    { intros help. apply (help []). }
-    induction Hl12 as [|x l1 l2 _ IH|x l1 l2 _ IH]; intros k.
-    + by eexists [].
-    + destruct (IH (k ++ [x])) as [is His]. exists is.
-      by rewrite <-!(associative (++)) in His.
-    + destruct (IH k) as [is His]. exists (is ++ [length k]).
-      unfold delete_list. rewrite fold_right_app. simpl.
-      by rewrite delete_middle.
-  * intros [is ?]. subst. apply sublist_delete_list.
+  * intros ?. by eexists [].
+  * intros ??? [k1 ?] [k2 ?].
+    exists (k2 ++ k1). subst. by rewrite (associative_L (++)).
 Qed.
 
-Global Instance: AntiSymmetric (@sublist A).
-Proof.
-  intros l1 l2 Hl12 Hl21. apply sublist_length in Hl21.
-  induction Hl12; simpl in *.
-  * done.
-  * f_equal. auto with arith.
-  * apply sublist_length in Hl12. lia.
-Qed.
-End general_properties.
+Global Instance prefix_of_dec `{∀ x y, Decision (x = y)} : ∀ l1 l2,
+  Decision (l1 `prefix_of` l2) := fix go l1 l2 :=
+  match l1, l2 return { l1 `prefix_of` l2 } + { ¬l1 `prefix_of` l2 } with
+  | [], _ => left (prefix_of_nil _)
+  | _, [] => right (prefix_of_nil_not _ _)
+  | x :: l1, y :: l2 =>
+    match decide_rel (=) x y with
+    | left Exy =>
+      match go l1 l2 with
+      | left Hl1l2 => left (prefix_of_cons_alt _ _ _ _ Exy Hl1l2)
+      | right Hl1l2 => right (Hl1l2 ∘ prefix_of_cons_inv_2 _ _ _ _)
+      end
+    | right Exy => right (Exy ∘ prefix_of_cons_inv_1 _ _ _ _)
+    end
+  end.
 
-(** ** Properties of the [same_length] predicate *)
-Instance: ∀ A, Reflexive (@same_length A A).
-Proof. intros A l. induction l; constructor; auto. Qed.
-Instance: ∀ A, Symmetric (@same_length A A).
+Section prefix_ops.
+  Context `{∀ x y, Decision (x = y)}.
+
+  Lemma max_prefix_of_fst l1 l2 :
+    l1 = snd (max_prefix_of l1 l2) ++ fst (fst (max_prefix_of l1 l2)).
+  Proof.
+    revert l2. induction l1; intros [|??]; simpl;
+      repeat case_decide; simpl; f_equal; auto.
+  Qed.
+  Lemma max_prefix_of_fst_alt l1 l2 k1 k2 k3 :
+    max_prefix_of l1 l2 = (k1, k2, k3) → l1 = k3 ++ k1.
+  Proof.
+    intro. pose proof (max_prefix_of_fst l1 l2).
+    by destruct (max_prefix_of l1 l2) as [[]?]; simplify_equality.
+  Qed.
+  Lemma max_prefix_of_fst_prefix l1 l2 :
+    snd (max_prefix_of l1 l2) `prefix_of` l1.
+  Proof. eexists. apply max_prefix_of_fst. Qed.
+  Lemma max_prefix_of_fst_prefix_alt l1 l2 k1 k2 k3 :
+    max_prefix_of l1 l2 = (k1, k2, k3) → k3 `prefix_of` l1.
+  Proof. eexists. eauto using max_prefix_of_fst_alt. Qed.
+
+  Lemma max_prefix_of_snd l1 l2 :
+    l2 = snd (max_prefix_of l1 l2) ++ snd (fst (max_prefix_of l1 l2)).
+  Proof.
+    revert l2. induction l1; intros [|??]; simpl;
+      repeat case_decide; simpl; f_equal; auto.
+  Qed.
+  Lemma max_prefix_of_snd_alt l1 l2 k1 k2 k3 :
+    max_prefix_of l1 l2 = (k1, k2, k3) → l2 = k3 ++ k2.
+  Proof.
+    intro. pose proof (max_prefix_of_snd l1 l2).
+    by destruct (max_prefix_of l1 l2) as [[]?]; simplify_equality.
+  Qed.
+  Lemma max_prefix_of_snd_prefix l1 l2 :
+    snd (max_prefix_of l1 l2) `prefix_of` l2.
+  Proof. eexists. apply max_prefix_of_snd. Qed.
+  Lemma max_prefix_of_snd_prefix_alt l1 l2 k1 k2 k3 :
+    max_prefix_of l1 l2 = (k1,k2,k3) → k3 `prefix_of` l2.
+  Proof. eexists. eauto using max_prefix_of_snd_alt. Qed.
+
+  Lemma max_prefix_of_max l1 l2 k :
+    k `prefix_of` l1 → k `prefix_of` l2 →
+    k `prefix_of` snd (max_prefix_of l1 l2).
+  Proof.
+    intros [l1' ?] [l2' ?]. subst.
+    by induction k; simpl; repeat case_decide; simpl;
+      auto using prefix_of_nil, prefix_of_cons.
+  Qed.
+  Lemma max_prefix_of_max_alt l1 l2 k1 k2 k3 k :
+    max_prefix_of l1 l2 = (k1,k2,k3) →
+    k `prefix_of` l1 → k `prefix_of` l2 → k `prefix_of` k3.
+  Proof.
+    intro. pose proof (max_prefix_of_max l1 l2 k).
+    by destruct (max_prefix_of l1 l2) as [[]?]; simplify_equality.
+  Qed.
+
+  Lemma max_prefix_of_max_snoc l1 l2 k1 k2 k3 x1 x2 :
+    max_prefix_of l1 l2 = (x1 :: k1, x2 :: k2, k3) → x1 ≠ x2.
+  Proof.
+    intros Hl ?. subst. destruct (prefix_of_snoc_not k3 x2).
+    eapply max_prefix_of_max_alt; eauto.
+    * rewrite (max_prefix_of_fst_alt _ _ _ _ _ Hl).
+      apply prefix_of_app, prefix_of_cons, prefix_of_nil.
+    * rewrite (max_prefix_of_snd_alt _ _ _ _ _ Hl).
+      apply prefix_of_app, prefix_of_cons, prefix_of_nil.
+  Qed.
+End prefix_ops.
+
+Lemma prefix_suffix_reverse l1 l2 :
+  l1 `prefix_of` l2 ↔ reverse l1 `suffix_of` reverse l2.
+Proof.
+  split; intros [k E]; exists (reverse k).
+  * by rewrite E, reverse_app.
+  * by rewrite <-(reverse_involutive l2), E, reverse_app, reverse_involutive.
+Qed.
+Lemma suffix_prefix_reverse l1 l2 :
+  l1 `suffix_of` l2 ↔ reverse l1 `prefix_of` reverse l2.
+Proof. by rewrite prefix_suffix_reverse, !reverse_involutive. Qed.
+
+Lemma suffix_of_nil l : [] `suffix_of` l.
+Proof. exists l. by rewrite (right_id_L [] (++)). Qed.
+Lemma suffix_of_nil_inv l : l `suffix_of` [] → l = [].
+Proof. by intros [[|?] ?]; simplify_list_equality. Qed.
+Lemma suffix_of_cons_nil_inv x l : ¬x :: l `suffix_of` [].
+Proof. by intros [[] ?]. Qed.
+Lemma suffix_of_snoc l1 l2 x :
+  l1 `suffix_of` l2 → l1 ++ [x] `suffix_of` l2 ++ [x].
+Proof. intros [k E]. exists k. subst. by rewrite (associative_L (++)). Qed.
+Lemma suffix_of_snoc_alt x y l1 l2 :
+  x = y → l1 `suffix_of` l2 → l1 ++ [x] `suffix_of` l2 ++ [y].
+Proof. intro. subst. apply suffix_of_snoc. Qed.
+
+Lemma suffix_of_app l1 l2 k : l1 `suffix_of` l2 → l1 ++ k `suffix_of` l2 ++ k.
+Proof. intros [k' E]. exists k'. subst. by rewrite (associative_L (++)). Qed.
+Lemma suffix_of_app_alt l1 l2 k1 k2 :
+  k1 = k2 → l1 `suffix_of` l2 → l1 ++ k1 `suffix_of` l2 ++ k2.
+Proof. intro. subst. apply suffix_of_app. Qed.
+
+Lemma suffix_of_snoc_inv_1 x y l1 l2 :
+  l1 ++ [x] `suffix_of` l2 ++ [y] → x = y.
+Proof.
+  intros [k' E]. rewrite (associative_L (++)) in E. by simplify_list_equality.
+Qed.
+Lemma suffix_of_snoc_inv_2 x y l1 l2 :
+  l1 ++ [x] `suffix_of` l2 ++ [y] → l1 `suffix_of` l2.
+Proof.
+  intros [k' E]. exists k'. rewrite (associative_L (++)) in E.
+  by simplify_list_equality.
+Qed.
+Lemma suffix_of_app_inv l1 l2 k :
+  l1 ++ k `suffix_of` l2 ++ k → l1 `suffix_of` l2.
+Proof.
+  intros [k' E]. exists k'. rewrite (associative_L (++)) in E.
+  by simplify_list_equality.
+Qed.
+
+Lemma suffix_of_cons_l l1 l2 x : x :: l1 `suffix_of` l2 → l1 `suffix_of` l2.
+Proof.
+  intros [k ?]. exists (k ++ [x]). subst. by rewrite <-(associative_L (++)).
+Qed.
+Lemma suffix_of_app_l l1 l2 l3 : l3 ++ l1 `suffix_of` l2 → l1 `suffix_of` l2.
+Proof.
+  intros [k ?]. exists (k ++ l3). subst. by rewrite <-(associative_L (++)).
+Qed.
+Lemma suffix_of_cons_r l1 l2 x : l1 `suffix_of` l2 → l1 `suffix_of` x :: l2.
+Proof. intros [k ?]. exists (x :: k). by subst. Qed.
+Lemma suffix_of_app_r l1 l2 l3 : l1 `suffix_of` l2 → l1 `suffix_of` l3 ++ l2.
+Proof. intros [k ?]. exists (l3 ++ k). subst. by rewrite (associative_L _). Qed.
+
+Lemma suffix_of_cons_inv l1 l2 x y :
+  x :: l1 `suffix_of` y :: l2 → x :: l1 = y :: l2 ∨ x :: l1 `suffix_of` l2.
+Proof.
+  intros [[|? k] E]; [by left |].
+  right. simplify_equality. by apply suffix_of_app_r.
+Qed.
+
+Lemma suffix_of_length l1 l2 : l1 `suffix_of` l2 → length l1 ≤ length l2.
+Proof. intros [??]. subst. rewrite app_length. lia. Qed.
+Lemma suffix_of_cons_not x l : ¬x :: l `suffix_of` l.
+Proof. intros [??]. discriminate_list_equality. Qed.
+
+Global Instance suffix_of_dec `{∀ x y, Decision (x = y)} l1 l2 :
+  Decision (l1 `suffix_of` l2).
+Proof.
+  refine (cast_if (decide_rel prefix_of (reverse l1) (reverse l2)));
+   abstract (by rewrite suffix_prefix_reverse).
+Defined.
+
+Section max_suffix_of.
+  Context `{∀ x y, Decision (x = y)}.
+
+  Lemma max_suffix_of_fst l1 l2 :
+    l1 = fst (fst (max_suffix_of l1 l2)) ++ snd (max_suffix_of l1 l2).
+  Proof.
+    rewrite <-(reverse_involutive l1) at 1.
+    rewrite (max_prefix_of_fst (reverse l1) (reverse l2)). unfold max_suffix_of.
+    destruct (max_prefix_of (reverse l1) (reverse l2)) as ((?&?)&?); simpl.
+    by rewrite reverse_app.
+  Qed.
+  Lemma max_suffix_of_fst_alt l1 l2 k1 k2 k3 :
+    max_suffix_of l1 l2 = (k1, k2, k3) → l1 = k1 ++ k3.
+  Proof.
+    intro. pose proof (max_suffix_of_fst l1 l2).
+    by destruct (max_suffix_of l1 l2) as [[]?]; simplify_equality.
+  Qed.
+  Lemma max_suffix_of_fst_suffix l1 l2 :
+    snd (max_suffix_of l1 l2) `suffix_of` l1.
+  Proof. eexists. apply max_suffix_of_fst. Qed.
+  Lemma max_suffix_of_fst_suffix_alt l1 l2 k1 k2 k3 :
+    max_suffix_of l1 l2 = (k1, k2, k3) → k3 `suffix_of` l1.
+  Proof. eexists. eauto using max_suffix_of_fst_alt. Qed.
+
+  Lemma max_suffix_of_snd l1 l2 :
+    l2 = snd (fst (max_suffix_of l1 l2)) ++ snd (max_suffix_of l1 l2).
+  Proof.
+    rewrite <-(reverse_involutive l2) at 1.
+    rewrite (max_prefix_of_snd (reverse l1) (reverse l2)).
+    unfold max_suffix_of.
+    destruct (max_prefix_of (reverse l1) (reverse l2)) as ((?&?)&?); simpl.
+    by rewrite reverse_app.
+  Qed.
+  Lemma max_suffix_of_snd_alt l1 l2 k1 k2 k3 :
+    max_suffix_of l1 l2 = (k1,k2,k3) → l2 = k2 ++ k3.
+  Proof.
+    intro. pose proof (max_suffix_of_snd l1 l2).
+    by destruct (max_suffix_of l1 l2) as [[]?]; simplify_equality.
+  Qed.
+  Lemma max_suffix_of_snd_suffix l1 l2 :
+    snd (max_suffix_of l1 l2) `suffix_of` l2.
+  Proof. eexists. apply max_suffix_of_snd. Qed.
+  Lemma max_suffix_of_snd_suffix_alt l1 l2 k1 k2 k3 :
+    max_suffix_of l1 l2 = (k1,k2,k3) → k3 `suffix_of` l2.
+  Proof. eexists. eauto using max_suffix_of_snd_alt. Qed.
+
+  Lemma max_suffix_of_max l1 l2 k :
+    k `suffix_of` l1 → k `suffix_of` l2 →
+     k `suffix_of` snd (max_suffix_of l1 l2).
+  Proof.
+    generalize (max_prefix_of_max (reverse l1) (reverse l2)).
+    rewrite !suffix_prefix_reverse. unfold max_suffix_of.
+    destruct (max_prefix_of (reverse l1) (reverse l2)) as ((?&?)&?); simpl.
+    rewrite reverse_involutive. auto.
+  Qed.
+  Lemma max_suffix_of_max_alt l1 l2 k1 k2 k3 k :
+    max_suffix_of l1 l2 = (k1, k2, k3) →
+    k `suffix_of` l1 → k `suffix_of` l2 → k `suffix_of` k3.
+  Proof.
+    intro. pose proof (max_suffix_of_max l1 l2 k).
+    by destruct (max_suffix_of l1 l2) as [[]?]; simplify_equality.
+  Qed.
+
+  Lemma max_suffix_of_max_snoc l1 l2 k1 k2 k3 x1 x2 :
+    max_suffix_of l1 l2 = (k1 ++ [x1], k2 ++ [x2], k3) → x1 ≠ x2.
+  Proof.
+    intros Hl ?. subst. destruct (suffix_of_cons_not x2 k3).
+    eapply max_suffix_of_max_alt; eauto.
+    * rewrite (max_suffix_of_fst_alt _ _ _ _ _ Hl).
+      by apply (suffix_of_app [x2]), suffix_of_app_r.
+    * rewrite (max_suffix_of_snd_alt _ _ _ _ _ Hl).
+      by apply (suffix_of_app [x2]), suffix_of_app_r.
+  Qed.
+End max_suffix_of.
+
+(** ** Properties of the [sublist] predicate *)
+Lemma sublist_length l1 l2 : l1 `sublist` l2 → length l1 ≤ length l2.
+Proof. induction 1; simpl; auto with arith. Qed.
+
+Lemma sublist_nil_l l : [] `sublist` l.
+Proof. induction l; try constructor; auto. Qed.
+Lemma sublist_nil_r l : l `sublist` [] ↔ l = [].
+Proof. split. by inversion 1. intros. subst. constructor. Qed.
+
+Lemma sublist_app l1 l2 k1 k2 :
+  l1 `sublist` l2 → k1 `sublist` k2 → l1 ++ k1 `sublist` l2 ++ k2.
+Proof. induction 1; simpl; try constructor; auto. Qed.
+Lemma sublist_inserts_l k l1 l2 : l1 `sublist` l2 → l1 `sublist` k ++ l2.
+Proof. induction k; try constructor; auto. Qed.
+Lemma sublist_inserts_r k l1 l2 : l1 `sublist` l2 → l1 `sublist` l2 ++ k.
+Proof. induction 1; simpl; try constructor; auto using sublist_nil_l. Qed.
+
+Lemma sublist_cons_r x l k :
+  l `sublist` x :: k ↔ l `sublist` k ∨ ∃ l', l = x :: l' ∧ l' `sublist` k.
+Proof.
+  split. inversion 1; eauto. intros [?|(?&?&?)]; subst; constructor; auto.
+Qed.
+Lemma sublist_cons_l x l k :
+  x :: l `sublist` k ↔ ∃ k1 k2, k = k1 ++ x :: k2 ∧ l `sublist` k2.
+Proof.
+  split.
+  * intros Hlk. induction k as [|y k IH]; inversion Hlk.
+    + eexists [], k. by repeat constructor.
+    + destruct IH as (k1&k2&?&?); subst; auto. by exists (y :: k1) k2.
+  * intros (k1&k2&?&?). subst. by apply sublist_inserts_l, sublist_skip.
+Qed.
+
+Lemma sublist_app_r l k1 k2 :
+  l `sublist` k1 ++ k2 ↔
+    ∃ l1 l2, l = l1 ++ l2 ∧ l1 `sublist` k1 ∧ l2 `sublist` k2.
+Proof.
+  split.
+  * revert l k2. induction k1 as [|y k1 IH]; intros l k2; simpl.
+    { eexists [], l. by repeat constructor. }
+    rewrite sublist_cons_r. intros [?|(l' & ? &?)]; subst.
+    + destruct (IH l k2) as (l1&l2&?&?&?); trivial; subst.
+      exists l1 l2. auto using sublist_insert.
+    + destruct (IH l' k2) as (l1&l2&?&?&?); trivial; subst.
+      exists (y :: l1) l2. auto using sublist_skip.
+  * intros (?&?&?&?&?); subst. auto using sublist_app.
+Qed.
+Lemma sublist_app_l l1 l2 k :
+  l1 ++ l2 `sublist` k ↔
+    ∃ k1 k2, k = k1 ++ k2 ∧ l1 `sublist` k1 ∧ l2 `sublist` k2.
+Proof.
+  split.
+  * revert l2 k. induction l1 as [|x l1 IH]; intros l2 k; simpl.
+    { eexists [], k. by repeat constructor. }
+    rewrite sublist_cons_l. intros (k1 & k2 &?&?); subst.
+    destruct (IH l2 k2) as (h1 & h2 &?&?&?); trivial; subst.
+    exists (k1 ++ x :: h1) h2. rewrite <-(associative_L (++)).
+    auto using sublist_inserts_l, sublist_skip.
+  * intros (?&?&?&?&?); subst. auto using sublist_app.
+Qed.
+Lemma sublist_app_inv_l k l1 l2 : k ++ l1 `sublist` k ++ l2 → l1 `sublist` l2.
+Proof.
+  induction k as [|y k IH]; simpl; [done |].
+  rewrite sublist_cons_r. intros [Hl12|(?&?&?)]; [|simplify_equality; eauto].
+  rewrite sublist_cons_l in Hl12. destruct Hl12 as (k1&k2&Hk&?).
+  apply IH. rewrite Hk. eauto using sublist_inserts_l, sublist_insert.
+Qed.
+Lemma sublist_app_inv_r k l1 l2 : l1 ++ k `sublist` l2 ++ k → l1 `sublist` l2.
+Proof.
+  revert l1 l2. induction k as [|y k IH]; intros l1 l2.
+  { by rewrite !(right_id_L [] (++)). }
+  intros. feed pose proof (IH (l1 ++ [y]) (l2 ++ [y])) as Hl12.
+  { by rewrite <-!(associative_L (++)). }
+  rewrite sublist_app_l in Hl12. destruct Hl12 as (k1&k2&E&?&Hk2).
+  destruct k2 as [|z k2] using rev_ind; [inversion Hk2|].
+  rewrite (associative_L (++)) in E. simplify_list_equality.
+  eauto using sublist_inserts_r.
+Qed.
+
+Global Instance: PartialOrder (@sublist A).
+Proof.
+  split; [split|].
+  * intros l. induction l; constructor; auto.
+  * intros l1 l2 l3 Hl12. revert l3. induction Hl12.
+    + auto using sublist_nil_l.
+    + intros ?. rewrite sublist_cons_l. intros (?&?&?&?); subst.
+      eauto using sublist_inserts_l, sublist_skip.
+    + intros ?. rewrite sublist_cons_l. intros (?&?&?&?); subst.
+      eauto using sublist_inserts_l, sublist_insert.
+  * intros l1 l2 Hl12 Hl21. apply sublist_length in Hl21.
+    induction Hl12; simpl in *; f_equal; auto with arith.
+    apply sublist_length in Hl12. lia.
+Qed.
+
+Lemma sublist_take l i : take i l `sublist` l.
+Proof. rewrite <-(take_drop i l) at 2. by apply sublist_inserts_r. Qed.
+Lemma sublist_drop l i : drop i l `sublist` l.
+Proof. rewrite <-(take_drop i l) at 2. by apply sublist_inserts_l. Qed.
+Lemma sublist_delete l i : delete i l `sublist` l.
+Proof. revert i. by induction l; intros [|?]; simpl; constructor. Qed.
+Lemma sublist_delete_list l is : delete_list is l `sublist` l.
+Proof.
+  induction is as [|i is IH]; simpl; [done |].
+  transitivity (delete_list is l); auto using sublist_delete.
+Qed.
+
+Lemma sublist_alt l1 l2 : l1 `sublist` l2 ↔ ∃ is, l1 = delete_list is l2.
+Proof.
+  split.
+  * intros Hl12. cut (∀ k, ∃ is, k ++ l1 = delete_list is (k ++ l2)).
+    { intros help. apply (help []). }
+    induction Hl12 as [|x l1 l2 _ IH|x l1 l2 _ IH]; intros k.
+    + by eexists [].
+    + destruct (IH (k ++ [x])) as [is His]. exists is.
+      by rewrite <-!(associative_L (++)) in His.
+    + destruct (IH k) as [is His]. exists (is ++ [length k]).
+      unfold delete_list. rewrite fold_right_app. simpl.
+      by rewrite delete_middle.
+  * intros [is ?]. subst. apply sublist_delete_list.
+Qed.
+
+Lemma Permutation_sublist l1 l2 l3 :
+  l1 ≡ₚ l2 → l2 `sublist` l3 → ∃ l4, l1 `sublist` l4 ∧ l4 ≡ₚ l3.
+Proof.
+  intros Hl1l2. revert l3.
+  induction Hl1l2 as [|x l1 l2 ? IH|x y l1|l1 l1' l2 ? IH1 ? IH2].
+  * intros l3. by exists l3.
+  * intros l3. rewrite sublist_cons_l. intros (l3'&l3''&?&?); subst.
+    destruct (IH l3'') as (l4&?&Hl4); auto. exists (l3' ++ x :: l4).
+    split. by apply sublist_inserts_l, sublist_skip. by rewrite Hl4.
+  * intros l3. rewrite sublist_cons_l. intros (l3'&l3''&?& Hl3); subst.
+    rewrite sublist_cons_l in Hl3. destruct Hl3 as (l5'&l5''&?& Hl5); subst.
+    exists (l3' ++ y :: l5' ++ x :: l5''). split.
+    - by do 2 apply sublist_inserts_l, sublist_skip.
+    - by rewrite !Permutation_middle, Permutation_swap.
+  * intros l3 ?. destruct (IH2 l3) as (l3'&?&?); trivial.
+    destruct (IH1 l3') as (l3'' &?&?); trivial. exists l3''.
+    split. done. etransitivity; eauto.
+Qed.
+Lemma sublist_Permutation l1 l2 l3 :
+  l1 `sublist` l2 → l2 ≡ₚ l3 → ∃ l4, l1 ≡ₚ l4 ∧ l4 `sublist` l3.
+Proof.
+  intros Hl1l2 Hl2l3. revert l1 Hl1l2.
+  induction Hl2l3 as [|x l2 l3 ? IH|x y l2|l2 l2' l3 ? IH1 ? IH2].
+  * intros l1. by exists l1.
+  * intros l1. rewrite sublist_cons_r. intros [?|(l1'&l1''&?)]; subst.
+    { destruct (IH l1) as (l4&?&?); trivial.
+      exists l4. split. done. by constructor. }
+    destruct (IH l1') as (l4&?&Hl4); auto. exists (x :: l4).
+    split. by constructor. by constructor.
+  * intros l1. rewrite sublist_cons_r. intros [Hl1|(l1'&l1''&Hl1)]; subst.
+    { exists l1. split; [done|]. rewrite sublist_cons_r in Hl1.
+      destruct Hl1 as [?|(l1'&?&?)]; subst; by repeat constructor. }
+    rewrite sublist_cons_r in Hl1. destruct Hl1 as [?|(l1''&?&?)]; subst.
+    + exists (y :: l1'). by repeat constructor.
+    + exists (x :: y :: l1''). by repeat constructor.
+  * intros l1 ?. destruct (IH1 l1) as (l3'&?&?); trivial.
+    destruct (IH2 l3') as (l3'' &?&?); trivial. exists l3''.
+    split; [|done]. etransitivity; eauto.
+Qed.
+
+(** Properties of the [contains] predicate *)
+Lemma contains_length l1 l2 : l1 `contains` l2 → length l1 ≤ length l2.
+Proof. induction 1; simpl; auto with lia. Qed.
+Lemma contains_nil_l l : [] `contains` l.
+Proof. induction l; constructor; auto. Qed.
+Lemma contains_nil_r l : l `contains` [] ↔ l = [].
+Proof.
+  split; [|intros; subst; constructor].
+  intros Hl. apply contains_length in Hl. destruct l; simpl in *; auto with lia.
+Qed.
+
+Global Instance: PreOrder (@contains A).
+Proof.
+  split.
+  * intros l. induction l; constructor; auto.
+  * red. apply contains_trans.
+Qed.
+
+Lemma Permutation_contains l1 l2 : l1 ≡ₚ l2 → l1 `contains` l2.
+Proof. induction 1; econstructor; eauto. Qed.
+Lemma sublist_contains l1 l2 : l1 `sublist` l2 → l1 `contains` l2.
+Proof. induction 1; constructor; auto. Qed.
+Lemma contains_Permutation_alt l1 l2 :
+  length l2 ≤ length l1 → l1 `contains` l2 → l1 ≡ₚ l2.
+Proof.
+  intros Hl21 Hl12. revert Hl21. elim Hl12; clear l1 l2 Hl12; simpl.
+  * constructor.
+  * constructor; auto with lia.
+  * constructor; auto with lia.
+  * intros x l1 l2 ? IH ?. feed specialize IH; [lia|].
+    apply Permutation_length in IH. lia.
+  * intros l1 l2 l3 Hl12 ? Hl23 ?.
+    apply contains_length in Hl12. apply contains_length in Hl23.
+    transitivity l2; auto with lia.
+Qed.
+Lemma contains_Permutation l1 l2 :
+  length l2 = length l1 → l1 `contains` l2 → l1 ≡ₚ l2.
+Proof. intro. apply contains_Permutation_alt. lia. Qed.
+
+Global Instance: Proper ((≡ₚ) ==> (≡ₚ) ==> iff) (@contains A).
+Proof.
+  intros l1 l2 ? k1 k2 ?. split; intros.
+  * transitivity l1. by apply Permutation_contains.
+    transitivity k1. done. by apply Permutation_contains.
+  * transitivity l2. by apply Permutation_contains.
+    transitivity k2. done. by apply Permutation_contains.
+Qed.
+Global Instance: AntiSymmetric (≡ₚ) (@contains A).
+Proof. red. auto using contains_Permutation_alt, contains_length. Qed.
+
+Lemma contains_take l i : take i l `contains` l.
+Proof. auto using sublist_take, sublist_contains. Qed.
+Lemma contains_drop l i : drop i l `contains` l.
+Proof. auto using sublist_drop, sublist_contains. Qed.
+Lemma contains_delete l i : delete i l `contains` l.
+Proof. auto using sublist_delete, sublist_contains. Qed.
+Lemma contains_delete_list l is : delete_list is l `sublist` l.
+Proof. auto using sublist_delete_list, sublist_contains. Qed.
+
+Lemma contains_sublist_l l1 l3 :
+  l1 `contains` l3 ↔ ∃ l2, l1 `sublist` l2 ∧ l2 ≡ₚ l3.
+Proof.
+  split.
+  { intros Hl13. elim Hl13; clear l1 l3 Hl13.
+    * by eexists [].
+    * intros x l1 l3 ? (l2&?&?). exists (x :: l2). by repeat constructor.
+    * intros x y l. exists (y :: x :: l). by repeat constructor.
+    * intros x l1 l3 ? (l2&?&?). exists (x :: l2). by repeat constructor.
+    * intros l1 l3 l5 ? (l2&?&?) ? (l4&?&?).
+      destruct (Permutation_sublist l2 l3 l4) as (l3'&?&?); trivial.
+      exists l3'. split; etransitivity; eauto. }
+  intros (l2&?&?).
+  transitivity l2; auto using sublist_contains, Permutation_contains.
+Qed.
+Lemma contains_sublist_r l1 l3 :
+  l1 `contains` l3 ↔ ∃ l2, l1 ≡ₚ l2 ∧ l2 `sublist` l3.
+Proof.
+  rewrite contains_sublist_l.
+  split; intros (l2&?&?); eauto using sublist_Permutation, Permutation_sublist.
+Qed.
+
+Lemma contains_inserts_l k l1 l2 : l1 `contains` l2 → l1 `contains` k ++ l2.
+Proof. induction k; try constructor; auto. Qed.
+Lemma contains_inserts_r k l1 l2 : l1 `contains` l2 → l1 `contains` l2 ++ k.
+Proof. rewrite (commutative (++)). apply contains_inserts_l. Qed.
+Lemma contains_skips_l k l1 l2 : l1 `contains` l2 → k ++ l1 `contains` k ++ l2.
+Proof. induction k; try constructor; auto. Qed.
+Lemma contains_skips_r k l1 l2 : l1 `contains` l2 → l1 ++ k `contains` l2 ++ k.
+Proof. rewrite !(commutative (++) _ k). apply contains_skips_l. Qed.
+Lemma contains_app l1 l2 k1 k2 :
+  l1 `contains` l2 → k1 `contains` k2 → l1 ++ k1 `contains` l2 ++ k2.
+Proof.
+  transitivity (l1 ++ k2); auto using contains_skips_l, contains_skips_r.
+Qed.
+
+Lemma contains_cons_r x l k :
+  l `contains` x :: k ↔ l `contains` k ∨ ∃ l', l ≡ₚ x :: l' ∧ l' `contains` k.
+Proof.
+  split.
+  * rewrite contains_sublist_r. intros (l'&E&Hl').
+    rewrite sublist_cons_r in Hl'. destruct Hl' as [?|(?&?&?)]; subst.
+    + left. rewrite E. eauto using sublist_contains.
+    + right. eauto using sublist_contains.
+  * intros [?|(?&E&?)]; [|rewrite E]; by constructor.
+Qed.
+Lemma contains_cons_l x l k :
+  x :: l `contains` k ↔ ∃ k', k ≡ₚ x :: k' ∧ l `contains` k'.
+Proof.
+  split.
+  * rewrite contains_sublist_l. intros (l'&Hl'&E).
+    rewrite sublist_cons_l in Hl'. destruct Hl' as (k1&k2&?&?); subst.
+    exists (k1 ++ k2). split; eauto using contains_inserts_l, sublist_contains.
+    by rewrite Permutation_middle.
+  * intros (?&E&?). rewrite E. by constructor.
+Qed.
+Lemma contains_app_r l k1 k2 :
+  l `contains` k1 ++ k2 ↔ ∃ l1 l2,
+    l ≡ₚ l1 ++ l2 ∧ l1 `contains` k1 ∧ l2 `contains` k2.
+Proof.
+  split.
+  * rewrite contains_sublist_r. intros (l'&E&Hl').
+    rewrite sublist_app_r in Hl'. destruct Hl' as (l1&l2&?&?&?); subst.
+    exists l1 l2. eauto using sublist_contains.
+  * intros (?&?&E&?&?). rewrite E. eauto using contains_app.
+Qed.
+Lemma contains_app_l l1 l2 k :
+  l1 ++ l2 `contains` k ↔ ∃ k1 k2,
+    k ≡ₚ k1 ++ k2 ∧ l1 `contains` k1 ∧ l2 `contains` k2.
+Proof.
+  split.
+  * rewrite contains_sublist_l. intros (l'&Hl'&E).
+    rewrite sublist_app_l in Hl'. destruct Hl' as (k1&k2&?&?&?); subst.
+    exists k1 k2. split. done. eauto using sublist_contains.
+  * intros (?&?&E&?&?). rewrite E. eauto using contains_app.
+Qed.
+Lemma contains_app_inv_l l1 l2 k :
+  k ++ l1 `contains` k ++ l2 → l1 `contains` l2.
+Proof.
+  induction k as [|y k IH]; simpl; [done |].
+  rewrite contains_cons_l. intros (?&E&?).
+  apply Permutation_cons_inv in E. apply IH. by rewrite E.
+Qed.
+Lemma contains_app_inv_r l1 l2 k :
+  l1 ++ k `contains` l2 ++ k → l1 `contains` l2.
+Proof.
+  revert l1 l2. induction k as [|y k IH]; intros l1 l2.
+  { by rewrite !(right_id_L [] (++)). }
+  intros. feed pose proof (IH (l1 ++ [y]) (l2 ++ [y])) as Hl12.
+  { by rewrite <-!(associative_L (++)). }
+  rewrite contains_app_l in Hl12. destruct Hl12 as (k1&k2&E1&?&Hk2).
+  rewrite contains_cons_l in Hk2. destruct Hk2 as (k2'&E2&?).
+  rewrite E2, (Permutation_cons_append k2'), (associative_L (++)) in E1.
+  apply Permutation_app_inv_r in E1. rewrite E1. eauto using contains_inserts_r.
+Qed.
+Lemma contains_cons_middle x l k1 k2 :
+  l `contains` k1 ++ k2 → x :: l `contains` k1 ++ x :: k2.
+Proof. rewrite <-Permutation_middle. by apply contains_skip. Qed.
+Lemma contains_app_middle l1 l2 k1 k2 :
+  l2 `contains` k1 ++ k2 → l1 ++ l2 `contains` k1 ++ l1 ++ k2.
+Proof.
+  rewrite !(associative (++)), (commutative (++) k1 l1), <-(associative_L (++)).
+  by apply contains_skips_l.
+Qed.
+Lemma contains_middle l k1 k2 : l `contains` k1 ++ l ++ k2.
+Proof. by apply contains_inserts_l, contains_inserts_r. Qed.
+
+Lemma Permutation_alt l1 l2 :
+  l1 ≡ₚ l2 ↔ length l1 = length l2 ∧ l1 `contains` l2.
+Proof.
+  split.
+  * intros Hl. by rewrite Hl.
+  * intros [??]. auto using contains_Permutation.
+Qed.
+
+Section contains_dec.
+  Context `{∀ x y, Decision (x = y)}.
+
+  Lemma list_remove_Permutation l1 l2 k1 x :
+    l1 ≡ₚ l2 → list_remove x l1 = Some k1 →
+    ∃ k2, list_remove x l2 = Some k2 ∧ k1 ≡ₚ k2.
+  Proof.
+    intros Hl. revert k1.
+    induction Hl as [|y l1 l2 ? IH|y1 y2 l|l1 l2 l3 ? IH1 ? IH2];
+      simpl; intros k1 Hk1.
+    * done.
+    * case_decide; simplify_equality; eauto.
+      destruct (list_remove x l1) as [l|] eqn:?; simplify_equality.
+      destruct (IH l) as (?&?&?); simplify_option_equality; eauto.
+    * repeat case_decide; simplify_option_equality;
+        eauto using Permutation_swap.
+    * destruct (IH1 k1) as (k2&?&?); trivial.
+      destruct (IH2 k2) as (k3&?&?); trivial.
+      exists k3. split; eauto. by transitivity k2.
+  Qed.
+
+  Lemma list_remove_Some l k x : list_remove x l = Some k → l ≡ₚ x :: k.
+  Proof.
+    revert k. induction l as [|y l IH]; simpl; intros k ?; [done |].
+    case_decide; simplify_option_equality; [done|].
+    by rewrite Permutation_swap, <-IH.
+  Qed.
+  Lemma list_remove_Some_inv l k x :
+    l ≡ₚ x :: k → ∃ k', list_remove x l = Some k' ∧ k ≡ₚ k'.
+  Proof.
+    intros. destruct (list_remove_Permutation (x :: k) l k x) as (k'&?&?).
+    * done.
+    * simpl; by case_decide.
+    * by exists k'.
+  Qed.
+
+  Lemma list_remove_list_contains l1 l2 :
+    l1 `contains` l2 ↔ is_Some (list_remove_list l1 l2).
+  Proof.
+    rewrite is_Some_alt. split.
+    * revert l2. induction l1 as [|x l1 IH]; simpl.
+      { intros l2 _. by exists l2. }
+      intros l2. rewrite contains_cons_l. intros (k&Hk&?).
+      destruct (list_remove_Some_inv l2 k x) as (k2&?&Hk2); trivial.
+      simplify_option_equality. apply IH. by rewrite <-Hk2.
+    * intros [k Hk]. revert l2 k Hk.
+      induction l1 as [|x l1 IH]; simpl; intros l2 k.
+      { intros. apply contains_nil_l. }
+      destruct (list_remove x l2) as [k'|] eqn:?; intros; simplify_equality.
+      rewrite contains_cons_l. eauto using list_remove_Some.
+  Qed.
+
+  Global Instance contains_dec l1 l2 : Decision (l1 `contains` l2).
+  Proof.
+   refine (cast_if (decide (is_Some (list_remove_list l1 l2))));
+    abstract (rewrite list_remove_list_contains; tauto).
+  Defined.
+  Global Instance Permutation_dec l1 l2 : Decision (l1 ≡ₚ l2).
+  Proof.
+   refine (cast_if_and
+    (decide (length l1 = length l2)) (decide (l1 `contains` l2)));
+    abstract (rewrite Permutation_alt; tauto).
+  Defined.
+End contains_dec.
+End general_properties.
+
+(** ** Properties of the [same_length] predicate *)
+Instance: ∀ A, Reflexive (@same_length A A).
+Proof. intros A l. induction l; constructor; auto. Qed.
+Instance: ∀ A, Symmetric (@same_length A A).
 Proof. induction 1; constructor; auto. Qed.
 
 Section same_length.
   Context {A B : Type}.
+  Implicit Types l : list A. Implicit Types k : list B.
 
-  Lemma same_length_length_1 (l : list A) (k : list B) :
-    same_length l k → length l = length k.
+  Lemma same_length_length_1 l k : l `same_length` k → length l = length k.
   Proof. induction 1; simpl; auto. Qed.
-  Lemma same_length_length_2 (l : list A) (k : list B) :
-    length l = length k → same_length l k.
+  Lemma same_length_length_2 l k : length l = length k → l `same_length` k.
   Proof.
     revert k. induction l; intros [|??]; try discriminate;
       constructor; auto with arith.
   Qed.
-  Lemma same_length_length (l : list A) (k : list B) :
-    same_length l k ↔ length l = length k.
+  Lemma same_length_length l k : l `same_length` k ↔ length l = length k.
   Proof. split; auto using same_length_length_1, same_length_length_2. Qed.
 
-  Lemma same_length_lookup (l : list A) (k : list B) i :
-    same_length l k → is_Some (l !! i) → is_Some (k !! i).
+  Lemma same_length_lookup l k i :
+    l `same_length` k → is_Some (l !! i) → is_Some (k !! i).
   Proof.
-    rewrite same_length_length.
-    setoid_rewrite lookup_lt_length.
+    rewrite same_length_length. setoid_rewrite lookup_lt_length.
     intros E. by rewrite E.
   Qed.
 
-  Lemma same_length_take (l1 : list A) (l2 : list B) n :
-    same_length l1 l2 →
-    same_length (take n l1) (take n l2).
+  Lemma same_length_take l k n :
+    l `same_length` k → take n l `same_length` take n k.
+  Proof. intros Hl. revert n; induction Hl; intros [|n]; constructor; auto. Qed.
+  Lemma same_length_drop l k n :
+    l `same_length` k → drop n l `same_length` drop n k.
   Proof.
-    intros Hl. revert n; induction Hl; intros [|n]; constructor; auto.
+    intros Hl. revert n; induction Hl; intros [|]; simpl; try constructor; auto.
   Qed.
-  Lemma same_length_drop (l1 : list A) (l2 : list B) n :
-    same_length l1 l2 →
-    same_length (drop n l1) (drop n l2).
-  Proof.
-    intros Hl.
-    revert n; induction Hl; intros [|n]; simpl; try constructor; auto.
-  Qed.
-  Lemma same_length_resize (l1 : list A) (l2 : list B) x1 x2 n :
-    same_length (resize n x1 l1) (resize n x2 l2).
+  Lemma same_length_resize l k x y n : resize n x l `same_length` resize n y k.
   Proof. apply same_length_length. by rewrite !resize_length. Qed.
 End same_length.
 
@@ -1165,8 +1678,10 @@ End same_length.
 Section Forall_Exists.
   Context {A} (P : A → Prop).
 
-  Lemma Forall_forall l :
-    Forall P l ↔ ∀ x, x ∈ l → P x.
+  Definition Forall_nil_2 := @Forall_nil A.
+  Definition Forall_cons_2 := @Forall_cons A.
+
+  Lemma Forall_forall l : Forall P l ↔ ∀ x, x ∈ l → P x.
   Proof.
     split.
     * induction 1; inversion 1; subst; auto.
@@ -1200,46 +1715,42 @@ Section Forall_Exists.
   Proof. split; subst; induction 1; constructor; firstorder. Qed.
 
   Lemma Forall_iff l (Q : A → Prop) :
-    (∀ x, P x ↔ Q x) →
-    Forall P l ↔ Forall Q l.
+    (∀ x, P x ↔ Q x) → Forall P l ↔ Forall Q l.
   Proof. intros H. apply Forall_proper. red. apply H. done. Qed.
 
   Lemma Forall_delete l i : Forall P l → Forall P (delete i l).
+  Proof. intros H. revert i. by induction H; intros [|i]; try constructor. Qed.
+  Lemma Forall_lookup l : Forall P l ↔ ∀ i x, l !! i = Some x → P x.
   Proof.
-    intros H. revert i.
-    by induction H; intros [|i]; try constructor.
-  Qed.
-  Lemma Forall_lookup l :
-    Forall P l ↔ ∀ i x, l !! i = Some x → P x.
-  Proof.
-    rewrite Forall_forall. setoid_rewrite elem_of_list_lookup.
-    naive_solver.
+    rewrite Forall_forall. setoid_rewrite elem_of_list_lookup. naive_solver.
   Qed.
-  Lemma Forall_lookup_1 l i x :
-    Forall P l → l !! i = Some x → P x.
+  Lemma Forall_lookup_1 l i x : Forall P l → l !! i = Some x → P x.
   Proof. rewrite Forall_lookup. eauto. Qed.
-  Lemma Forall_lookup_2 l :
-    (∀ i x, l !! i = Some x → P x) → Forall P l.
+  Lemma Forall_lookup_2 l : (∀ i x, l !! i = Some x → P x) → Forall P l.
   Proof. by rewrite Forall_lookup. Qed.
 
   Lemma Forall_alter f l i :
-    Forall P l →
-    (∀ x, l !! i = Some x → P x → P (f x)) →
+    Forall P l → (∀ x, l !! i = Some x → P x → P (f x)) →
     Forall P (alter f i l).
   Proof.
-    intros Hl. revert i.
-    induction Hl; simpl; intros [|i]; constructor; auto.
+    intros Hl. revert i. induction Hl; simpl; intros [|i]; constructor; auto.
   Qed.
 
-  Lemma Forall_replicate n x :
-    P x → Forall P (replicate n x).
+  Lemma Forall_replicate n x : P x → Forall P (replicate n x).
   Proof. induction n; simpl; constructor; auto. Qed.
-  Lemma Forall_replicate_eq n (x : A) :
-    Forall (=x) (replicate n x).
+  Lemma Forall_replicate_eq n (x : A) : Forall (=x) (replicate n x).
   Proof. induction n; simpl; constructor; auto. Qed.
 
-  Lemma Exists_exists l :
-    Exists P l ↔ ∃ x, x ∈ l ∧ P x.
+  Lemma Forall_take n l : Forall P l → Forall P (take n l).
+  Proof. intros Hl. revert n. induction Hl; intros [|?]; simpl; auto. Qed.
+  Lemma Forall_drop n l : Forall P l → Forall P (drop n l).
+  Proof. intros Hl. revert n. induction Hl; intros [|?]; simpl; auto. Qed.
+  Lemma Forall_resize n x l : P x → Forall P l → Forall P (resize n x l).
+  Proof.
+    intros ? Hl. revert n.
+    induction Hl; intros [|?]; simpl; auto using Forall_replicate.
+  Qed.
+  Lemma Exists_exists l : Exists P l ↔ ∃ x, x ∈ l ∧ P x.
   Proof.
     split.
     * induction 1 as [x|y ?? IH].
@@ -1255,9 +1766,7 @@ Section Forall_Exists.
   Proof.
     split.
     * induction l1; inversion 1; intuition.
-    * intros [H|H].
-      + induction H; simpl; intuition.
-      + induction l1; simpl; intuition.
+    * intros [H|H]; [induction H | induction l1]; simpl; intuition.
   Qed.
 
   Global Instance Exists_proper:
@@ -1308,39 +1817,34 @@ Section Forall_Exists.
     end.
 End Forall_Exists.
 
+Lemma Forall_swap {A B} (Q : A → B → Prop) l1 l2 :
+  Forall (λ y, Forall (Q y) l1) l2 ↔ Forall (λ x, Forall (flip Q x) l2) l1.
+Proof. repeat setoid_rewrite Forall_forall. simpl. split; eauto. Qed.
+
 (** ** Properties of the [Forall2] predicate *)
 Section Forall2.
   Context {A B} (P : A → B → Prop).
 
-  Lemma Forall2_nil_inv_l k :
-    Forall2 P [] k → k = [].
+  Lemma Forall2_nil_inv_l k : Forall2 P [] k → k = [].
   Proof. by inversion 1. Qed.
-  Lemma Forall2_nil_inv_r k :
-    Forall2 P k [] → k = [].
+  Lemma Forall2_nil_inv_r k : Forall2 P k [] → k = [].
   Proof. by inversion 1. Qed.
-
   Lemma Forall2_cons_inv l1 l2 x1 x2 :
     Forall2 P (x1 :: l1) (x2 :: l2) → P x1 x2 ∧ Forall2 P l1 l2.
   Proof. by inversion 1. Qed.
   Lemma Forall2_cons_inv_l l1 k x1 :
-    Forall2 P (x1 :: l1) k → ∃ x2 l2,
-      P x1 x2 ∧ Forall2 P l1 l2 ∧ k = x2 :: l2.
+    Forall2 P (x1 :: l1) k → ∃ x2 l2, P x1 x2 ∧ Forall2 P l1 l2 ∧ k = x2 :: l2.
   Proof. inversion 1; subst; eauto. Qed.
   Lemma Forall2_cons_inv_r k l2 x2 :
-    Forall2 P k (x2 :: l2) → ∃ x1 l1,
-      P x1 x2 ∧ Forall2 P l1 l2 ∧ k = x1 :: l1.
+    Forall2 P k (x2 :: l2) → ∃ x1 l1, P x1 x2 ∧ Forall2 P l1 l2 ∧ k = x1 :: l1.
   Proof. inversion 1; subst; eauto. Qed.
-  Lemma Forall2_cons_nil_inv l1 x1 :
-    Forall2 P (x1 :: l1) [] → False.
+  Lemma Forall2_cons_nil_inv l1 x1 : Forall2 P (x1 :: l1) [] → False.
   Proof. by inversion 1. Qed.
-  Lemma Forall2_nil_cons_inv l2 x2 :
-    Forall2 P [] (x2 :: l2) → False.
+  Lemma Forall2_nil_cons_inv l2 x2 : Forall2 P [] (x2 :: l2) → False.
   Proof. by inversion 1. Qed.
-
   Lemma Forall2_app_inv l1 l2 k1 k2 :
-    same_length l1 k1 →
-    Forall2 P (l1 ++ l2) (k1 ++ k2) →
-    Forall2 P l1 k1 ∧ Forall2 P l2 k2.
+    l1 `same_length` k1 →
+    Forall2 P (l1 ++ l2) (k1 ++ k2) → Forall2 P l1 k1 ∧ Forall2 P l2 k2.
   Proof. induction 1. done. inversion 1; naive_solver. Qed.
   Lemma Forall2_app_inv_l l1 l2 k :
     Forall2 P (l1 ++ l2) k →
@@ -1351,71 +1855,51 @@ Section Forall2.
       ∃ l1 l2, Forall2 P l1 k1 ∧ Forall2 P l2 k2 ∧ l = l1 ++ l2.
   Proof. revert l. induction k1; simpl; inversion 1; naive_solver. Qed.
 
-  Lemma Forall2_length l1 l2 :
-    Forall2 P l1 l2 → length l1 = length l2.
+  Lemma Forall2_length l1 l2 : Forall2 P l1 l2 → length l1 = length l2.
   Proof. induction 1; simpl; auto. Qed.
-  Lemma Forall2_same_length l1 l2 :
-    Forall2 P l1 l2 →
-    same_length l1 l2.
+  Lemma Forall2_same_length l1 l2 : Forall2 P l1 l2 → l1 `same_length` l2.
   Proof. induction 1; constructor; auto. Qed.
 
-  Lemma Forall2_flip l1 l2 :
-    Forall2 P l1 l2 ↔ Forall2 (flip P) l2 l1.
+  Lemma Forall2_flip l1 l2 : Forall2 P l1 l2 ↔ Forall2 (flip P) l2 l1.
   Proof. split; induction 1; constructor; auto. Qed.
   Lemma Forall2_impl (Q : A → B → Prop) l1 l2 :
     Forall2 P l1 l2 → (∀ x y, P x y → Q x y) → Forall2 Q l1 l2.
   Proof. intros H ?. induction H; auto. Defined.
-
   Lemma Forall2_unique l k1 k2 :
-    Forall2 P l k1 →
-    Forall2 P l k2 →
-    (∀ x y1 y2, P x y1 → P x y2 → y1 = y2) →
-    k1 = k2.
+    Forall2 P l k1 →  Forall2 P l k2 →
+    (∀ x y1 y2, P x y1 → P x y2 → y1 = y2) → k1 = k2.
   Proof.
-    intros H. revert k2.
-    induction H; inversion_clear 1; intros; f_equal; eauto.
+    intros H. revert k2. induction H; inversion_clear 1; intros; f_equal; eauto.
   Qed.
 
   Lemma Forall2_Forall_l (Q : A → Prop) l k :
-    Forall2 P l k →
-    Forall (λ y, ∀ x, P x y → Q x) k →
-    Forall Q l.
+    Forall2 P l k → Forall (λ y, ∀ x, P x y → Q x) k → Forall Q l.
   Proof. induction 1; inversion_clear 1; eauto. Qed.
   Lemma Forall2_Forall_r (Q : B → Prop) l k :
-    Forall2 P l k →
-    Forall (λ x, ∀ y, P x y → Q y) l →
-    Forall Q k.
+    Forall2 P l k → Forall (λ x, ∀ y, P x y → Q y) l → Forall Q k.
   Proof. induction 1; inversion_clear 1; eauto. Qed.
 
   Lemma Forall2_lookup_lr l1 l2 i x y :
-    Forall2 P l1 l2 →
-      l1 !! i = Some x → l2 !! i = Some y → P x y.
+    Forall2 P l1 l2 → l1 !! i = Some x → l2 !! i = Some y → P x y.
   Proof.
-    intros H. revert i. induction H.
-    * discriminate.
-    * intros [|?] ??; simpl in *; simplify_equality; eauto.
+    intros H. revert i. induction H; [done|].
+    intros [|?] ??; simpl in *; simplify_equality; eauto.
   Qed.
   Lemma Forall2_lookup_l l1 l2 i x :
-    Forall2 P l1 l2 → l1 !! i = Some x → ∃ y,
-      l2 !! i = Some y ∧ P x y.
+    Forall2 P l1 l2 → l1 !! i = Some x → ∃ y, l2 !! i = Some y ∧ P x y.
   Proof.
-    intros H. revert i. induction H.
-    * discriminate.
-    * intros [|?] ?; simpl in *; simplify_equality; eauto.
+    intros H. revert i. induction H; [done|].
+    intros [|?] ?; simpl in *; simplify_equality; eauto.
   Qed.
   Lemma Forall2_lookup_r l1 l2 i y :
-    Forall2 P l1 l2 → l2 !! i = Some y → ∃ x,
-      l1 !! i = Some x ∧ P x y.
+    Forall2 P l1 l2 → l2 !! i = Some y → ∃ x, l1 !! i = Some x ∧ P x y.
   Proof.
-    intros H. revert i. induction H.
-    * discriminate.
-    * intros [|?] ?; simpl in *; simplify_equality; eauto.
+    intros H. revert i. induction H; [done|].
+    intros [|?] ?; simpl in *; simplify_equality; eauto.
   Qed.
-
   Lemma Forall2_lookup_2 l1 l2 :
-    same_length l1 l2 →
-    (∀ i x y, l1 !! i = Some x → l2 !! i = Some y → P x y) →
-    Forall2 P l1 l2.
+    l1 `same_length` l2 →
+    (∀ i x y, l1 !! i = Some x → l2 !! i = Some y → P x y) → Forall2 P l1 l2.
   Proof.
     eauto using Forall2_same_length, Forall2_lookup_lr.
     intros Hl Hlookup. induction Hl as [|????? IH]; constructor.
@@ -1423,99 +1907,70 @@ Section Forall2.
     * apply IH. intros i. apply (Hlookup (S i)).
   Qed.
   Lemma Forall2_lookup l1 l2 :
-    Forall2 P l1 l2 ↔ same_length l1 l2 ∧
+    Forall2 P l1 l2 ↔ l1 `same_length` l2 ∧
       (∀ i x y, l1 !! i = Some x → l2 !! i = Some y → P x y).
   Proof.
     split.
     * eauto using Forall2_same_length, Forall2_lookup_lr.
     * intros [??]; eauto using Forall2_lookup_2.
   Qed.
-
   Lemma Forall2_alter_l f l1 l2 i :
-    Forall2 P l1 l2 →
-    (∀ x1 x2,
+    Forall2 P l1 l2 → (∀ x1 x2,
       l1 !! i = Some x1 → l2 !! i = Some x2 → P x1 x2 → P (f x1) x2) →
     Forall2 P (alter f i l1) l2.
   Proof.
-    intros Hl. revert i.
-    induction Hl; simpl; intros [|i]; constructor; auto.
+    intros Hl. revert i. induction Hl; simpl; intros [|i]; constructor; auto.
   Qed.
   Lemma Forall2_alter_r f l1 l2 i :
-    Forall2 P l1 l2 →
-    (∀ x1 x2,
+    Forall2 P l1 l2 → (∀ x1 x2,
       l1 !! i = Some x1 → l2 !! i = Some x2 → P x1 x2 → P x1 (f x2)) →
     Forall2 P l1 (alter f i l2).
   Proof.
-    intros Hl. revert i.
-    induction Hl; simpl; intros [|i]; constructor; auto.
+    intros Hl. revert i. induction Hl; simpl; intros [|i]; constructor; auto.
   Qed.
   Lemma Forall2_alter f g l1 l2 i :
-    Forall2 P l1 l2 →
-    (∀ x1 x2,
+    Forall2 P l1 l2 → (∀ x1 x2,
       l1 !! i = Some x1 → l2 !! i = Some x2 → P x1 x2 → P (f x1) (g x2)) →
     Forall2 P (alter f i l1) (alter g i l2).
   Proof.
-    intros Hl. revert i.
-    induction Hl; simpl; intros [|i]; constructor; auto.
+    intros Hl. revert i. induction Hl; simpl; intros [|i]; constructor; auto.
   Qed.
-
   Lemma Forall2_delete l1 l2 i :
-    Forall2 P l1 l2 →
-    Forall2 P (delete i l1) (delete i l2).
+    Forall2 P l1 l2 → Forall2 P (delete i l1) (delete i l2).
   Proof.
-    intros Hl12. revert i.
-    induction Hl12; intros [|i]; simpl; intuition.
+    intros Hl12. revert i. induction Hl12; intros [|i]; simpl; intuition.
   Qed.
-
   Lemma Forall2_replicate_l l n x :
-    Forall (P x) l →
-    length l = n →
-    Forall2 P (replicate n x) l.
+    Forall (P x) l → length l = n → Forall2 P (replicate n x) l.
   Proof.
     intros Hl. revert n.
     induction Hl; intros [|?] ?; simplify_equality; constructor; auto.
   Qed.
   Lemma Forall2_replicate_r l n x :
-    Forall (flip P x) l →
-    length l = n →
-    Forall2 P l (replicate n x).
+    Forall (flip P x) l → length l = n → Forall2 P l (replicate n x).
   Proof.
     intros Hl. revert n.
     induction Hl; intros [|?] ?; simplify_equality; constructor; auto.
   Qed.
   Lemma Forall2_replicate n x1 x2 :
-    P x1 x2 →
-    Forall2 P (replicate n x1) (replicate n x2).
+    P x1 x2 → Forall2 P (replicate n x1) (replicate n x2).
   Proof. induction n; simpl; constructor; auto. Qed.
-
   Lemma Forall2_take l1 l2 n :
-    Forall2 P l1 l2 →
-    Forall2 P (take n l1) (take n l2).
-  Proof.
-    intros Hl1l2. revert n.
-    induction Hl1l2; intros [|?]; simpl; auto.
-  Qed.
+    Forall2 P l1 l2 → Forall2 P (take n l1) (take n l2).
+  Proof. intros Hl1l2. revert n. induction Hl1l2; intros [|?]; simpl; auto. Qed.
   Lemma Forall2_drop l1 l2 n :
-    Forall2 P l1 l2 →
-    Forall2 P (drop n l1) (drop n l2).
-  Proof.
-    intros Hl1l2. revert n.
-    induction Hl1l2; intros [|?]; simpl; auto.
-  Qed.
+    Forall2 P l1 l2 → Forall2 P (drop n l1) (drop n l2).
+  Proof. intros Hl1l2. revert n. induction Hl1l2; intros [|?]; simpl; auto. Qed.
   Lemma Forall2_resize l1 l2 x1 x2 n :
-    P x1 x2 →
-    Forall2 P l1 l2 →
-    Forall2 P (resize n x1 l1) (resize n x2 l2).
+    P x1 x2 → Forall2 P l1 l2 → Forall2 P (resize n x1 l1) (resize n x2 l2).
   Proof.
     intros. rewrite !resize_spec, (Forall2_length l1 l2) by done.
     auto using Forall2_app, Forall2_take, Forall2_replicate.
   Qed.
 
   Lemma Forall2_resize_ge_l l1 l2 x1 x2 n m :
-    (∀ x, P x x2) →
-    n ≤ m →
-    Forall2 P (resize n x1 l1) l2 →
-    Forall2 P (resize m x1 l1) (resize m x2 l2).
+    P x1 x2 → Forall (flip P x2) l1 → n ≤ m →
+    Forall2 P (resize n x1 l1) l2 → Forall2 P (resize m x1 l1) (resize m x2 l2).
   Proof.
     intros. assert (n = length l2).
     { by rewrite <-(Forall2_length (resize n x1 l1) l2), resize_length. }
@@ -1523,13 +1978,11 @@ Section Forall2.
     rewrite !resize_plus, resize_all, drop_all, resize_nil.
     apply Forall2_app; [done |].
     apply Forall2_replicate_r; [| by rewrite resize_length].
-    by apply Forall_true.
+    eauto using Forall_resize, Forall_drop.
   Qed.
   Lemma Forall2_resize_ge_r l1 l2 x1 x2 n m :
-    (∀ x3, P x1 x3) →
-    n ≤ m →
-    Forall2 P l1 (resize n x2 l2) →
-    Forall2 P (resize m x1 l1) (resize m x2 l2).
+    P x1 x2 → Forall (P x1) l2 → n ≤ m →
+    Forall2 P l1 (resize n x2 l2) → Forall2 P (resize m x1 l1) (resize m x2 l2).
   Proof.
     intros. assert (n = length l1).
     { by rewrite (Forall2_length l1 (resize n x2 l2)), resize_length. }
@@ -1537,17 +1990,14 @@ Section Forall2.
     rewrite !resize_plus, resize_all, drop_all, resize_nil.
     apply Forall2_app; [done |].
     apply Forall2_replicate_l; [| by rewrite resize_length].
-    by apply Forall_true.
+    eauto using Forall_resize, Forall_drop.
   Qed.
 
-  Lemma Forall2_trans {C} (Q : B → C → Prop) (R : A → C → Prop) l1 l2 l3 :
+  Lemma Forall2_transitive {C} (Q : B → C → Prop) (R : A → C → Prop) l1 l2 l3 :
     (∀ x1 x2 x3, P x1 x2 → Q x2 x3 → R x1 x3) →
-    Forall2 P l1 l2 →
-    Forall2 Q l2 l3 →
-    Forall2 R l1 l3.
+    Forall2 P l1 l2 → Forall2 Q l2 l3 → Forall2 R l1 l3.
   Proof.
-    intros ? Hl1l2. revert l3.
-    induction Hl1l2; inversion_clear 1; eauto.
+    intros ? Hl1l2. revert l3. induction Hl1l2; inversion_clear 1; eauto.
   Qed.
 
   Lemma Forall2_Forall (Q : A → A → Prop) l :
@@ -1575,19 +2025,37 @@ Section Forall2_order.
   Global Instance: Symmetric R → Symmetric (Forall2 R).
   Proof. intros. induction 1; constructor; auto. Qed.
   Global Instance: Transitive R → Transitive (Forall2 R).
-  Proof. intros ????. apply Forall2_trans. apply transitivity. Qed.
+  Proof. intros ????. apply Forall2_transitive. apply transitivity. Qed.
+  Global Instance: Equivalence R → Equivalence (Forall2 R).
+  Proof. split; apply _. Qed.
   Global Instance: PreOrder R → PreOrder (Forall2 R).
   Proof. split; apply _. Qed.
-  Global Instance: AntiSymmetric R → AntiSymmetric (Forall2 R).
+  Global Instance: AntiSymmetric (=) R → AntiSymmetric (=) (Forall2 R).
   Proof. induction 2; inversion_clear 1; f_equal; auto. Qed.
+
+  Global Instance: Proper (R ==> Forall2 R ==> Forall2 R) (::).
+  Proof. by constructor. Qed.
+  Global Instance: Proper (Forall2 R ==> Forall2 R ==> Forall2 R) (++).
+  Proof. repeat intro. eauto using Forall2_app. Qed.
+  Global Instance: Proper (Forall2 R ==> Forall2 R) (delete i).
+  Proof. repeat intro. eauto using Forall2_delete. Qed.
+  Global Instance: Proper (R ==> Forall2 R) (replicate n).
+  Proof. repeat intro. eauto using Forall2_replicate. Qed.
+  Global Instance: Proper (Forall2 R ==> Forall2 R) (take n).
+  Proof. repeat intro. eauto using Forall2_take. Qed.
+  Global Instance: Proper (Forall2 R ==> Forall2 R) (drop n).
+  Proof. repeat intro. eauto using Forall2_drop. Qed.
+  Global Instance: Proper (R ==> Forall2 R ==> Forall2 R) (resize n).
+  Proof. repeat intro. eauto using Forall2_resize. Qed.
+  Global Instance: Proper ((=) ==> R ==> Forall2 R ==> Forall2 R) insert.
+  Proof. repeat intro. subst. apply Forall2_alter; auto. Qed.
 End Forall2_order.
 
 (** * Properties of the monadic operations *)
 Section fmap.
   Context {A B : Type} (f : A → B).
 
-  Lemma list_fmap_compose {C} (g : B → C) l :
-    g ∘ f <$> l = g <$> f <$> l.
+  Lemma list_fmap_compose {C} (g : B → C) l : g ∘ f <$> l = g <$> f <$> l.
   Proof. induction l; simpl; f_equal; auto. Qed.
 
   Lemma list_fmap_ext (g : A → B) (l : list A) :
@@ -1603,22 +2071,13 @@ Section fmap.
   Lemma fmap_app l1 l2 : f <$> l1 ++ l2 = (f <$> l1) ++ (f <$> l2).
   Proof. induction l1; simpl; by f_equal. Qed.
 
-  Lemma fmap_nil_inv k :
-    f <$> k = [] → k = [].
+  Lemma fmap_nil_inv k :  f <$> k = [] → k = [].
   Proof. by destruct k. Qed.
   Lemma fmap_cons_inv y l k :
-    f <$> l = y :: k →
-    ∃ x l',
-      y = f x ∧
-      k = f <$> l' ∧
-      l = x :: l'.
+    f <$> l = y :: k → ∃ x l', y = f x ∧ k = f <$> l' ∧ l = x :: l'.
   Proof. intros. destruct l; simpl; simplify_equality; eauto. Qed.
   Lemma fmap_app_inv l k1 k2 :
-    f <$> l = k1 ++ k2 →
-    ∃ l1 l2,
-      k1 = f <$> l1 ∧
-      k2 = f <$> l2 ∧
-      l = l1 ++ l2.
+    f <$> l = k1 ++ k2 → ∃ l1 l2, k1 = f <$> l1 ∧ k2 = f <$> l2 ∧ l = l1 ++ l2.
   Proof.
     revert l. induction k1 as [|y k1 IH]; simpl.
     * intros l ?. by eexists [], l.
@@ -1631,11 +2090,9 @@ Section fmap.
   Proof. induction l; simpl; by f_equal. Qed.
   Lemma fmap_reverse l : f <$> reverse l = reverse (f <$> l).
   Proof.
-    induction l; simpl; [done |].
-    by rewrite !reverse_cons, fmap_app, IHl.
+    induction l; simpl; [done |]. by rewrite !reverse_cons, fmap_app, IHl.
   Qed.
-  Lemma fmap_replicate n x :
-    f <$> replicate n x = replicate n (f x).
+  Lemma fmap_replicate n x :  f <$> replicate n x = replicate n (f x).
   Proof. induction n; simpl; f_equal; auto. Qed.
 
   Lemma list_lookup_fmap l i : (f <$> l) !! i = f <$> (l !! i).
@@ -1648,11 +2105,9 @@ Section fmap.
   Qed.
 
   Lemma list_alter_fmap (g : A → A) (h : B → B) l i :
-    Forall (λ x, f (g x) = h (f x)) l →
-    f <$> alter g i l = alter h i (f <$> l).
+    Forall (λ x, f (g x) = h (f x)) l → f <$> alter g i l = alter h i (f <$> l).
   Proof.
-    intros Hl. revert i.
-    induction Hl; intros [|i]; simpl; f_equal; auto.
+    intros Hl. revert i. induction Hl; intros [|i]; simpl; f_equal; auto.
   Qed.
   Lemma elem_of_list_fmap_1 l x : x ∈ l → f x ∈ f <$> l.
   Proof. induction 1; simpl; rewrite elem_of_cons; intuition. Qed.
@@ -1669,25 +2124,24 @@ Section fmap.
     firstorder eauto using elem_of_list_fmap_1_alt, elem_of_list_fmap_2.
   Qed.
 
-  Lemma NoDup_fmap_1 (l : list A) :
-    NoDup (f <$> l) → NoDup l.
+  Lemma NoDup_fmap_1 l : NoDup (f <$> l) → NoDup l.
   Proof.
     induction l; simpl; inversion_clear 1; constructor; auto.
     rewrite elem_of_list_fmap in *. naive_solver.
   Qed.
-  Lemma NoDup_fmap_2 `{!Injective (=) (=) f} (l : list A) :
-    NoDup l → NoDup (f <$> l).
+  Lemma NoDup_fmap_2 `{!Injective (=) (=) f} l : NoDup l → NoDup (f <$> l).
   Proof.
-    induction 1; simpl; constructor; trivial.
-    rewrite elem_of_list_fmap. intros [y [Hxy ?]].
-    apply (injective f) in Hxy. by subst.
+    induction 1; simpl; constructor; trivial. rewrite elem_of_list_fmap.
+    intros [y [Hxy ?]]. apply (injective f) in Hxy. by subst.
   Qed.
-  Lemma NoDup_fmap `{!Injective (=) (=) f} (l : list A) :
-    NoDup (f <$> l) ↔ NoDup l.
+  Lemma NoDup_fmap `{!Injective (=) (=) f} l : NoDup (f <$> l) ↔ NoDup l.
   Proof. split; auto using NoDup_fmap_1, NoDup_fmap_2. Qed.
 
-  Global Instance fmap_Permutation_proper:
-    Proper (Permutation ==> Permutation) (fmap f).
+  Global Instance fmap_sublist: Proper (sublist ==> sublist) (fmap f).
+  Proof. induction 1; simpl; econstructor; eauto. Qed.
+  Global Instance fmap_contains: Proper (contains ==> contains) (fmap f).
+  Proof. induction 1; simpl; econstructor; eauto. Qed.
+  Global Instance fmap_Permutation: Proper ((≡ₚ) ==> (≡ₚ)) (fmap f).
   Proof. induction 1; simpl; econstructor; eauto. Qed.
 
   Lemma Forall_fmap_ext (g : A → B) (l : list A) :
@@ -1697,11 +2151,8 @@ Section fmap.
     * induction 1; simpl; f_equal; auto.
     * induction l; simpl; constructor; simplify_equality; auto.
   Qed.
-  Lemma Forall_fmap (l : list A) (P : B → Prop) :
-    Forall P (f <$> l) ↔ Forall (P ∘ f) l.
-  Proof.
-    split; induction l; inversion_clear 1; constructor; auto.
-  Qed.
+  Lemma Forall_fmap (P : B → Prop) l : Forall P (f <$> l) ↔ Forall (P ∘ f) l.
+  Proof. split; induction l; inversion_clear 1; constructor; auto. Qed.
 
   Lemma Forall2_fmap_l {C} (P : B → C → Prop) l1 l2 :
     Forall2 P (f <$> l1) l2 ↔ Forall2 (P ∘ f) l1 l2.
@@ -1714,26 +2165,20 @@ Section fmap.
     split; revert l1; induction l2; inversion_clear 1; constructor; auto.
   Qed.
   Lemma Forall2_fmap_1 {C D} (g : C → D) (P : B → D → Prop) l1 l2 :
-    Forall2 P (f <$> l1) (g <$> l2) →
-    Forall2 (λ x1 x2, P (f x1) (g x2)) l1 l2.
+    Forall2 P (f <$> l1) (g <$> l2) → Forall2 (λ x1 x2, P (f x1) (g x2)) l1 l2.
   Proof. revert l2; induction l1; intros [|??]; inversion_clear 1; auto. Qed.
   Lemma Forall2_fmap_2 {C D} (g : C → D) (P : B → D → Prop) l1 l2 :
-    Forall2 (λ x1 x2, P (f x1) (g x2)) l1 l2 →
-    Forall2 P (f <$> l1) (g <$> l2).
+    Forall2 (λ x1 x2, P (f x1) (g x2)) l1 l2 → Forall2 P (f <$> l1) (g <$> l2).
   Proof. induction 1; simpl; auto. Qed.
   Lemma Forall2_fmap {C D} (g : C → D) (P : B → D → Prop) l1 l2 :
-    Forall2 P (f <$> l1) (g <$> l2) ↔
-    Forall2 (λ x1 x2, P (f x1) (g x2)) l1 l2.
+    Forall2 P (f <$> l1) (g <$> l2) ↔ Forall2 (λ x1 x2, P (f x1) (g x2)) l1 l2.
   Proof. split; auto using Forall2_fmap_1, Forall2_fmap_2. Qed.
 
   Lemma mapM_fmap_Some (g : B → option A) (l : list A) :
-    (∀ x, g (f x) = Some x) →
-    mapM g (f <$> l) = Some l.
+    (∀ x, g (f x) = Some x) → mapM g (f <$> l) = Some l.
   Proof. intros. by induction l; simpl; simplify_option_equality. Qed.
   Lemma mapM_fmap_Some_inv (g : B → option A) (l : list A) (k : list B) :
-    (∀ x y, g y = Some x → y = f x) →
-    mapM g k = Some l →
-    k = f <$> l.
+    (∀ x y, g y = Some x → y = f x) → mapM g k = Some l → k = f <$> l.
   Proof.
     intros Hgf. revert l; induction k as [|??]; intros [|??] ?;
       simplify_option_equality; f_equiv; eauto.
@@ -1741,12 +2186,9 @@ Section fmap.
 End fmap.
 
 Lemma NoDup_fmap_fst {A B} (l : list (A * B)) :
-  (∀ x y1 y2, (x,y1) ∈ l → (x,y2) ∈ l → y1 = y2) →
-  NoDup l →
-  NoDup (fst <$> l).
+  (∀ x y1 y2, (x,y1) ∈ l → (x,y2) ∈ l → y1 = y2) → NoDup l → NoDup (fst <$> l).
 Proof.
-  intros Hunique.
-  induction 1 as [|[x1 y1] l Hin Hnodup IH]; simpl; constructor.
+  intros Hunique. induction 1 as [|[x1 y1] l Hin Hnodup IH]; simpl; constructor.
   * rewrite elem_of_list_fmap.
     intros [[x2 y2] [??]]; simpl in *; subst. destruct Hin.
     rewrite (Hunique x2 y1 y2); rewrite ?elem_of_cons; auto.
@@ -1757,24 +2199,43 @@ Qed.
 Section bind.
   Context {A B : Type} (f : A → list B).
 
-  Lemma bind_app (l1 l2 : list A) :
-    (l1 ++ l2) ≫= f = (l1 ≫= f) ++ (l2 ≫= f).
+  Global Instance mbind_sublist: Proper (sublist ==> sublist) (mbind f).
+  Proof.
+    induction 1; simpl; auto.
+    * done.
+    * by apply sublist_app.
+    * by apply sublist_inserts_l.
+  Qed.
+  Global Instance mbind_contains: Proper (contains ==> contains) (mbind f).
+  Proof.
+    induction 1; simpl; auto.
+    * done.
+    * by apply contains_app.
+    * by rewrite !(associative_L (++)), (commutative (++) (f _)).
+    * by apply contains_inserts_l.
+    * etransitivity; eauto.
+  Qed.
+  Global Instance mbind_Permutation: Proper ((≡ₚ) ==> (≡ₚ)) (mbind f).
+  Proof.
+    induction 1; simpl; auto.
+    * by f_equiv.
+    * by rewrite !(associative_L (++)), (commutative (++) (f _)).
+    * etransitivity; eauto.
+  Qed.
+
+  Lemma bind_app (l1 l2 : list A) : (l1 ++ l2) ≫= f = (l1 ≫= f) ++ (l2 ≫= f).
   Proof.
-    induction l1; simpl; [done|].
-    by rewrite <-(associative (++)), IHl1.
+    induction l1; simpl; [done|]. by rewrite <-(associative_L (++)), IHl1.
   Qed.
   Lemma elem_of_list_bind (x : B) (l : list A) :
     x ∈ l ≫= f ↔ ∃ y, x ∈ f y ∧ y ∈ l.
   Proof.
     split.
-    * induction l as [|y l IH]; simpl.
-      { inversion 1. }
+    * induction l as [|y l IH]; simpl; [inversion 1|].
       rewrite elem_of_app. intros [?|?].
       + exists y. split; [done | by left].
-      + destruct IH as [z [??]]. done.
-        exists z. split; [done | by right].
-    * intros [y [Hx Hy]].
-      induction Hy; simpl; rewrite elem_of_app; intuition.
+      + destruct IH as [z [??]]. done. exists z. split; [done | by right].
+    * intros [y [Hx Hy]]. induction Hy; simpl; rewrite elem_of_app; intuition.
   Qed.
 
   Lemma Forall2_bind {C D} (g : C → list D) (P : B → D → Prop) l1 l2 :
@@ -1786,420 +2247,189 @@ End bind.
 Section ret_join.
   Context {A : Type}.
 
-  Lemma list_join_bind (ls : list (list A)) :
-    mjoin ls = ls ≫= id.
+  Lemma list_join_bind (ls : list (list A)) : mjoin ls = ls ≫= id.
   Proof. induction ls; simpl; f_equal; auto. Qed.
 
-  Lemma elem_of_list_ret (x y : A) :
-    x ∈ @mret list _ A y ↔ x = y.
+  Global Instance mjoin_Permutation:
+    Proper (@Permutation (list A) ==> (≡ₚ)) mjoin.
+  Proof. intros ?? E. by rewrite !list_join_bind, E. Qed.
+
+  Lemma elem_of_list_ret (x y : A) : x ∈ @mret list _ A y ↔ x = y.
   Proof. apply elem_of_list_singleton. Qed.
   Lemma elem_of_list_join (x : A) (ls : list (list A)) :
     x ∈ mjoin ls ↔ ∃ l, x ∈ l ∧ l ∈ ls.
   Proof. by rewrite list_join_bind, elem_of_list_bind. Qed.
 
-  Lemma join_nil (ls : list (list A)) :
-    mjoin ls = [] ↔ Forall (= []) ls.
+  Lemma join_nil (ls : list (list A)) : mjoin ls = [] ↔ Forall (= []) ls.
   Proof.
     split.
     * by induction ls as [|[|??] ?]; constructor; auto.
     * by induction 1 as [|[|??] ?].
   Qed.
-  Lemma join_nil_1 (ls : list (list A)) :
-    mjoin ls = [] → Forall (= []) ls.
+  Lemma join_nil_1 (ls : list (list A)) : mjoin ls = [] → Forall (= []) ls.
   Proof. by rewrite join_nil. Qed.
-  Lemma join_nil_2 (ls : list (list A)) :
-    Forall (= []) ls → mjoin ls = [].
+  Lemma join_nil_2 (ls : list (list A)) : Forall (= []) ls → mjoin ls = [].
   Proof. by rewrite join_nil. Qed.
 
   Lemma join_length (ls : list (list A)) :
     length (mjoin ls) = foldr (plus ∘ length) 0 ls.
   Proof. by induction ls; simpl; rewrite ?app_length; f_equal. Qed.
   Lemma join_length_same (ls : list (list A)) n :
-    Forall (λ l, length l = n) ls →
-    length (mjoin ls) = length ls * n.
+    Forall (λ l, length l = n) ls → length (mjoin ls) = length ls * n.
   Proof. rewrite join_length. by induction 1; simpl; f_equal. Qed.
 
   Lemma lookup_join_same_length (ls : list (list A)) n i :
-    n ≠ 0 →
-    Forall (λ l, length l = n) ls →
+    n ≠ 0 → Forall (λ l, length l = n) ls →
     mjoin ls !! i = ls !! (i `div` n) ≫= (!! (i `mod` n)).
   Proof.
-    intros Hn Hls. revert i.
-    induction Hls as [|l ls ? Hls IH]; simpl; [done |]. intros i.
-    destruct (decide (i < n)) as [Hin|Hin].
+    intros Hn Hls. revert i. induction Hls as [|l ls ? Hls IH]; simpl; [done |].
+    intros i. destruct (decide (i < n)) as [Hin|Hin].
     * rewrite <-(NPeano.Nat.div_unique i n 0 i) by lia.
       rewrite <-(NPeano.Nat.mod_unique i n 0 i) by lia.
       simpl. rewrite lookup_app_l; auto with lia.
     * replace i with ((i - n) + 1 * n) by lia.
       rewrite NPeano.Nat.div_add, NPeano.Nat.mod_add by done.
       replace (i - n + 1 * n) with i by lia.
-      rewrite (plus_comm _ 1), lookup_app_r_alt, IH by lia.
-      by subst.
+      rewrite (plus_comm _ 1), lookup_app_r_alt, IH by lia. by subst.
   Qed.
 
   (* This should be provable using the previous lemma in a shorter way *)
   Lemma alter_join_same_length f (ls : list (list A)) n i :
-    n ≠ 0 →
-    Forall (λ l, length l = n) ls →
+    n ≠ 0 → Forall (λ l, length l = n) ls →
     alter f i (mjoin ls) = mjoin (alter (alter f (i `mod` n)) (i `div` n) ls).
   Proof.
-    intros Hn Hls. revert i.
-    induction Hls as [|l ls ? Hls IH]; simpl; [done |]. intros i.
-    destruct (decide (i < n)) as [Hin|Hin].
+    intros Hn Hls. revert i. induction Hls as [|l ls ? Hls IH]; simpl; [done |].
+    intros i. destruct (decide (i < n)) as [Hin|Hin].
     * rewrite <-(NPeano.Nat.div_unique i n 0 i) by lia.
       rewrite <-(NPeano.Nat.mod_unique i n 0 i) by lia.
       simpl. rewrite alter_app_l; auto with lia.
     * replace i with ((i - n) + 1 * n) by lia.
       rewrite NPeano.Nat.div_add, NPeano.Nat.mod_add by done.
       replace (i - n + 1 * n) with i by lia.
-      rewrite (plus_comm _ 1), alter_app_r_alt, IH by lia.
-      by subst.
+      rewrite (plus_comm _ 1), alter_app_r_alt, IH by lia. by subst.
   Qed.
   Lemma insert_join_same_length (ls : list (list A)) n i x :
-    n ≠ 0 →
-    Forall (λ l, length l = n) ls →
+    n ≠ 0 → Forall (λ l, length l = n) ls →
     <[i:=x]>(mjoin ls) = mjoin (alter <[i `mod` n:=x]> (i `div` n) ls).
   Proof. apply alter_join_same_length. Qed.
 
   Lemma Forall2_join {B} (P : A → B → Prop) ls1 ls2 :
-    Forall2 (Forall2 P) ls1 ls2 →
-    Forall2 P (mjoin ls1) (mjoin ls2).
+    Forall2 (Forall2 P) ls1 ls2 → Forall2 P (mjoin ls1) (mjoin ls2).
   Proof. induction 1; simpl; auto using Forall2_app. Qed.
 End ret_join.
 
-(** ** Properties of the [prefix_of] and [suffix_of] predicates *)
-Section prefix_postfix.
+(** ** Properties of the [permutations] function *)
+Section permutations.
   Context {A : Type}.
+  Implicit Types x y z : A.
+  Implicit Types l : list A.
 
-  Global Instance: PreOrder (@prefix_of A).
-  Proof.
-    split.
-    * intros ?. eexists []. by rewrite (right_id [] (++)).
-    * intros ??? [k1 ?] [k2 ?].
-      exists (k1 ++ k2). subst. by rewrite (associative (++)).
-  Qed.
-
-  Lemma prefix_of_nil (l : list A) : prefix_of [] l.
-  Proof. by exists l. Qed.
-  Lemma prefix_of_nil_not x (l : list A) : ¬prefix_of (x :: l) [].
-  Proof. by intros [k E]. Qed.
-  Lemma prefix_of_cons x (l1 l2 : list A) :
-    prefix_of l1 l2 → prefix_of (x :: l1) (x :: l2).
-  Proof. intros [k E]. exists k. by subst. Qed.
-  Lemma prefix_of_cons_alt x y (l1 l2 : list A) :
-    x = y → prefix_of l1 l2 → prefix_of (x :: l1) (y :: l2).
-  Proof. intro. subst. apply prefix_of_cons. Qed.
-  Lemma prefix_of_cons_inv_1 x y (l1 l2 : list A) :
-    prefix_of (x :: l1) (y :: l2) → x = y.
-  Proof. intros [k E]. by injection E. Qed.
-  Lemma prefix_of_cons_inv_2 x y (l1 l2 : list A) :
-    prefix_of (x :: l1) (y :: l2) → prefix_of l1 l2.
-  Proof. intros [k E]. exists k. by injection E. Qed.
-
-  Lemma prefix_of_app k (l1 l2 : list A) :
-    prefix_of l1 l2 → prefix_of (k ++ l1) (k ++ l2).
-  Proof. intros [k' ?]. subst. exists k'. by rewrite (associative (++)). Qed.
-  Lemma prefix_of_app_alt k1 k2 (l1 l2 : list A) :
-    k1 = k2 → prefix_of l1 l2 → prefix_of (k1 ++ l1) (k2 ++ l2).
-  Proof. intro. subst. apply prefix_of_app. Qed.
-  Lemma prefix_of_app_l (l1 l2 l3 : list A) :
-    prefix_of (l1 ++ l3) l2 → prefix_of l1 l2.
-  Proof.
-    intros [k ?]. red. exists (l3 ++ k). subst.
-    by rewrite <-(associative (++)).
-  Qed.
-  Lemma prefix_of_app_r (l1 l2 l3 : list A) :
-    prefix_of l1 l2 → prefix_of l1 (l2 ++ l3).
-  Proof.
-    intros [k ?]. exists (k ++ l3). subst.
-    by rewrite (associative (++)).
-  Qed.
-
-  Lemma prefix_of_length (l1 l2 : list A) :
-    prefix_of l1 l2 → length l1 ≤ length l2.
-  Proof. intros [??]. subst. rewrite app_length. lia. Qed.
-  Lemma prefix_of_snoc_not (l : list A) x : ¬prefix_of (l ++ [x]) l.
-  Proof. intros [??]. discriminate_list_equality. Qed.
-
-  Global Instance: PreOrder (@suffix_of A).
+  Lemma interleave_cons x l : x :: l ∈ interleave x l.
+  Proof. destruct l; simpl; rewrite elem_of_cons; auto. Qed.
+  Lemma interleave_Permutation x l l' : l' ∈ interleave x l → l' ≡ₚ x :: l.
   Proof.
-    split.
-    * intros ?. by eexists [].
-    * intros ??? [k1 ?] [k2 ?].
-      exists (k2 ++ k1). subst. by rewrite (associative (++)).
+    revert l'. induction l as [|y l IH]; intros l'; simpl.
+    * rewrite elem_of_list_singleton. intros. by subst.
+    * rewrite elem_of_cons, elem_of_list_fmap. intros [?|[? [? H]]]; subst.
+      + by constructor.
+      + rewrite (IH _ H). constructor.
   Qed.
 
-  Global Instance prefix_of_dec `{∀ x y : A, Decision (x = y)} :
-      ∀ l1 l2 : list A, Decision (prefix_of l1 l2) :=
-    fix go l1 l2 :=
-    match l1, l2 return { prefix_of l1 l2 } + { ¬prefix_of l1 l2 } with
-    | [], _ => left (prefix_of_nil _)
-    | _, [] => right (prefix_of_nil_not _ _)
-    | x :: l1, y :: l2 =>
-      match decide_rel (=) x y with
-      | left Exy =>
-        match go l1 l2 with
-        | left Hl1l2 => left (prefix_of_cons_alt _ _ _ _ Exy Hl1l2)
-        | right Hl1l2 => right (Hl1l2 ∘ prefix_of_cons_inv_2 _ _ _ _)
-        end
-      | right Exy => right (Exy ∘ prefix_of_cons_inv_1 _ _ _ _)
-      end
-    end.
+  Lemma permutations_refl l : l ∈ permutations l.
+  Proof.
+    induction l; simpl.
+    * by apply elem_of_list_singleton.
+    * apply elem_of_list_bind. eauto using interleave_cons.
+  Qed.
+  Lemma permutations_skip x l l' :
+    l ∈ permutations l' → x :: l ∈ permutations (x :: l').
+  Proof.
+    intros Hl. simpl. apply elem_of_list_bind. eauto using interleave_cons.
+  Qed.
+  Lemma permutations_swap x y l : y :: x :: l ∈ permutations (x :: y :: l).
+  Proof.
+    simpl. apply elem_of_list_bind. exists (y :: l). split; simpl.
+    * destruct l; simpl; rewrite !elem_of_cons; auto.
+    * apply elem_of_list_bind. simpl.
+      eauto using interleave_cons, permutations_refl.
+  Qed.
+  Lemma permutations_nil l : l ∈ permutations [] ↔ l = [].
+  Proof. simpl. by rewrite elem_of_list_singleton. Qed.
 
-  Section prefix_ops.
-    Context `{∀ x y : A, Decision (x = y)}.
-
-    Lemma max_prefix_of_fst (l1 l2 : list A) :
-      l1 = snd (max_prefix_of l1 l2) ++ fst (fst (max_prefix_of l1 l2)).
-    Proof.
-      revert l2. induction l1; intros [|??]; simpl;
-        repeat case_decide; simpl; f_equal; auto.
-    Qed.
-    Lemma max_prefix_of_fst_alt (l1 l2 : list A) k1 k2 k3 :
-      max_prefix_of l1 l2 = (k1,k2,k3) → l1 = k3 ++ k1.
-    Proof.
-      intro. pose proof (max_prefix_of_fst l1 l2).
-      by destruct (max_prefix_of l1 l2) as [[]?]; simplify_equality.
-    Qed.
-    Lemma max_prefix_of_fst_prefix (l1 l2 : list A) :
-      prefix_of (snd (max_prefix_of l1 l2)) l1.
-    Proof. eexists. apply max_prefix_of_fst. Qed.
-    Lemma max_prefix_of_fst_prefix_alt (l1 l2 : list A) k1 k2 k3 :
-      max_prefix_of l1 l2 = (k1,k2,k3) → prefix_of k3 l1.
-    Proof. eexists. eauto using max_prefix_of_fst_alt. Qed.
-
-    Lemma max_prefix_of_snd (l1 l2 : list A) :
-      l2 = snd (max_prefix_of l1 l2) ++ snd (fst (max_prefix_of l1 l2)).
-    Proof.
-      revert l2. induction l1; intros [|??]; simpl;
-        repeat case_decide; simpl; f_equal; auto.
-    Qed.
-    Lemma max_prefix_of_snd_alt (l1 l2 : list A) k1 k2 k3 :
-      max_prefix_of l1 l2 = (k1,k2,k3) → l2 = k3 ++ k2.
-    Proof.
-      intro. pose proof (max_prefix_of_snd l1 l2).
-      by destruct (max_prefix_of l1 l2) as [[]?]; simplify_equality.
-    Qed.
-    Lemma max_prefix_of_snd_prefix (l1 l2 : list A) :
-      prefix_of (snd (max_prefix_of l1 l2)) l2.
-    Proof. eexists. apply max_prefix_of_snd. Qed.
-    Lemma max_prefix_of_snd_prefix_alt (l1 l2 : list A) k1 k2 k3 :
-      max_prefix_of l1 l2 = (k1,k2,k3) → prefix_of k3 l2.
-    Proof. eexists. eauto using max_prefix_of_snd_alt. Qed.
-
-    Lemma max_prefix_of_max (l1 l2 : list A) k :
-      prefix_of k l1 →
-      prefix_of k l2 →
-      prefix_of k (snd (max_prefix_of l1 l2)).
-    Proof.
-      intros [l1' ?] [l2' ?]. subst.
-      by induction k; simpl; repeat case_decide; simpl;
-        auto using prefix_of_nil, prefix_of_cons.
-    Qed.
-    Lemma max_prefix_of_max_alt (l1 l2 : list A) k1 k2 k3 k :
-      max_prefix_of l1 l2 = (k1,k2,k3) →
-      prefix_of k l1 →
-      prefix_of k l2 →
-      prefix_of k k3.
-    Proof.
-      intro. pose proof (max_prefix_of_max l1 l2 k).
-      by destruct (max_prefix_of l1 l2) as [[]?]; simplify_equality.
-    Qed.
-
-    Lemma max_prefix_of_max_snoc (l1 l2 : list A) k1 k2 k3 x1 x2 :
-      max_prefix_of l1 l2 = (x1 :: k1, x2 :: k2, k3) →
-      x1 ≠ x2.
-    Proof.
-      intros Hl ?. subst. destruct (prefix_of_snoc_not k3 x2).
-      eapply max_prefix_of_max_alt; eauto.
-      * rewrite (max_prefix_of_fst_alt _ _ _ _ _ Hl).
-        apply prefix_of_app, prefix_of_cons, prefix_of_nil.
-      * rewrite (max_prefix_of_snd_alt _ _ _ _ _ Hl).
-        apply prefix_of_app, prefix_of_cons, prefix_of_nil.
-    Qed.
-  End prefix_ops.
-
-  Lemma prefix_suffix_reverse (l1 l2 : list A) :
-    prefix_of l1 l2 ↔ suffix_of (reverse l1) (reverse l2).
-  Proof.
-    split; intros [k E]; exists (reverse k).
-    * by rewrite E, reverse_app.
-    * by rewrite <-(reverse_involutive l2), E, reverse_app, reverse_involutive.
-  Qed.
-  Lemma suffix_prefix_reverse (l1 l2 : list A) :
-    suffix_of l1 l2 ↔ prefix_of (reverse l1) (reverse l2).
-  Proof. by rewrite prefix_suffix_reverse, !reverse_involutive. Qed.
-
-  Lemma suffix_of_nil (l : list A) : suffix_of [] l.
-  Proof. exists l. by rewrite (right_id [] (++)). Qed.
-  Lemma suffix_of_nil_inv (l : list A) : suffix_of l [] → l = [].
-  Proof. by intros [[|?] ?]; simplify_list_equality. Qed.
-  Lemma suffix_of_cons_nil_inv x (l : list A) : ¬suffix_of (x :: l) [].
-  Proof. by intros [[] ?]. Qed.
-  Lemma suffix_of_snoc (l1 l2 : list A) x :
-    suffix_of l1 l2 → suffix_of (l1 ++ [x]) (l2 ++ [x]).
-  Proof. intros [k E]. exists k. subst. by rewrite (associative (++)). Qed.
-  Lemma suffix_of_snoc_alt x y (l1 l2 : list A) :
-    x = y → suffix_of l1 l2 → suffix_of (l1 ++ [x]) (l2 ++ [y]).
-  Proof. intro. subst. apply suffix_of_snoc. Qed.
-
-  Lemma suffix_of_app (l1 l2 k : list A) :
-    suffix_of l1 l2 → suffix_of (l1 ++ k) (l2 ++ k).
-  Proof. intros [k' E]. exists k'. subst. by rewrite (associative (++)). Qed.
-  Lemma suffix_of_app_alt (l1 l2 k1 k2 : list A) :
-    k1 = k2 → suffix_of l1 l2 → suffix_of (l1 ++ k1) (l2 ++ k2).
-  Proof. intro. subst. apply suffix_of_app. Qed.
-
-  Lemma suffix_of_snoc_inv_1 x y (l1 l2 : list A) :
-    suffix_of (l1 ++ [x]) (l2 ++ [y]) → x = y.
-  Proof.
-    rewrite suffix_prefix_reverse, !reverse_snoc.
-    by apply prefix_of_cons_inv_1.
-  Qed.
-  Lemma suffix_of_snoc_inv_2 x y (l1 l2 : list A) :
-    suffix_of (l1 ++ [x]) (l2 ++ [y]) → suffix_of l1 l2.
-  Proof.
-    rewrite !suffix_prefix_reverse, !reverse_snoc.
-    by apply prefix_of_cons_inv_2.
-  Qed.
-
-  Lemma suffix_of_cons_l (l1 l2 : list A) x :
-    suffix_of (x :: l1) l2 → suffix_of l1 l2.
-  Proof.
-    intros [k ?]. exists (k ++ [x]). subst.
-    by rewrite <-(associative (++)).
-  Qed.
-  Lemma suffix_of_app_l (l1 l2 l3 : list A) :
-    suffix_of (l3 ++ l1) l2 → suffix_of l1 l2.
-  Proof.
-    intros [k ?]. exists (k ++ l3). subst.
-    by rewrite <-(associative (++)).
-  Qed.
-  Lemma suffix_of_cons_r (l1 l2 : list A) x :
-    suffix_of l1 l2 → suffix_of l1 (x :: l2).
-  Proof. intros [k ?]. exists (x :: k). by subst. Qed.
-  Lemma suffix_of_app_r (l1 l2 l3 : list A) :
-    suffix_of l1 l2 → suffix_of l1 (l3 ++ l2).
-  Proof.
-    intros [k ?]. exists (l3 ++ k). subst.
-    by rewrite (associative (++)).
-  Qed.
-
-  Lemma suffix_of_cons_inv (l1 l2 : list A) x y :
-    suffix_of (x :: l1) (y :: l2) →
-      x :: l1 = y :: l2 ∨ suffix_of (x :: l1) l2.
-  Proof.
-    intros [[|? k] E].
-    * by left.
-    * right. simplify_equality. by apply suffix_of_app_r.
-  Qed.
-
-  Lemma suffix_of_length (l1 l2 : list A) :
-    suffix_of l1 l2 → length l1 ≤ length l2.
-  Proof. intros [??]. subst. rewrite app_length. lia. Qed.
-  Lemma suffix_of_cons_not x (l : list A) : ¬suffix_of (x :: l) l.
-  Proof. intros [??]. discriminate_list_equality. Qed.
-
-  Global Instance suffix_of_dec `{∀ x y : A, Decision (x = y)}
-    (l1 l2 : list A) : Decision (suffix_of l1 l2).
-  Proof.
-    refine (cast_if (decide_rel prefix_of (reverse l1) (reverse l2)));
-     abstract (by rewrite suffix_prefix_reverse).
-  Defined.
+  Lemma interleave_interleave_toggle x1 x2 l1 l2 l3 :
+    l1 ∈ interleave x1 l2 → l2 ∈ interleave x2 l3 → ∃ l4,
+      l1 ∈ interleave x2 l4 ∧ l4 ∈ interleave x1 l3.
+  Proof.
+    revert l1 l2. induction l3 as [|y l3 IH]; intros l1 l2; simpl.
+    { intros Hl1 Hl2.
+      rewrite elem_of_list_singleton in Hl2. subst. simpl in Hl1.
+      rewrite elem_of_cons, elem_of_list_singleton in Hl1. exists [x1]. simpl.
+      rewrite elem_of_cons, !elem_of_list_singleton. tauto. }
+    rewrite elem_of_cons, elem_of_list_fmap.
+    intros Hl1 [? | [l2' [??]]]; subst; simpl in *.
+    * rewrite !elem_of_cons, elem_of_list_fmap in Hl1.
+      destruct Hl1 as [? | [? | [l4 [??]]]]; subst.
+      + exists (x1 :: y :: l3). simpl. rewrite !elem_of_cons. tauto.
+      + exists (x1 :: y :: l3). simpl. rewrite !elem_of_cons. tauto.
+      + exists l4. simpl. rewrite elem_of_cons. auto using interleave_cons.
+    * rewrite elem_of_cons, elem_of_list_fmap in Hl1.
+      destruct Hl1 as [? | [l1' [??]]]; subst.
+      + exists (x1 :: y :: l3). simpl.
+        rewrite !elem_of_cons, !elem_of_list_fmap.
+        split; [| by auto]. right. right. exists (y :: l2').
+        rewrite elem_of_list_fmap. naive_solver.
+      + destruct (IH l1' l2') as [l4 [??]]; auto. exists (y :: l4). simpl.
+        rewrite !elem_of_cons, !elem_of_list_fmap. naive_solver.
+  Qed.
+  Lemma permutations_interleave_toggle x l1 l2 l3 :
+    l1 ∈ permutations l2 → l2 ∈ interleave x l3 → ∃ l4,
+      l1 ∈ interleave x l4 ∧ l4 ∈ permutations l3.
+  Proof.
+    revert l1 l2. induction l3 as [|y l3 IH]; intros l1 l2; simpl.
+    { intros Hl1 Hl2. eexists []. simpl.
+      split; [| by rewrite elem_of_list_singleton].
+      rewrite elem_of_list_singleton in Hl2. by rewrite Hl2 in Hl1. }
+    rewrite elem_of_cons, elem_of_list_fmap.
+    intros Hl1 [? | [l2' [? Hl2']]]; subst; simpl in *.
+    * rewrite elem_of_list_bind in Hl1.
+      destruct Hl1 as [l1' [??]]. by exists l1'.
+    * rewrite elem_of_list_bind in Hl1. setoid_rewrite elem_of_list_bind.
+      destruct Hl1 as [l1' [??]]. destruct (IH l1' l2') as (l1''&?&?); auto.
+      destruct (interleave_interleave_toggle y x l1 l1' l1'') as (?&?&?); eauto.
+  Qed.
+  Lemma permutations_trans l1 l2 l3 :
+    l1 ∈ permutations l2 → l2 ∈ permutations l3 → l1 ∈ permutations l3.
+  Proof.
+    revert l1 l2. induction l3 as [|x l3 IH]; intros l1 l2; simpl.
+    * intros Hl1 Hl2. rewrite elem_of_list_singleton in Hl2.
+      by rewrite Hl2 in Hl1.
+    * rewrite !elem_of_list_bind. intros Hl1 [l2' [Hl2 Hl2']].
+      destruct (permutations_interleave_toggle x l1 l2 l2') as [? [??]]; eauto.
+  Qed.
 
-  Section max_suffix_of.
-    Context `{∀ x y : A, Decision (x = y)}.
-
-    Lemma max_suffix_of_fst (l1 l2 : list A) :
-      l1 = fst (fst (max_suffix_of l1 l2)) ++ snd (max_suffix_of l1 l2).
-    Proof.
-      rewrite <-(reverse_involutive l1) at 1.
-      rewrite (max_prefix_of_fst (reverse l1) (reverse l2)).
-      unfold max_suffix_of.
-      destruct (max_prefix_of (reverse l1) (reverse l2)) as ((?&?)&?); simpl.
-      by rewrite reverse_app.
-    Qed.
-    Lemma max_suffix_of_fst_alt (l1 l2 : list A) k1 k2 k3 :
-      max_suffix_of l1 l2 = (k1,k2,k3) → l1 = k1 ++ k3.
-    Proof.
-      intro. pose proof (max_suffix_of_fst l1 l2).
-      by destruct (max_suffix_of l1 l2) as [[]?]; simplify_equality.
-    Qed.
-    Lemma max_suffix_of_fst_suffix (l1 l2 : list A) :
-      suffix_of (snd (max_suffix_of l1 l2)) l1.
-    Proof. eexists. apply max_suffix_of_fst. Qed.
-    Lemma max_suffix_of_fst_suffix_alt (l1 l2 : list A) k1 k2 k3 :
-      max_suffix_of l1 l2 = (k1,k2,k3) → suffix_of k3 l1.
-    Proof. eexists. eauto using max_suffix_of_fst_alt. Qed.
-
-    Lemma max_suffix_of_snd (l1 l2 : list A) :
-      l2 = snd (fst (max_suffix_of l1 l2)) ++ snd (max_suffix_of l1 l2).
-    Proof.
-      rewrite <-(reverse_involutive l2) at 1.
-      rewrite (max_prefix_of_snd (reverse l1) (reverse l2)).
-      unfold max_suffix_of.
-      destruct (max_prefix_of (reverse l1) (reverse l2)) as ((?&?)&?); simpl.
-      by rewrite reverse_app.
-    Qed.
-    Lemma max_suffix_of_snd_alt (l1 l2 : list A) k1 k2 k3 :
-      max_suffix_of l1 l2 = (k1,k2,k3) → l2 = k2 ++ k3.
-    Proof.
-      intro. pose proof (max_suffix_of_snd l1 l2).
-      by destruct (max_suffix_of l1 l2) as [[]?]; simplify_equality.
-    Qed.
-    Lemma max_suffix_of_snd_suffix (l1 l2 : list A) :
-      suffix_of (snd (max_suffix_of l1 l2)) l2.
-    Proof. eexists. apply max_suffix_of_snd. Qed.
-    Lemma max_suffix_of_snd_suffix_alt (l1 l2 : list A) k1 k2 k3 :
-      max_suffix_of l1 l2 = (k1,k2,k3) → suffix_of k3 l2.
-    Proof. eexists. eauto using max_suffix_of_snd_alt. Qed.
-
-    Lemma max_suffix_of_max (l1 l2 : list A) k :
-      suffix_of k l1 →
-      suffix_of k l2 →
-      suffix_of k (snd (max_suffix_of l1 l2)).
-    Proof.
-      generalize (max_prefix_of_max (reverse l1) (reverse l2)).
-      rewrite !suffix_prefix_reverse. unfold max_suffix_of.
-      destruct (max_prefix_of (reverse l1) (reverse l2)) as ((?&?)&?); simpl.
-      rewrite reverse_involutive. auto.
-    Qed.
-    Lemma max_suffix_of_max_alt (l1 l2 : list A) k1 k2 k3 k :
-      max_suffix_of l1 l2 = (k1,k2,k3) →
-      suffix_of k l1 →
-      suffix_of k l2 →
-      suffix_of k k3.
-    Proof.
-      intro. pose proof (max_suffix_of_max l1 l2 k).
-      by destruct (max_suffix_of l1 l2) as [[]?]; simplify_equality.
-    Qed.
-
-    Lemma max_suffix_of_max_snoc (l1 l2 : list A) k1 k2 k3 x1 x2 :
-      max_suffix_of l1 l2 = (k1 ++ [x1], k2 ++ [x2], k3) →
-      x1 ≠ x2.
-    Proof.
-      intros Hl ?. subst. destruct (suffix_of_cons_not x2 k3).
-      eapply max_suffix_of_max_alt; eauto.
-      * rewrite (max_suffix_of_fst_alt _ _ _ _ _ Hl).
-        by apply (suffix_of_app [x2]), suffix_of_app_r.
-      * rewrite (max_suffix_of_snd_alt _ _ _ _ _ Hl).
-        by apply (suffix_of_app [x2]), suffix_of_app_r.
-    Qed.
-  End max_suffix_of.
-End prefix_postfix.
+  Lemma permutations_Permutation l l' : l' ∈ permutations l ↔ l ≡ₚ l'.
+  Proof.
+    split.
+    * revert l'. induction l; simpl; intros l''.
+      + rewrite elem_of_list_singleton. intros. subst. constructor.
+      + rewrite elem_of_list_bind. intros [l' [Hl'' ?]].
+        rewrite (interleave_Permutation _ _ _ Hl''). constructor; auto.
+    * induction 1; eauto using permutations_refl,
+        permutations_skip, permutations_swap, permutations_trans.
+  Qed.
+End permutations.
 
 (** ** Properties of the folding functions *)
-Notation foldr_app := fold_right_app.
+Definition foldr_app := @fold_right_app.
+
 Lemma foldl_app {A B} (f : A → B → A) (l k : list B) (a : A) :
   foldl f a (l ++ k) = foldl f (foldl f a l) k.
 Proof. revert a. induction l; simpl; auto. Qed.
 
-Lemma foldr_permutation {A B} (R : relation B)
-   `{!Equivalence R}
-   (f : A → B → B) (b : B)
-   `{!Proper ((=) ==> R ==> R) f}
-   (Hf : ∀ a1 a2 b, R (f a1 (f a2 b)) (f a2 (f a1 b))) :
-  Proper (Permutation ==> R) (foldr f b).
+Lemma foldr_permutation {A B} (R : relation B) `{!Equivalence R}
+    (f : A → B → B) (b : B) `{!Proper ((=) ==> R ==> R) f}
+    (Hf : ∀ a1 a2 b, R (f a1 (f a2 b)) (f a2 (f a1 b))) :
+  Proper ((≡ₚ) ==> R) (foldr f b).
 Proof.
   induction 1; simpl.
   * done.
@@ -2208,12 +2438,9 @@ Proof.
   * etransitivity; eauto.
 Qed.
 
-Lemma ifoldr_app {A B} (f : nat → B → A → A) (a : nat → A)
-    (l1 l2 : list B) n :
+Lemma ifoldr_app {A B} (f : nat → B → A → A) (a : nat → A) (l1 l2 : list B) n :
   ifoldr f a n (l1 ++ l2) = ifoldr f (λ n, ifoldr f a n l2) n l1.
-Proof.
-  revert n a. induction l1 as [| b l1 IH ]; intros; simpl; f_equal; auto.
-Qed.
+Proof. revert n a. induction l1; intros; simpl; f_equal; auto. Qed.
 
 (** ** Properties of the [zip_with] and [zip] functions *)
 Section zip_with.
@@ -2224,129 +2451,87 @@ Section zip_with.
   Proof. destruct l1, l2; simpl; auto with congruence. Qed.
   Lemma zip_with_cons_inv y l1 l2 k :
     zip_with f l1 l2 = y :: k →
-    ∃ x1 x2 l1' l2',
-      y = f x1 x2 ∧
-      k = zip_with f l1' l2' ∧
-      l1 = x1 :: l1' ∧
-      l2 = x2 :: l2'.
-  Proof.
-    intros. destruct l1, l2; simpl; simplify_equality; repeat eexists.
-  Qed.
+    ∃ x1 x2 l1' l2', y = f x1 x2 ∧ k = zip_with f l1' l2' ∧
+      l1 = x1 :: l1' ∧ l2 = x2 :: l2'.
+  Proof. intros. destruct l1, l2; simpl; simplify_equality; repeat eexists. Qed.
   Lemma zip_with_app_inv l1 l2 k' k'' :
     zip_with f l1 l2 = k' ++ k'' →
-    ∃ l1' l1'' l2' l2'',
-      k' = zip_with f l1' l2' ∧
-      k'' = zip_with f l1'' l2'' ∧
-      l1 = l1' ++ l1'' ∧
-      l2 = l2' ++ l2''.
+    ∃ l1' l1'' l2' l2'', k' = zip_with f l1' l2' ∧ k'' = zip_with f l1'' l2'' ∧
+      l1 = l1' ++ l1'' ∧ l2 = l2' ++ l2''.
   Proof.
     revert l1 l2. induction k' as [|y k' IH]; simpl.
     * intros l1 l2 ?. by eexists [], l1, [], l2.
     * intros [|x1 l1] [|x2 l2] ?; simpl; simplify_equality.
-      destruct (IH l1 l2) as (l1' & l1'' & l2' & l2'' &?&?&?&?);
-        subst; [done |].
+      destruct (IH l1 l2) as (l1'&l1''&l2'&l2''&?&?&?&?); subst; [done |].
       by exists (x1 :: l1') l1'' (x2 :: l2') l2''.
   Qed.
 
   Lemma zip_with_inj l1 l2 k1 k2 :
     (∀ x1 x2 y1 y2, f x1 x2 = f y1 y2 → x1 = y1 ∧ x2 = y2) →
-    same_length l1 l2 →
-    same_length k1 k2 →
-    zip_with f l1 l2 = zip_with f k1 k2 →
-    l1 = k1 ∧ l2 = k2.
+    l1 `same_length` l2 → k1 `same_length` k2 →
+    zip_with f l1 l2 = zip_with f k1 k2 → l1 = k1 ∧ l2 = k2.
   Proof.
-    intros ? Hl. revert k1 k2.
-    induction Hl; intros ?? [] ?; simpl;
+    intros ? Hl. revert k1 k2. induction Hl; intros ?? [] ?; simpl;
       simplify_equality; f_equal; naive_solver.
   Qed.
 
   Lemma zip_with_length l1 l2 :
-    length l1 ≤ length l2 →
-    length (zip_with f l1 l2) = length l1.
-  Proof.
-    revert l2.
-    induction l1; intros [|??]; simpl; auto with lia.
-  Qed.
+    length l1 ≤ length l2 → length (zip_with f l1 l2) = length l1.
+  Proof. revert l2. induction l1; intros [|??]; simpl; auto with lia. Qed.
 
   Lemma zip_with_fmap_fst_le (g : C → A) l1 l2 :
-    (∀ x y, g (f x y) = x) →
-    length l1 ≤ length l2 →
+    (∀ x y, g (f x y) = x) → length l1 ≤ length l2 →
     g <$> zip_with f l1 l2 = l1.
   Proof.
     revert l2.
     induction l1; intros [|??] ??; simpl in *; f_equal; auto with lia.
   Qed.
   Lemma zip_with_fmap_snd_le (g : C → B) l1 l2 :
-    (∀ x y, g (f x y) = y) →
-    length l2 ≤ length l1 →
+    (∀ x y, g (f x y) = y) → length l2 ≤ length l1 →
     g <$> zip_with f l1 l2 = l2.
   Proof.
     revert l1.
     induction l2; intros [|??] ??; simpl in *; f_equal; auto with lia.
   Qed.
   Lemma zip_with_fmap_fst (g : C → A) l1 l2 :
-    (∀ x y, g (f x y) = x) →
-    same_length l1 l2 →
-    g <$> zip_with f l1 l2 = l1.
+    (∀ x y, g (f x y) = x) → l1 `same_length` l2 → g <$> zip_with f l1 l2 = l1.
   Proof. induction 2; simpl; f_equal; auto. Qed.
   Lemma zip_with_fmap_snd (g : C → B) l1 l2 :
-    (∀ x y, g (f x y) = y) →
-    same_length l1 l2 →
-    g <$> zip_with f l1 l2 = l2.
+    (∀ x y, g (f x y) = y) → l1 `same_length` l2 → g <$> zip_with f l1 l2 = l2.
   Proof. induction 2; simpl; f_equal; auto. Qed.
 
   Lemma Forall_zip_with_fst (P : A → Prop) (Q : C → Prop) l1 l2 :
-    Forall P l1 →
-    Forall (λ y, ∀ x, P x → Q (f x y)) l2 →
+    Forall P l1 → Forall (λ y, ∀ x, P x → Q (f x y)) l2 →
     Forall Q (zip_with f l1 l2).
-  Proof.
-    intros Hl1. revert l2.
-    induction Hl1; destruct 1; simpl in *; auto.
-  Qed.
+  Proof. intros Hl. revert l2. induction Hl; destruct 1; simpl in *; auto. Qed.
   Lemma Forall_zip_with_snd (P : B → Prop) (Q : C → Prop) l1 l2 :
-    Forall (λ x, ∀ y, P y → Q (f x y)) l1 →
-    Forall P l2 →
+    Forall (λ x, ∀ y, P y → Q (f x y)) l1 → Forall P l2 →
     Forall Q (zip_with f l1 l2).
-  Proof.
-    intros Hl1. revert l2.
-    induction Hl1; destruct 1; simpl in *; auto.
-  Qed.
+  Proof. intros Hl. revert l2. induction Hl; destruct 1; simpl in *; auto. Qed.
 End zip_with.
 
 Section zip.
   Context {A B : Type}.
 
   Lemma zip_length (l1 : list A) (l2 : list B) :
-    length l1 ≤ length l2 →
-    length (zip l1 l2) = length l1.
+    length l1 ≤ length l2 → length (zip l1 l2) = length l1.
   Proof. by apply zip_with_length. Qed.
 
   Lemma zip_fmap_fst_le (l1 : list A) (l2 : list B) :
-    length l1 ≤ length l2 →
-    fst <$> zip l1 l2 = l1.
+    length l1 ≤ length l2 → fst <$> zip l1 l2 = l1.
   Proof. by apply zip_with_fmap_fst_le. Qed.
   Lemma zip_fmap_snd (l1 : list A) (l2 : list B) :
-    length l2 ≤ length l1 →
-    snd <$> zip l1 l2 = l2.
+    length l2 ≤ length l1 → snd <$> zip l1 l2 = l2.
   Proof. by apply zip_with_fmap_snd_le. Qed.
 
   Lemma zip_fst (l1 : list A) (l2 : list B) :
-    same_length l1 l2 →
-    fst <$> zip l1 l2 = l1.
+    l1 `same_length` l2 → fst <$> zip l1 l2 = l1.
   Proof. by apply zip_with_fmap_fst. Qed.
   Lemma zip_snd (l1 : list A) (l2 : list B) :
-    same_length l1 l2 → snd <$> zip l1 l2 = l2.
+    l1 `same_length` l2 → snd <$> zip l1 l2 = l2.
   Proof. by apply zip_with_fmap_snd. Qed.
 End zip.
 
-Definition zipped_map {A B} (f : list A → list A → A → B) :
-    list A → list A → list B :=
-  fix go l k :=
-  match k with
-  | [] => []
-  | x :: k => f l k x :: go (x :: l) k
-  end.
-
 Lemma elem_of_zipped_map {A B} (f : list A → list A → A → B) l k x :
   x ∈ zipped_map f l k ↔
     ∃ k' k'' y, k = k' ++ [y] ++ k'' ∧ x = f (reverse k' ++ l) k'' y.
@@ -2356,17 +2541,15 @@ Proof.
     + by eexists [], k, z.
     + destruct (IH (z :: l)) as [k' [k'' [y [??]]]]; [done |]; subst.
       eexists (z :: k'), k'', y. split; [done |].
-      by rewrite reverse_cons, <-(associative (++)).
+      by rewrite reverse_cons, <-(associative_L (++)).
   * intros [k' [k'' [y [??]]]]; subst.
-    revert l. induction k' as [|z k' IH]; intros l.
-    + by left.
-    + right. by rewrite reverse_cons, <-!(associative (++)).
+    revert l. induction k' as [|z k' IH]; intros l; [by left|].
+    right. by rewrite reverse_cons, <-!(associative_L (++)).
 Qed.
 
 Section zipped_list_ind.
   Context {A} (P : list A → list A → Prop).
-  Context (Pnil : ∀ l, P l []).
-  Context (Pcons : ∀ l k x, P (x :: l) k → P l (x :: k)).
+  Context (Pnil : ∀ l, P l []) (Pcons : ∀ l k x, P (x :: l) k → P l (x :: k)).
 
   Fixpoint zipped_list_ind l k : P l k :=
     match k with
@@ -2375,236 +2558,117 @@ Section zipped_list_ind.
     end.
 End zipped_list_ind.
 
-Inductive zipped_Forall {A} (P : list A → list A → A → Prop) :
-    list A → list A → Prop :=
-  | zipped_Forall_nil l : zipped_Forall P l []
-  | zipped_Forall_cons l k x :
-     P l k x →
-     zipped_Forall P (x :: l) k →
-     zipped_Forall P l (x :: k).
-Arguments zipped_Forall_nil {_ _} _.
-Arguments zipped_Forall_cons {_ _} _ _ _ _ _.
-
 Lemma zipped_Forall_app {A} (P : list A → list A → A → Prop) l k k' :
   zipped_Forall P l (k ++ k') → zipped_Forall P (reverse k ++ l) k'.
 Proof.
   revert l. induction k as [|x k IH]; simpl; [done |].
-  inversion_clear 1. rewrite reverse_cons, <-(associative (++)).
-  by apply IH.
+  inversion_clear 1. rewrite reverse_cons, <-(associative_L (++)). by apply IH.
 Qed.
 
-(** ** Permutations *)
-Section permutations.
-  Context {A : Type}.
-
-  Lemma interleave_cons (x : A) (l : list A) :
-    x :: l ∈ interleave x l.
-  Proof. destruct l; simpl; rewrite elem_of_cons; auto. Qed.
-  Lemma interleave_Permutation (x : A) (l l' : list A) :
-    l' ∈ interleave x l → Permutation l' (x :: l).
-  Proof.
-    revert l'. induction l as [|y l IH]; intros l'; simpl.
-    * rewrite elem_of_list_singleton. intros. by subst.
-    * rewrite elem_of_cons, elem_of_list_fmap.
-      intros [?|[? [? H]]]; subst.
-      + by constructor.
-      + rewrite (IH _ H). constructor.
-  Qed.
-
-  Lemma permutations_refl (l : list A) :
-    l ∈ permutations l.
-  Proof.
-    induction l; simpl.
-    * by apply elem_of_list_singleton.
-    * apply elem_of_list_bind. eauto using interleave_cons.
-  Qed.
-  Lemma permutations_skip (x : A) (l l' : list A) :
-    l ∈ permutations l' →
-    x :: l ∈ permutations (x :: l').
-  Proof.
-    intros Hl. simpl. apply elem_of_list_bind.
-    eauto using interleave_cons.
-  Qed.
-  Lemma permutations_swap (x y : A) (l : list A) :
-    y :: x :: l ∈ permutations (x :: y :: l).
-  Proof.
-    simpl. apply elem_of_list_bind.
-    exists (y :: l). split; simpl.
-    * destruct l; simpl; rewrite !elem_of_cons; auto.
-    * apply elem_of_list_bind. simpl.
-      eauto using interleave_cons, permutations_refl.
-  Qed.
-  Lemma permutations_nil (l : list A) :
-    l ∈ permutations [] ↔ l = [].
-  Proof. simpl. by rewrite elem_of_list_singleton. Qed.
-
-  Lemma interleave_interleave_toggle (x1 x2 : A) (l1 l2 l3 : list A) :
-    l1 ∈ interleave x1 l2 →
-    l2 ∈ interleave x2 l3 → ∃ l4,
-      l1 ∈ interleave x2 l4 ∧ l4 ∈ interleave x1 l3.
-  Proof.
-    revert l1 l2. induction l3 as [|y l3 IH]; intros l1 l2; simpl.
-    { intros Hl1 Hl2.
-      rewrite elem_of_list_singleton in Hl2. subst. simpl in Hl1.
-      rewrite elem_of_cons, elem_of_list_singleton in Hl1.
-      exists [x1]. simpl.
-      rewrite elem_of_cons, !elem_of_list_singleton. tauto. }
-    rewrite elem_of_cons, elem_of_list_fmap.
-    intros Hl1 [? | [l2' [??]]]; subst; simpl in *.
-    * rewrite !elem_of_cons, elem_of_list_fmap in Hl1.
-      destruct Hl1 as [? | [? | [l4 [??]]]]; subst.
-      + exists (x1 :: y :: l3). simpl. rewrite !elem_of_cons. tauto.
-      + exists (x1 :: y :: l3). simpl. rewrite !elem_of_cons. tauto.
-      + exists l4. simpl. rewrite elem_of_cons. auto using interleave_cons.
-    * rewrite elem_of_cons, elem_of_list_fmap in Hl1.
-      destruct Hl1 as [? | [l1' [??]]]; subst.
-      + exists (x1 :: y :: l3). simpl.
-        rewrite !elem_of_cons, !elem_of_list_fmap.
-        split; [| by auto]. right. right. exists (y :: l2').
-        rewrite elem_of_list_fmap. naive_solver.
-      + destruct (IH l1' l2') as [l4 [??]]; auto.
-        exists (y :: l4). simpl.
-        rewrite !elem_of_cons, !elem_of_list_fmap. naive_solver.
-  Qed.
-  Lemma permutations_interleave_toggle (x : A) (l1 l2 l3 : list A) :
-    l1 ∈ permutations l2 →
-    l2 ∈ interleave x l3 → ∃ l4,
-      l1 ∈ interleave x l4 ∧ l4 ∈ permutations l3.
-  Proof.
-    revert l1 l2. induction l3 as [|y l3 IH]; intros l1 l2; simpl.
-    { intros Hl1 Hl2. eexists []. simpl.
-      split; [| by rewrite elem_of_list_singleton].
-      rewrite elem_of_list_singleton in Hl2.
-      by rewrite Hl2 in Hl1. }
-    rewrite elem_of_cons, elem_of_list_fmap.
-    intros Hl1 [? | [l2' [? Hl2']]]; subst; simpl in *.
-    * rewrite elem_of_list_bind in Hl1.
-      destruct Hl1 as [l1' [??]]. by exists l1'.
-    * rewrite elem_of_list_bind in Hl1.
-      setoid_rewrite elem_of_list_bind.
-      destruct Hl1 as [l1' [??]].
-      destruct (IH l1' l2') as [l1'' [??]]; auto.
-      destruct (interleave_interleave_toggle y x l1 l1' l1'') as [? [??]]; eauto.
-  Qed.
-  Lemma permutations_trans (l1 l2 l3 : list A) :
-    l1 ∈ permutations l2 →
-    l2 ∈ permutations l3 →
-    l1 ∈ permutations l3.
-  Proof.
-    revert l1 l2. induction l3 as [|x l3 IH]; intros l1 l2; simpl.
-    * intros Hl1 Hl2. rewrite elem_of_list_singleton in Hl2.
-      by rewrite Hl2 in Hl1.
-    * rewrite !elem_of_list_bind. intros Hl1 [l2' [Hl2 Hl2']].
-      destruct (permutations_interleave_toggle x l1 l2 l2') as [? [??]]; eauto.
-  Qed.
-
-  Lemma permutations_Permutation (l l' : list A) :
-    l' ∈ permutations l ↔ Permutation l l'.
-  Proof.
-    split.
-    * revert l'. induction l; simpl; intros l''.
-      + rewrite elem_of_list_singleton.
-        intros. subst. constructor.
-      + rewrite elem_of_list_bind. intros [l' [Hl'' ?]].
-        rewrite (interleave_Permutation _ _ _ Hl'').
-        constructor; auto.
-    * induction 1; eauto using permutations_refl,
-        permutations_skip, permutations_swap, permutations_trans.
-  Qed.
-
-  Global Instance Permutation_dec `{∀ x y : A, Decision (x = y)}
-    (l1 l2 : list A) : Decision (Permutation l1 l2).
-  Proof.
-    refine (cast_if (decide (l2 ∈ permutations l1)));
-      by rewrite <-permutations_Permutation.
-  Defined.
-End permutations.
+(** * Relection over lists *)
+(** We define a simple data structure [rlist] to capture a syntactic
+representation of lists consisting of constants, applications and the nil list.
+Note that we represent [(x ::)] as [rapp (rnode [x])]. For now, we abstract
+over the type of constants, but later we use [nat]s and a list representing
+a corresponding environment. *)
+Inductive rlist (A : Type) :=
+  | rnil : rlist A
+  | rnode : A → rlist A
+  | rapp : rlist A → rlist A → rlist A.
+Arguments rnil {_}.
+Arguments rnode {_} _.
+Arguments rapp {_} _ _.
+
+Module rlist.
+Fixpoint to_list {A} (t : rlist A) : list A :=
+  match t with
+  | rnil => []
+  | rnode l => [l]
+  | rapp t1 t2 => to_list t1 ++ to_list t2
+  end.
 
-(** * Set operations on lists *)
-Section list_set_operations.
-  Context {A} {dec : ∀ x y : A, Decision (x = y)}.
+Notation env A := (list (list A)) (only parsing).
+Definition eval {A} (E : env A) : rlist nat → list A :=
+  fix go t :=
+  match t with
+  | rnil => []
+  | rnode i => from_option [] (E !! i)
+  | rapp t1 t2 => go t1 ++ go t2
+  end.
 
-  Fixpoint list_difference (l k : list A) : list A :=
-    match l with
-    | [] => []
-    | x :: l =>
-      if decide_rel (∈) x k
-      then list_difference l k
-      else x :: list_difference l k
-    end.
-  Lemma elem_of_list_difference l k x :
-    x ∈ list_difference l k ↔ x ∈ l ∧ x ∉ k.
-  Proof.
-    split; induction l; simpl; try case_decide;
-      rewrite ?elem_of_nil, ?elem_of_cons; intuition congruence.
-  Qed.
-  Lemma list_difference_nodup l k :
-    NoDup l → NoDup (list_difference l k).
-  Proof.
-    induction 1; simpl; try case_decide.
-    * constructor.
+(** A simple quoting mechanism using type classes. [QuoteLookup E1 E2 x i]
+means: starting in environment [E1], look up the index [i] corresponding to the
+constant [x]. In case [x] has a corresponding index [i] in [E1], the original
+environment is given back as [E2]. Otherwise, the environment [E2] is extended
+with a binding [i] for [x]. *)
+Section quote_lookup.
+  Context {A : Type}.
+  Class QuoteLookup (E1 E2 : list A) (x : A) (i : nat) := {}.
+  Global Instance quote_lookup_here E x : QuoteLookup (x :: E) (x :: E) x 0.
+  Global Instance quote_lookup_end x : QuoteLookup [] [x] x 0.
+  Global Instance quote_lookup_further E1 E2 x i y :
+    QuoteLookup E1 E2 x i → QuoteLookup (y :: E1) (y :: E2) x (S i) | 1000.
+End quote_lookup.
+
+Section quote.
+  Context {A : Type}.
+  Class Quote (E1 E2 : env A) (l : list A) (t : rlist nat) := {}.
+  Global Instance quote_nil: Quote E1 E1 [] rnil.
+  Global Instance quote_node E1 E2 l i:
+    QuoteLookup E1 E2 l i → Quote E1 E2 l (rnode i) | 1000.
+  Global Instance quote_cons E1 E2 E3 x l i t :
+    QuoteLookup E1 E2 [x] i →
+    Quote E2 E3 l t → Quote E1 E3 (x :: l) (rapp (rnode i) t).
+  Global Instance quote_app E1 E2 E3 l1 l2 t1 t2 :
+    Quote E1 E2 l1 t1 → Quote E2 E3 l2 t2 → Quote E1 E3 (l1 ++ l2) (rapp t1 t2).
+End quote.
+
+Section eval.
+  Context {A} (E : env A).
+
+  Lemma eval_alt t : eval E t = to_list t ≫= from_option [] ∘ (E !!).
+  Proof.
+    induction t; simpl.
     * done.
-    * constructor. rewrite elem_of_list_difference; intuition. done.
-  Qed.
+    * by rewrite (right_id_L [] (++)).
+    * rewrite bind_app. by f_equal.
+  Qed.
+  Lemma eval_eq t1 t2 : to_list t1 = to_list t2 → eval E t1 = eval E t2.
+  Proof. intros Ht. by rewrite !eval_alt, Ht. Qed.
+  Lemma eval_Permutation t1 t2 :
+    to_list t1 ≡ₚ to_list t2 → eval E t1 ≡ₚ eval E t2.
+  Proof. intros Ht. by rewrite !eval_alt, Ht. Qed.
+  Lemma eval_contains t1 t2 :
+    to_list t1 `contains` to_list t2 → eval E t1 `contains` eval E t2.
+  Proof. intros Ht. by rewrite !eval_alt, Ht. Qed.
+End eval.
+End rlist.
 
-  Fixpoint list_intersection (l k : list A) : list A :=
-    match l with
-    | [] => []
-    | x :: l =>
-      if decide_rel (∈) x k
-      then x :: list_intersection l k
-      else list_intersection l k
-    end.
-  Lemma elem_of_list_intersection l k x :
-    x ∈ list_intersection l k ↔ x ∈ l ∧ x ∈ k.
-  Proof.
-    split; induction l; simpl; repeat case_decide;
-      rewrite ?elem_of_nil, ?elem_of_cons; intuition congruence.
-  Qed.
-  Lemma list_intersection_nodup l k :
-    NoDup l → NoDup (list_intersection l k).
-  Proof.
-    induction 1; simpl; try case_decide.
-    * constructor.
-    * constructor. rewrite elem_of_list_intersection; intuition. done.
-    * done.
-  Qed.
+(** * Tactics *)
+Ltac quote_Permutation :=
+  match goal with
+  | |- ?l1 ≡ₚ ?l2 =>
+    match type of (_ : rlist.Quote [] _ l1 _) with rlist.Quote _ ?E2 _ ?t1 =>
+    match type of (_ : rlist.Quote E2 _ l2 _) with rlist.Quote _ ?E3 _ ?t2 =>
+      change (rlist.eval E3 t1 ≡ₚ rlist.eval E3 t2)
+    end end
+  end.
+Ltac solve_Permutation :=
+  quote_Permutation; apply rlist.eval_Permutation;
+  apply (bool_decide_unpack _); by vm_compute.
 
-  Definition list_intersection_with (f : A → A → option A) :
-      list A → list A → list A :=
-    fix go l k :=
-    match l with
-    | [] => []
-    | x :: l => foldr (λ y,
-       match f x y with None => id | Some z => (z ::) end) (go l k) k
-    end.
-  Lemma elem_of_list_intersection_with f l k x :
-    x ∈ list_intersection_with f l k ↔ ∃ x1 x2,
-      x1 ∈ l ∧ x2 ∈ k ∧ f x1 x2 = Some x.
-  Proof.
-    split.
-    * induction l as [|x1 l IH]; simpl.
-      + by rewrite elem_of_nil.
-      + intros Hx. setoid_rewrite elem_of_cons.
-        cut ((∃ x2, x2 ∈ k ∧ f x1 x2 = Some x)
-          ∨ x ∈ list_intersection_with f l k).
-        { naive_solver. }
-        clear IH. revert Hx. generalize (list_intersection_with f l k).
-        induction k; simpl; [by auto|].
-        case_match; setoid_rewrite elem_of_cons; naive_solver.
-    * intros (x1 & x2 & Hx1 & Hx2 & Hx).
-      induction Hx1 as [x1 | x1 ? l ? IH]; simpl.
-      + generalize (list_intersection_with f l k).
-        induction Hx2; simpl; [by rewrite Hx; left |].
-        case_match; simpl; try setoid_rewrite elem_of_cons; auto.
-      + generalize (IH Hx). clear Hx IH Hx2.
-        generalize (list_intersection_with f l k).
-        induction k; simpl; intros; [done |].
-        case_match; simpl; rewrite ?elem_of_cons; auto.
-  Qed.
-End list_set_operations.
+Ltac quote_contains :=
+  match goal with
+  | |- ?l1 `contains` ?l2 =>
+    match type of (_ : rlist.Quote [] _ l1 _) with rlist.Quote _ ?E2 _ ?t1 =>
+    match type of (_ : rlist.Quote E2 _ l2 _) with rlist.Quote _ ?E3 _ ?t2 =>
+      change (rlist.eval E3 t1 `contains` rlist.eval E3 t2)
+    end end
+  end.
+Ltac solve_contains :=
+  quote_contains; apply rlist.eval_contains;
+  apply (bool_decide_unpack _); by vm_compute.
 
-(** * Tactics *)
 Ltac decompose_elem_of_list := repeat
   match goal with
   | H : ?x ∈ [] |- _ => by destruct (not_elem_of_nil x)
@@ -2618,25 +2682,22 @@ Ltac simplify_list_fmap_equality := repeat
   | H : _ <$> _ = [] |- _ => apply fmap_nil_inv in H
   | H : [] = _ <$> _ |- _ => symmetry in H; apply fmap_nil_inv in H
   | H : _ <$> _ = _ :: _ |- _ =>
-     apply fmap_cons_inv in H; destruct H as (?&?&?&?&?)
+    apply fmap_cons_inv in H; destruct H as (?&?&?&?&?)
   | H : _ :: _ = _ <$> _ |- _ => symmetry in H
   | H : _ <$> _ = _ ++ _ |- _ =>
-     apply fmap_app_inv in H; destruct H as (?&?&?&?&?)
+    apply fmap_app_inv in H; destruct H as (?&?&?&?&?)
   | H : _ ++ _ = _ <$> _ |- _ => symmetry in H
   end.
-
 Ltac simplify_zip_equality := repeat
   match goal with
   | _ => progress simplify_equality
-  | H : zip_with _ _ _ = [] |- _ =>
-     apply zip_with_nil_inv in H; destruct H
-  | H : [] = zip_with _ _ _ |- _ =>
-     symmetry in H
+  | H : zip_with _ _ _ = [] |- _ => apply zip_with_nil_inv in H; destruct H
+  | H : [] = zip_with _ _ _ |- _ => symmetry in H
   | H : zip_with _ _ _ = _ :: _ |- _ =>
     apply zip_with_cons_inv in H; destruct H as (?&?&?&?&?&?&?&?)
   | H : _ :: _ = zip_with _ _ _ |- _ => symmetry in H
   | H : zip_with _ _ _ = _ ++ _ |- _ =>
-     apply zip_with_app_inv in H; destruct H as (?&?&?&?&?&?&?&?)
+    apply zip_with_app_inv in H; destruct H as (?&?&?&?&?&?&?&?)
   | H : _ ++ _ = zip_with _ _ _ |- _ => symmetry in H
   end.
 
@@ -2652,33 +2713,31 @@ Ltac decompose_Forall_hyps := repeat
   | H : Forall2 _ [] ?l |- _ => apply Forall2_nil_inv_l in H; subst l
   | H : Forall2 _ ?l [] |- _ => apply Forall2_nil_inv_r in H; subst l
   | H : Forall2 _ (_ :: _) (_ :: _) |- _ =>
-     apply Forall2_cons_inv in H; destruct H
+    apply Forall2_cons_inv in H; destruct H
   | H : Forall2 _ (_ :: _) ?l |- _ =>
-     apply Forall2_cons_inv_l in H; destruct H as (? & ? & ? & ? & ?); subst l
+    apply Forall2_cons_inv_l in H; destruct H as (? & ? & ? & ? & ?); subst l
   | H : Forall2 _ ?l (_ :: _) |- _ =>
      apply Forall2_cons_inv_r in H; destruct H as (? & ? & ? & ? & ?); subst l
   | H : Forall2 _ (_ ++ _) (_ ++ _) |- _ =>
-     destruct (Forall2_app_inv _ _ _ _ _ H); [eauto using Forall2_same_length |]
+    destruct (Forall2_app_inv _ _ _ _ _ H); [eauto using Forall2_same_length |]
   | H : Forall2 _ (_ ++ _) ?l |- _ =>
-     apply Forall2_app_inv_l in H; destruct H as (? & ? & ? & ? & ?); subst l
+    apply Forall2_app_inv_l in H; destruct H as (? & ? & ? & ? & ?); subst l
   | H : Forall2 _ ?l (_ ++ _) |- _ =>
-     apply Forall2_app_inv_r in H; destruct H as (? & ? & ? & ? & ?); subst l
+    apply Forall2_app_inv_r in H; destruct H as (? & ? & ? & ? & ?); subst l
   | H : Forall ?P ?l, H1 : ?l !! _ = Some ?x |- _ =>
     unless (P x) by done;
     let E := fresh in
-    assert (P x) as E by (apply (Forall_lookup_1 P _ _ _ H H1));
-    lazy beta in E
-  | _ =>
+    assert (P x) as E by (apply (Forall_lookup_1 P _ _ _ H H1)); lazy beta in E
+  | H : Forall2 ?P ?l1 ?l2 |- _ =>
     lazymatch goal with
-    | H : Forall2 ?P ?l1 ?l2, H1 : ?l1 !! ?i = Some ?x,
-                              H2 : ?l2 !! ?i = Some ?y |- _ =>
+    | H1 : l1 !! ?i = Some ?x, H2 : l2 !! ?i = Some ?y |- _ =>
       unless (P x y) by done;
       let E := fresh in
       assert (P x y) as E by (apply (Forall2_lookup_lr P _ _ _ _ _ H H1 H2));
-     lazy beta in E
-    | H : Forall2 ?P ?l1 _, H1 : ?l1 !! _ = Some ?x |- _ =>
+      lazy beta in E
+    | H1 : l1 !! _ = Some ?x |- _ =>
       destruct (Forall2_lookup_l P _ _ _ _ H H1) as (?&?&?)
-    | H : Forall2 ?P _ ?l2, H2 : ?l2 !! _ = Some ?y |- _ =>
+    | H2 : l2 !! _ = Some ?y |- _ =>
       destruct (Forall2_lookup_r P _ _ _ _ H H2) as (?&?&?)
     end
   end.
@@ -2699,8 +2758,7 @@ Ltac decompose_Forall := repeat
   | |- Forall2 _ _ (_ <$> _) => apply Forall2_fmap_r
   | _ => progress decompose_Forall_hyps
   | |- Forall _ _ =>
-    apply Forall_lookup_2;
-    intros ???; progress decompose_Forall_hyps
+    apply Forall_lookup_2; intros ???; progress decompose_Forall_hyps
   | |- Forall2 _ _ _ =>
     apply Forall2_lookup_2; [by eauto using Forall2_same_length|];
     intros ?????; progress decompose_Forall_hyps
@@ -2711,10 +2769,9 @@ tautologies, and simplifies [suffix_of] hypotheses involving [(::)] and
 [(++)]. *)
 Ltac simplify_suffix_of := repeat
   match goal with
-  | H : suffix_of (_ :: _) _ |- _ =>
-    destruct (suffix_of_cons_not _ _ H)
-  | H : suffix_of (_ :: _) [] |- _ =>
-    apply suffix_of_nil_inv in H
+  | H : suffix_of (_ :: _) _ |- _ => destruct (suffix_of_cons_not _ _ H)
+  | H : suffix_of (_ :: _) [] |- _ => apply suffix_of_nil_inv in H
+  | H : suffix_of (_ ++ _) (_ ++ _) |- _ => apply suffix_of_app_inv in H
   | H : suffix_of (_ :: _) (_ :: _) |- _ =>
     destruct (suffix_of_cons_inv _ _ _ _ H); clear H
   | H : suffix_of ?x ?x |- _ => clear H
@@ -2726,7 +2783,7 @@ Ltac simplify_suffix_of := repeat
 (** The [solve_suffix_of] tactic tries to solve goals involving [suffix_of]. It
 uses [simplify_suffix_of] to simplify hypotheses and tries to solve [suffix_of]
 conclusions. This tactic either fails or proves the goal. *)
-Ltac solve_suffix_of := solve [intuition (repeat
+Ltac solve_suffix_of := by intuition (repeat
   match goal with
   | _ => done
   | _ => progress simplify_suffix_of
@@ -2735,6 +2792,6 @@ Ltac solve_suffix_of := solve [intuition (repeat
   | |- suffix_of _ (_ :: _) => apply suffix_of_cons_r
   | |- suffix_of _ (_ ++ _) => apply suffix_of_app_r
   | H : suffix_of _ _ → False |- _ => destruct H
-  end)].
+  end).
 Hint Extern 0 (PropHolds (suffix_of _ _)) =>
   unfold PropHolds; solve_suffix_of : typeclass_instances.
diff --git a/theories/listset.v b/theories/listset.v
index 7738d9e90fd0b264876800c64af1be14a2f9238e..6592895b0915893b19c71a35ab136ca1397f035d 100644
--- a/theories/listset.v
+++ b/theories/listset.v
@@ -4,25 +4,18 @@
 removed. This implementation forms a monad. *)
 Require Export base decidable collections list.
 
-Record listset A := Listset {
-  listset_car: list A
-}.
+Record listset A := Listset { listset_car: list A }.
 Arguments listset_car {_} _.
 Arguments Listset {_} _.
 
 Section listset.
 Context {A : Type}.
 
-Instance listset_elem_of: ElemOf A (listset A) := λ x l,
-  x ∈ listset_car l.
-Instance listset_empty: Empty (listset A) :=
-  Listset [].
-Instance listset_singleton: Singleton A (listset A) := λ x,
-  Listset [x].
+Instance listset_elem_of: ElemOf A (listset A) := λ x l, x ∈ listset_car l.
+Instance listset_empty: Empty (listset A) := Listset [].
+Instance listset_singleton: Singleton A (listset A) := λ x, Listset [x].
 Instance listset_union: Union (listset A) := λ l k,
-  match l, k with
-  | Listset l', Listset k' => Listset (l' ++ k')
-  end.
+  match l, k with Listset l', Listset k' => Listset (l' ++ k') end.
 
 Global Instance: SimpleCollection A (listset A).
 Proof.
@@ -47,9 +40,7 @@ Instance listset_intersection_with: IntersectionWith A (listset A) := λ f l k,
   | Listset l', Listset k' => Listset (list_intersection_with f l' k')
   end.
 Instance listset_filter: Filter A (listset A) := λ P _ l,
-  match l with
-  | Listset l' => Listset (filter P l')
-  end.
+  match l with Listset l' => Listset (filter P l') end.
 
 Instance: Collection A (listset A).
 Proof.
@@ -59,8 +50,7 @@ Proof.
   * intros [?] [?]. apply elem_of_list_difference.
 Qed.
 
-Instance listset_elems: Elements A (listset A) :=
-  remove_dups ∘ listset_car.
+Instance listset_elems: Elements A (listset A) := remove_dups ∘ listset_car.
 
 Global Instance: FinCollection A (listset A).
 Proof.
@@ -100,16 +90,11 @@ Hint Extern 1 (Elements _ (listset _)) =>
 Hint Extern 1 (Filter _ (listset _)) =>
   eapply @listset_filter : typeclass_instances.
 
-Instance listset_ret: MRet listset := λ A x,
-  {[ x ]}.
+Instance listset_ret: MRet listset := λ A x, {[ x ]}.
 Instance listset_fmap: FMap listset := λ A B f l,
-  match l with
-  | Listset l' => Listset (f <$> l')
-  end.
+  match l with Listset l' => Listset (f <$> l') end.
 Instance listset_bind: MBind listset := λ A B f l,
-  match l with
-  | Listset l' => Listset (mbind (listset_car ∘ f) l')
-  end.
+  match l with Listset l' => Listset (mbind (listset_car ∘ f) l') end.
 Instance listset_join: MJoin listset := λ A, mbind id.
 
 Instance: CollectionMonad listset.
@@ -119,7 +104,6 @@ Proof.
   * intros ??? [?] ?. apply elem_of_list_bind.
   * intros. apply elem_of_list_ret.
   * intros ??? [?]. apply elem_of_list_fmap.
-  * intros ? [?] ?.
-    unfold mjoin, listset_join, elem_of, listset_elem_of.
+  * intros ? [?] ?. unfold mjoin, listset_join, elem_of, listset_elem_of.
     simpl. by rewrite elem_of_list_bind.
 Qed.
diff --git a/theories/listset_nodup.v b/theories/listset_nodup.v
index 86b91d5b06efb8c8aa53e9ec41a4429ef657d56d..42c15949fbaf09a7e73d52ab0cbc7aade940471b 100644
--- a/theories/listset_nodup.v
+++ b/theories/listset_nodup.v
@@ -19,10 +19,8 @@ Context {A : Type} `{∀ x y : A, Decision (x = y)}.
 Notation C := (listset_nodup A).
 Notation LS := ListsetNoDup.
 
-Instance listset_nodup_elem_of: ElemOf A C := λ x l,
-  x ∈ listset_nodup_car l.
-Instance listset_nodup_empty: Empty C :=
-  LS [] (@NoDup_nil_2 _).
+Instance listset_nodup_elem_of: ElemOf A C := λ x l, x ∈ listset_nodup_car l.
+Instance listset_nodup_empty: Empty C := LS [] (@NoDup_nil_2 _).
 Instance listset_nodup_singleton: Singleton A C := λ x,
   LS [x] (NoDup_singleton x).
 Instance listset_nodup_difference: Difference C := λ l k,
@@ -85,8 +83,7 @@ Proof.
   * apply _.
   * intros. unfold intersection_with, listset_nodup_intersection_with,
       elem_of, listset_nodup_elem_of. simpl.
-    rewrite elem_of_remove_dups.
-    by apply elem_of_list_intersection_with.
+    rewrite elem_of_remove_dups. by apply elem_of_list_intersection_with.
   * intros. apply elem_of_list_filter.
 Qed.
 End list_collection.
diff --git a/theories/mapset.v b/theories/mapset.v
index 6c2bd438e1281e862883ac0967e2c04c13d80b7b..21e2f7b6b343b9875864c2f4664cd794932720d7 100644
--- a/theories/mapset.v
+++ b/theories/mapset.v
@@ -3,12 +3,9 @@
 (** This files gives an implementation of finite sets using finite maps with
 elements of the unit type. Since maps enjoy extensional equality, the
 constructed finite sets do so as well. *)
-
 Require Export fin_map_dom.
 
-Record mapset (M : Type → Type) := Mapset {
-  mapset_car: M unit
-}.
+Record mapset (M : Type → Type) := Mapset { mapset_car: M unit }.
 Arguments Mapset {_} _.
 Arguments mapset_car {_} _.
 
@@ -17,46 +14,33 @@ Context `{FinMap K M}.
 
 Instance mapset_elem_of: ElemOf K (mapset M) := λ x X,
   mapset_car X !! x = Some ().
-Instance mapset_empty: Empty (mapset M) :=
-  Mapset ∅.
-Instance mapset_singleton: Singleton K (mapset M) := λ x,
-  Mapset {[ (x,()) ]}.
+Instance mapset_empty: Empty (mapset M) := Mapset ∅.
+Instance mapset_singleton: Singleton K (mapset M) := λ x, Mapset {[ (x,()) ]}.
 Instance mapset_union: Union (mapset M) := λ X1 X2,
-  match X1, X2 with
-  | Mapset m1, Mapset m2 => Mapset (m1 ∪ m2)
-  end.
+  match X1, X2 with Mapset m1, Mapset m2 => Mapset (m1 ∪ m2) end.
 Instance mapset_intersection: Intersection (mapset M) := λ X1 X2,
-  match X1, X2 with
-  | Mapset m1, Mapset m2 => Mapset (m1 ∩ m2)
-  end.
+  match X1, X2 with Mapset m1, Mapset m2 => Mapset (m1 ∩ m2) end.
 Instance mapset_difference: Difference (mapset M) := λ X1 X2,
-  match X1, X2 with
-  | Mapset m1, Mapset m2 => Mapset (m1 ∖ m2)
-  end.
+  match X1, X2 with Mapset m1, Mapset m2 => Mapset (m1 ∖ m2) end.
 Instance mapset_elems: Elements K (mapset M) := λ X,
-  match X with
-  | Mapset m => fst <$> map_to_list m
-  end.
+  match X with Mapset m => fst <$> map_to_list m end.
 
 Lemma mapset_eq (X1 X2 : mapset M) : X1 = X2 ↔ ∀ x, x ∈ X1 ↔ x ∈ X2.
 Proof.
   split.
   * intros. by subst.
   * destruct X1 as [m1], X2 as [m2]. simpl. intros E.
-    f_equal. apply map_eq. intros i.
-    apply option_eq. intros []. by apply E.
+    f_equal. apply map_eq. intros i. apply option_eq. intros []. by apply E.
 Qed.
 
 Global Instance mapset_eq_dec `{∀ m1 m2 : M unit, Decision (m1 = m2)}
     (X1 X2 : mapset M) : Decision (X1 = X2) | 1.
 Proof.
  refine
-  match X1, X2 with
-  | Mapset m1, Mapset m2 => cast_if (decide (m1 = m2))
-  end; abstract congruence.
+  match X1, X2 with Mapset m1, Mapset m2 => cast_if (decide (m1 = m2)) end;
+  abstract congruence.
 Defined.
-Global Instance mapset_elem_of_dec x (X : mapset M) :
-  Decision (x ∈ X) | 1.
+Global Instance mapset_elem_of_dec x (X : mapset M) : Decision (x ∈ X) | 1.
 Proof. solve_decision. Defined.
 
 Instance: Collection K (mapset M).
@@ -78,11 +62,8 @@ Proof.
     destruct (m2 !! x) as [[]|]; intuition congruence.
 Qed.
 
-Global Instance: PartialOrder (mapset M).
-Proof.
-  split; try apply _.
-  intros ????. apply mapset_eq. intuition.
-Qed.
+Global Instance: PartialOrder (@subseteq (mapset M) _).
+Proof. split; try apply _. intros ????. apply mapset_eq. intuition. Qed.
 
 Global Instance: FinCollection K (mapset M).
 Proof.
@@ -123,14 +104,12 @@ Lemma elem_of_mapset_dom_with `(f : A → bool) m i :
   i ∈ mapset_dom_with f m ↔ ∃ x, m !! i = Some x ∧ f x.
 Proof.
   unfold mapset_dom_with, elem_of, mapset_elem_of.
-  simpl. rewrite lookup_merge by done.
-  destruct (m !! i) as [a|].
+  simpl. rewrite lookup_merge by done. destruct (m !! i) as [a|].
   * destruct (Is_true_reflect (f a)); naive_solver.
   * naive_solver.
 Qed.
 
-Instance mapset_dom {A} : Dom (M A) (mapset M) :=
-  mapset_dom_with (λ _, true).
+Instance mapset_dom {A} : Dom (M A) (mapset M) := mapset_dom_with (λ _, true).
 Instance mapset_dom_spec: FinMapDom K M (mapset M).
 Proof.
   split; try apply _. intros. unfold dom, mapset_dom.
diff --git a/theories/natmap.v b/theories/natmap.v
index de543be9739d60d65ee3714c7faa6eabd60e2071..40a65f6fb2dc172ffe9b9add071523d132edbc0d 100644
--- a/theories/natmap.v
+++ b/theories/natmap.v
@@ -1,15 +1,13 @@
 (* Copyright (c) 2012-2013, Robbert Krebbers. *)
 (* This file is distributed under the terms of the BSD license. *)
-(** This files implements finite maps whose keys range over Coq's data type of
-unary natural numbers [nat]. *)
-Require Import fin_maps.
+(** This files implements a type [natmap A] of finite maps whose keys range
+over Coq's data type of unary natural numbers [nat]. The implementation equips
+a list with a proof of canonicity. *)
+Require Import fin_maps mapset.
 
 Notation natmap_raw A := (list (option A)).
 Definition natmap_wf {A} (l : natmap_raw A) :=
-  match last l with
-  | None => True
-  | Some x => is_Some x
-  end.
+  match last l with None => True | Some x => is_Some x end.
 Instance natmap_wf_pi {A} (l : natmap_raw A) : ProofIrrel (natmap_wf l).
 Proof. unfold natmap_wf. case_match; apply _. Qed.
 
@@ -31,7 +29,7 @@ Definition natmap (A : Type) : Type := sig (@natmap_wf A).
 
 Instance natmap_empty {A} : Empty (natmap A) := [] ↾ I.
 Instance natmap_lookup {A} : Lookup nat A (natmap A) :=
-  λ i m, mjoin (`m !! i).
+  λ i m, match m with exist l _ => mjoin (l !! i) end.
 
 Fixpoint natmap_singleton_raw {A} (i : nat) (x : A) : natmap_raw A :=
   match i with
@@ -91,7 +89,7 @@ Proof.
     eauto using natmap_singleton_wf, natmap_cons_canon_wf, natmap_wf_inv.
 Qed.
 Instance natmap_alter {A} : PartialAlter nat A (natmap A) := λ f i m,
-  natmap_alter_raw f i (`m)↾natmap_alter_wf _ _ _ (proj2_sig m).
+  match m with exist l Hl => _↾natmap_alter_wf f i l Hl end.
 Lemma natmap_lookup_alter_raw {A} (f : option A → option A) i l :
   mjoin (natmap_alter_raw f i l !! i) = f (mjoin (l !! i)).
 Proof.
@@ -138,15 +136,18 @@ Proof.
   revert l2. induction l1; intros [|??]; simpl;
     eauto using natmap_merge_aux_wf, natmap_cons_canon_wf, natmap_wf_inv.
 Qed.
-Lemma natmap_lookup_merge_raw {A B C} (f : option A → option B → option C) l1 l2 i :
-  f None None = None →
+Lemma natmap_lookup_merge_raw {A B C} (f : option A → option B → option C)
+    l1 l2 i : f None None = None →
   mjoin (natmap_merge_raw f l1 l2 !! i) = f (mjoin (l1 !! i)) (mjoin (l2 !! i)).
 Proof.
   intros. revert i l2. induction l1; intros [|?] [|??]; simpl;
     autorewrite with natmap; auto.
 Qed.
 Instance natmap_merge: Merge natmap := λ A B C f m1 m2,
-  natmap_merge_raw f _ _ ↾ natmap_merge_wf _ _ _ (proj2_sig m1) (proj2_sig m2).
+  match m1, m2 with
+  | exist l1 Hl1, exist l2 Hl2 =>
+     natmap_merge_raw f _ _ ↾ natmap_merge_wf _ _ _ Hl1 Hl2
+  end.
 
 Fixpoint natmap_to_list_raw {A} (i : nat) (l : natmap_raw A) : list (nat * A) :=
   match l with
@@ -186,7 +187,7 @@ Proof.
   rewrite natmap_elem_of_to_list_raw_aux. intros (?&?&?). lia.
 Qed.
 Instance natmap_to_list {A} : FinMapToList nat A (natmap A) := λ m,
-  natmap_to_list_raw 0 (`m).
+  match m with exist l _ => natmap_to_list_raw 0 l end.
 
 Definition natmap_map_raw {A B} (f : A → B) : natmap_raw A → natmap_raw B :=
   fmap (fmap f).
@@ -199,7 +200,9 @@ Proof.
 Qed.
 Lemma natmap_lookup_map_raw {A B} (f : A → B) i l :
   mjoin (natmap_map_raw f l !! i) = f <$> mjoin (l !! i).
-Proof. unfold natmap_map_raw. rewrite list_lookup_fmap. by destruct (l !! i). Qed.
+Proof.
+  unfold natmap_map_raw. rewrite list_lookup_fmap. by destruct (l !! i).
+Qed.
 Instance natmap_map: FMap natmap := λ A B f m,
   natmap_map_raw f _ ↾ natmap_map_wf _ _ (proj2_sig m).
 
@@ -228,3 +231,37 @@ Proof.
   * intros ? [??] ??. by apply natmap_elem_of_to_list_raw.
   * intros ????? [??] [??] ?. by apply natmap_lookup_merge_raw.
 Qed.
+
+(** Finally, we can construct sets of [nat]s satisfying extensional equality. *)
+Notation natset := (mapset natmap).
+Instance natmap_dom {A} : Dom (natmap A) natset := mapset_dom.
+Instance: FinMapDom nat natmap natset := mapset_dom_spec.
+
+(** A [natmap A] forms a stack with elements of type [A] and possible holes *)
+Definition natmap_push {A} (o : option A) (m : natmap A) : natmap A :=
+  match m with exist l Hl => _↾natmap_cons_canon_wf o l Hl end.
+
+Definition natmap_pop_raw {A} (l : natmap_raw A) : natmap_raw A := tail l.
+Lemma natmap_pop_wf {A} (l : natmap_raw A) :
+  natmap_wf l → natmap_wf (natmap_pop_raw l).
+Proof. destruct l; simpl; eauto using natmap_wf_inv. Qed.
+Definition natmap_pop {A} (m : natmap A) : natmap A :=
+  match m with exist l Hl => _↾natmap_pop_wf _ Hl end.
+
+Lemma lookup_natmap_push_O {A} o (m : natmap A) : natmap_push o m !! 0 = o.
+Proof. by destruct o, m as [[|??]]. Qed.
+Lemma lookup_natmap_push_S {A} o (m : natmap A) i :
+  natmap_push o m !! S i = m !! i.
+Proof. by destruct o, m as [[|??]]. Qed.
+Lemma lookup_natmap_pop {A} (m : natmap A) i : natmap_pop m !! i = m !! S i.
+Proof. by destruct m as [[|??]]. Qed.
+
+Lemma natmap_push_pop {A} (m : natmap A) :
+  natmap_push (m !! 0) (natmap_pop m) = m.
+Proof.
+  apply map_eq. intros i. destruct i.
+  * by rewrite lookup_natmap_push_O.
+  * by rewrite lookup_natmap_push_S, lookup_natmap_pop.
+Qed.
+Lemma natmap_pop_push {A} o (m : natmap A) : natmap_pop (natmap_push o m) = m.
+Proof. apply (sig_eq_pi _). by destruct o, m as [[|??]]. Qed.
diff --git a/theories/nmap.v b/theories/nmap.v
index a2abeeaf683b422493defa55cce955a55886be8a..4a79107e320af9acae3ef248c92ae9151497cb2b 100644
--- a/theories/nmap.v
+++ b/theories/nmap.v
@@ -2,7 +2,7 @@
 (* This file is distributed under the terms of the BSD license. *)
 (** This files extends the implementation of finite over [positive] to finite
 maps whose keys range over Coq's data type of binary naturals [N]. *)
-Require Import pmap.
+Require Import pmap mapset.
 Require Export prelude fin_maps.
 
 Local Open Scope N_scope.
@@ -17,8 +17,7 @@ Instance Nmap_eq_dec `{∀ x y : A, Decision (x = y)} (t1 t2 : Nmap A) :
 Proof.
  refine
   match t1, t2 with
-  | NMap x t1, NMap y t2 =>
-    cast_if_and (decide (x = y)) (decide (t1 = t2))
+  | NMap x t1, NMap y t2 => cast_if_and (decide (x = y)) (decide (t1 = t2))
   end; abstract congruence.
 Defined.
 
@@ -35,17 +34,14 @@ Instance Npartial_alter {A} : PartialAlter N A (Nmap A) := λ f i t,
   end.
 Instance Nto_list {A} : FinMapToList N A (Nmap A) := λ t,
   match t with
-  | NMap o t => option_case (λ x, [(0,x)]) [] o ++
-     (fst_map Npos <$> map_to_list t)
+  | NMap o t => default [] o (λ x, [(0,x)]) ++ (fst_map Npos <$> map_to_list t)
   end.
 Instance Nmerge: Merge Nmap := λ A B C f t1 t2,
   match t1, t2 with
   | NMap o1 t1, NMap o2 t2 => NMap (f o1 o2) (merge f t1 t2)
   end.
 Instance Nfmap: FMap Nmap := λ A B f t,
-  match t with
-  | NMap o t => NMap (fmap f o) (fmap f t)
-  end.
+  match t with NMap o t => NMap (fmap f o) (fmap f t) end.
 
 Instance: FinMap N Nmap.
 Proof.
@@ -54,9 +50,8 @@ Proof.
     + apply (H 0).
     + apply map_eq. intros i. apply (H (Npos i)).
   * by intros ? [|?].
-  * intros ? f [? t] [|i]; simpl.
-    + done.
-    + apply lookup_partial_alter.
+  * intros ? f [? t] [|i]; simpl; [done |].
+    apply lookup_partial_alter.
   * intros ? f [? t] [|i] [|j]; simpl; try intuition congruence.
     intros. apply lookup_partial_alter_ne. congruence.
   * intros ??? [??] []; simpl. done. apply lookup_fmap.
@@ -80,7 +75,11 @@ Proof.
       - rewrite elem_of_list_fmap.
         destruct i as [|i]; simpl; [done |].
         intros. exists (i, x). by rewrite elem_of_map_to_list.
-  * intros ??? f ? [o1 t1] [o2 t2] [|?]; simpl.
-    + done.
-    + apply (lookup_merge f t1 t2).
+  * intros ??? f ? [o1 t1] [o2 t2] [|?]; simpl; [done|].
+    apply (lookup_merge f t1 t2).
 Qed.
+
+(** Finally, we can construct sets of [N]s satisfying extensional equality. *)
+Notation Nset := (mapset Nmap).
+Instance Nmap_dom {A} : Dom (Nmap A) Nset := mapset_dom.
+Instance: FinMapDom N Nmap Nset := mapset_dom_spec.
diff --git a/theories/numbers.v b/theories/numbers.v
index 2315710ceded039e2f9f613a3f85943f19b24cee..d0e2c74bdbccb64a6b49694ce034c40418e617f6 100644
--- a/theories/numbers.v
+++ b/theories/numbers.v
@@ -3,7 +3,7 @@
 (** This file collects some trivial facts on the Coq types [nat] and [N] for
 natural numbers, and the type [Z] for integers. It also declares some useful
 notations. *)
-Require Export PArith NArith ZArith.
+Require Export Eqdep PArith NArith ZArith.
 Require Import Qcanon.
 Require Export base decidable.
 Open Scope nat_scope.
@@ -32,6 +32,21 @@ Instance nat_le_dec: ∀ x y : nat, Decision (x ≤ y) := le_dec.
 Instance nat_lt_dec: ∀ x y : nat, Decision (x < y) := lt_dec.
 Instance nat_inhabited: Inhabited nat := populate 0%nat.
 
+Instance nat_le_pi: ∀ x y : nat, ProofIrrel (x ≤ y).
+Proof.
+  assert (∀ x y (p : x ≤ y) y' (q : x ≤ y'),
+    y = y' → eq_dep nat (le x) y p y' q) as aux.
+  { fix 3. intros x ? [|y p] ? [|y' q].
+    * done.
+    * clear nat_le_pi. omega.
+    * clear nat_le_pi. omega.
+    * injection 1. intros Hy. by case (nat_le_pi x y p y' q Hy). }
+  intros x y p q.
+  by apply (eq_dep_eq_dec (λ x y, decide (x = y))), aux.
+Qed.
+Instance nat_lt_pi: ∀ x y : nat, ProofIrrel (x < y).
+Proof. apply _. Qed.
+
 Lemma lt_n_SS n : n < S (S n).
 Proof. auto with arith. Qed.
 Lemma lt_n_SSS n : n < S (S (S n)).
@@ -45,6 +60,14 @@ Definition sum_list_with {A} (f : A → nat) : list A → nat :=
   end.
 Notation sum_list := (sum_list_with id).
 
+Lemma mult_split_eq n x1 x2 y1 y2 :
+  x2 < n → y2 < n → x1 * n + x2 = y1 * n + y2 → x1 = y1 ∧ x2 = y2.
+Proof.
+  intros Hx2 Hy2 E.
+  cut (x1 = y1); [intros; subst;lia |].
+  revert y1 E. induction x1; simpl; intros [|?]; simpl; auto with lia.
+Qed.
+
 (** * Notations and properties of [positive] *)
 Open Scope positive_scope.
 
@@ -185,6 +208,16 @@ Arguments Z.modulo _ _ : simpl never.
 Arguments Z.quot _ _ : simpl never.
 Arguments Z.rem _ _ : simpl never.
 
+Lemma Zmod_pos a b : (0 < b)%Z → (0 ≤ a `mod` b)%Z.
+Proof. apply Z.mod_pos_bound. Qed.
+
+Hint Resolve Z.lt_le_incl : zpos.
+Hint Resolve Z.add_nonneg_pos Z.add_pos_nonneg Z.add_nonneg_nonneg : zpos.
+Hint Resolve Z.mul_nonneg_nonneg Z.mul_pos_pos : zpos.
+Hint Resolve Z.pow_pos_nonneg : zpos.
+Hint Resolve Zmod_pos Z.div_pos : zpos.
+Hint Extern 1000 => lia : zpos.
+
 (** * Notations and properties of [Qc] *)
 Notation "2" := (1+1)%Qc : Qc_scope.
 Infix "≤" := Qcle : Qc_scope.
@@ -213,8 +246,7 @@ Proof. split; auto using Qcle_not_lt, Qcnot_lt_le. Qed.
 Lemma Qclt_nge (x y : Qc) : (x < y ↔ ¬y ≤ x)%Qc.
 Proof. split; auto using Qclt_not_le, Qcnot_le_lt. Qed.
 
-Lemma Qcplus_le_mono_l (x y z : Qc) :
-  (x ≤ y ↔ z + x ≤ z + y)%Qc.
+Lemma Qcplus_le_mono_l (x y z : Qc) : (x ≤ y ↔ z + x ≤ z + y)%Qc.
 Proof.
   split; intros.
   * by apply Qcplus_le_compat.
@@ -222,17 +254,20 @@ Proof.
     replace y with ((0 - z) + (z + y))%Qc by ring.
     by apply Qcplus_le_compat.
 Qed.
-Lemma Qcplus_le_mono_r (x y z : Qc) :
-  (x ≤ y ↔ x + z ≤ y + z)%Qc.
+Lemma Qcplus_le_mono_r (x y z : Qc) : (x ≤ y ↔ x + z ≤ y + z)%Qc.
 Proof. rewrite !(Qcplus_comm _ z). apply Qcplus_le_mono_l. Qed.
-Lemma Qcplus_lt_mono_l (x y z : Qc) :
-  (x < y ↔ z + x < z + y)%Qc.
+Lemma Qcplus_lt_mono_l (x y z : Qc) : (x < y ↔ z + x < z + y)%Qc.
 Proof. by rewrite !Qclt_nge, <-Qcplus_le_mono_l. Qed.
-Lemma Qcplus_lt_mono_r (x y z : Qc) :
-  (x < y ↔ x + z < y + z)%Qc.
+Lemma Qcplus_lt_mono_r (x y z : Qc) : (x < y ↔ x + z < y + z)%Qc.
 Proof. by rewrite !Qclt_nge, <-Qcplus_le_mono_r. Qed.
 
 (** * Conversions *)
+Lemma Z_to_nat_nonpos x : (x ≤ 0)%Z → Z.to_nat x = 0.
+Proof.
+  destruct x; simpl; auto using Z2Nat.inj_neg.
+  by intros [].
+Qed.
+
 (** The function [Z_to_option_N] converts an integer [x] into a natural number
 by giving [None] in case [x] is negative. *)
 Definition Z_to_option_N (x : Z) : option N :=
@@ -277,8 +312,7 @@ Proof.
   rewrite Z_to_option_nat_Some.
   split; intros [??]; subst; auto using Nat2Z.id, Z2Nat.id, eq_sym.
 Qed.
-Lemma Z_to_option_of_nat x :
-  Z_to_option_nat (Z.of_nat x) = Some x.
+Lemma Z_to_option_of_nat x : Z_to_option_nat (Z.of_nat x) = Some x.
 Proof. apply Z_to_option_nat_Some_alt. auto using Nat2Z.is_nonneg. Qed.
 
 (** The function [Z_of_sumbool] converts a sumbool [P] into an integer
@@ -308,11 +342,9 @@ Proof.
 Qed.
 (* We have [x `mod` 0 = 0] on [nat], and [x `mod` 0 = x] on [N]. *)
 Lemma N_to_nat_mod x y :
-  y ≠ 0%N →
-  N.to_nat (x `mod` y) = N.to_nat x `mod` N.to_nat y.
+  y ≠ 0%N → N.to_nat (x `mod` y) = N.to_nat x `mod` N.to_nat y.
 Proof.
-  intros.
-  apply NPeano.Nat.mod_unique with (N.to_nat (x `div` y)).
+  intros. apply NPeano.Nat.mod_unique with (N.to_nat (x `div` y)).
   { by apply N_to_nat_lt, N.mod_lt. }
   rewrite (N.div_unique_exact (x * y) y x), N.div_mul by lia.
   by rewrite <-N2Nat.inj_mul, <-N2Nat.inj_add, <-N.div_mod.
diff --git a/theories/option.v b/theories/option.v
index fc9d13771288a801798d9f37c295a0a395b33777..8f280e906fc3e7837ffbed36087537322ffe1d04 100644
--- a/theories/option.v
+++ b/theories/option.v
@@ -16,28 +16,20 @@ Instance Some_inj {A} : Injective (=) (=) (@Some A).
 Proof. congruence. Qed.
 
 (** The non dependent elimination principle on the option type. *)
-Definition option_case {A B} (f : A → B) (b : B) (x : option A) : B :=
-  match x with
-  | None => b
-  | Some a => f a
-  end.
+Definition default {A B} (b : B) (x : option A) (f : A → B)  : B :=
+  match x with None => b | Some a => f a end.
 
 (** The [from_option] function allows us to get the value out of the option
 type by specifying a default value. *)
 Definition from_option {A} (a : A) (x : option A) : A :=
-  match x with
-  | None => a
-  | Some b => b
-  end.
+  match x with None => a | Some b => b end.
 
 (** An alternative, but equivalent, definition of equality on the option
 data type. This theorem is useful to prove that two options are the same. *)
 Lemma option_eq {A} (x y : option A) :
   x = y ↔ ∀ a, x = Some a ↔ y = Some a.
 Proof.
-  split.
-  { intros. by subst. }
-  intros E. destruct x, y.
+  split; [by intros; by subst |]. intros E. destruct x, y.
   + by apply E.
   + symmetry. by apply E.
   + by apply E.
@@ -45,24 +37,19 @@ Proof.
 Qed.
 
 Inductive is_Some {A} : option A → Prop :=
-  make_is_Some x : is_Some (Some x).
+  mk_is_Some x : is_Some (Some x).
 
 Instance is_Some_pi {A} (x : option A) : ProofIrrel (is_Some x).
 Proof.
   intros [?] p2. by refine
     match p2 in is_Some o return
-      match o with
-      | Some y => (make_is_Some y =)
-      | _ => λ _, False
-      end p2
-    with
-    | make_is_Some y => _
-    end.
+      match o with Some y => (mk_is_Some y =) | _ => λ _, False end p2
+    with mk_is_Some y => _ end.
 Qed.
 
-Lemma make_is_Some_alt `(x : option A) a : x = Some a → is_Some x.
+Lemma mk_is_Some_alt `(x : option A) a : x = Some a → is_Some x.
 Proof. intros. by subst. Qed.
-Hint Resolve make_is_Some_alt.
+Hint Resolve mk_is_Some_alt.
 Lemma is_Some_None {A} : ¬is_Some (@None A).
 Proof. by inversion 1. Qed.
 Hint Resolve is_Some_None.
@@ -71,9 +58,7 @@ Lemma is_Some_alt `(x : option A) : is_Some x ↔ ∃ y, x = Some y.
 Proof. split. inversion 1; eauto. intros [??]. by subst. Qed.
 
 Ltac inv_is_Some := repeat
-  match goal with
-  | H : is_Some _ |- _ => inversion H; clear H; subst
-  end.
+  match goal with H : is_Some _ |- _ => inversion H; clear H; subst end.
 
 Definition is_Some_proj `{x : option A} : is_Some x → A :=
   match x with
@@ -87,7 +72,7 @@ Definition Some_dec `(x : option A) : { a | x = Some a } + { x = None } :=
   end.
 Instance is_Some_dec `(x : option A) : Decision (is_Some x) :=
   match x with
-  | Some x => left (make_is_Some x)
+  | Some x => left (mk_is_Some x)
   | None => right is_Some_None
   end.
 Instance None_dec `(x : option A) : Decision (x = None) :=
@@ -101,7 +86,7 @@ Proof. split. by destruct 2. destruct x. by intros []. done. Qed.
 Lemma not_eq_None_Some `(x : option A) : x ≠ None ↔ is_Some x.
 Proof. rewrite eq_None_not_Some. split. apply dec_stable. tauto. Qed.
 
-Lemma make_eq_Some {A} (x : option A) a :
+Lemma mk_eq_Some {A} (x : option A) a :
   is_Some x → (∀ b, x = Some b → b = a) → x = Some a.
 Proof. destruct 1. intros. f_equal. auto. Qed.
 
@@ -122,18 +107,12 @@ Instance option_eq_dec `{dec : ∀ x y : A, Decision (x = y)}
 (** * Monadic operations *)
 Instance option_ret: MRet option := @Some.
 Instance option_bind: MBind option := λ A B f x,
-  match x with
-  | Some a => f a
-  | None => None
-  end.
+  match x with Some a => f a | None => None end.
 Instance option_join: MJoin option := λ A x,
-  match x with
-  | Some x => x
-  | None => None
-  end.
+  match x with Some x => x | None => None end.
 Instance option_fmap: FMap option := @option_map.
 Instance option_guard: MGuard option := λ P dec A x,
-  if dec then x else None.
+  match dec with left H => x H | _ => None end.
 
 Definition mapM `{!MBind M} `{!MRet M} {A B}
     (f : A → M B) : list A → M (list B) :=
@@ -153,60 +132,49 @@ Lemma fmap_None {A B} (f : A → B) (x : option A) :
   f <$> x = None ↔ x = None.
 Proof. unfold fmap, option_fmap. by destruct x. Qed.
 
-Lemma option_fmap_id {A} (x : option A) :
-  id <$> x = x.
+Lemma option_fmap_id {A} (x : option A) : id <$> x = x.
 Proof. by destruct x. Qed.
 Lemma option_bind_assoc {A B C} (f : A → option B)
     (g : B → option C) (x : option A) : (x ≫= f) ≫= g = x ≫= (mbind g ∘ f).
 Proof. by destruct x; simpl. Qed.
 Lemma option_bind_ext {A B} (f g : A → option B) x y :
-  (∀ a, f a = g a) →
-  x = y →
-  x ≫= f = y ≫= g.
+  (∀ a, f a = g a) → x = y → x ≫= f = y ≫= g.
 Proof. intros. destruct x, y; simplify_equality; simpl; auto. Qed.
 Lemma option_bind_ext_fun {A B} (f g : A → option B) x :
-  (∀ a, f a = g a) →
-  x ≫= f = x ≫= g.
+  (∀ a, f a = g a) → x ≫= f = x ≫= g.
 Proof. intros. by apply option_bind_ext. Qed.
 
 Section mapM.
   Context {A B : Type} (f : A → option B).
 
-  Lemma mapM_ext (g : A → option B) l :
-    (∀ x, f x = g x) → mapM f l = mapM g l.
+  Lemma mapM_ext (g : A → option B) l : (∀ x, f x = g x) → mapM f l = mapM g l.
   Proof. intros Hfg. by induction l; simpl; rewrite ?Hfg, ?IHl. Qed.
   Lemma Forall2_mapM_ext (g : A → option B) l k :
     Forall2 (λ x y, f x = g y) l k → mapM f l = mapM g k.
-  Proof.
-    induction 1 as [|???? Hfg ? IH]; simpl. done. by rewrite Hfg, IH.
-  Qed.
+  Proof. induction 1 as [|???? Hfg ? IH]; simpl. done. by rewrite Hfg, IH. Qed.
   Lemma Forall_mapM_ext (g : A → option B) l :
     Forall (λ x, f x = g x) l → mapM f l = mapM g l.
-  Proof.
-    induction 1 as [|?? Hfg ? IH]; simpl. done. by rewrite Hfg, IH.
-  Qed.
+  Proof. induction 1 as [|?? Hfg ? IH]; simpl. done. by rewrite Hfg, IH. Qed.
 
-  Lemma mapM_Some_1 l k :
-    mapM f l = Some k → Forall2 (λ x y, f x = Some y) l k.
+  Lemma mapM_Some_1 l k : mapM f l = Some k → Forall2 (λ x y, f x = Some y) l k.
   Proof.
     revert k. induction l as [|x l]; intros [|y k]; simpl; try done.
     * destruct (f x); simpl; [|discriminate]. by destruct (mapM f l).
     * destruct (f x) eqn:?; simpl; [|discriminate].
       destruct (mapM f l); intros; simplify_equality. constructor; auto.
   Qed.
-  Lemma mapM_Some_2 l k :
-    Forall2 (λ x y, f x = Some y) l k → mapM f l = Some k.
+  Lemma mapM_Some_2 l k : Forall2 (λ x y, f x = Some y) l k → mapM f l = Some k.
   Proof.
     induction 1 as [|???? Hf ? IH]; simpl; [done |].
     rewrite Hf. simpl. by rewrite IH.
   Qed.
-  Lemma mapM_Some l k :
-    mapM f l = Some k ↔ Forall2 (λ x y, f x = Some y) l k.
+  Lemma mapM_Some l k : mapM f l = Some k ↔ Forall2 (λ x y, f x = Some y) l k.
   Proof. split; auto using mapM_Some_1, mapM_Some_2. Qed.
 End mapM.
 
 Tactic Notation "simplify_option_equality" "by" tactic3(tac) := repeat
   match goal with
+  | _ => progress (unfold default in *)
   | _ => first [progress simpl in * | progress simplify_equality]
   | H : context [mbind (M:=option) (A:=?A) ?f ?o] |- _ =>
     let Hx := fresh in
@@ -279,10 +247,10 @@ Tactic Notation "simplify_option_equality" "by" tactic3(tac) := repeat
       rewrite Hx; clear Hx
     end
   | H : context C [@mguard option _ ?P ?dec _ ?x] |- _ =>
-    let X := context C [ if dec then x else None ] in
+    let X := context C [ match dec with left H => x H | _ => None end ] in
     change X in H; destruct_decide dec
   | |- context C [@mguard option _ ?P ?dec _ ?x] =>
-    let X := context C [ if dec then x else None ] in
+    let X := context C [ match dec with left H => x H | _ => None end ] in
     change X; destruct_decide dec
   | H1 : ?o = Some ?x, H2 : ?o = Some ?y |- _ =>
     assert (y = x) by congruence; clear H2
@@ -304,14 +272,9 @@ Instance option_union_with {A} : UnionWith A (option A) := λ f x y,
   | None, Some b => Some b
   | None, None => None
   end.
-Instance option_intersection_with {A} :
-    IntersectionWith A (option A) := λ f x y,
-  match x, y with
-  | Some a, Some b => f a b
-  | _, _ => None
-  end.
-Instance option_difference_with {A} :
-    DifferenceWith A (option A) := λ f x y,
+Instance option_intersection_with {A} : IntersectionWith A (option A) :=
+  λ f x y, match x, y with Some a, Some b => f a b | _, _ => None end.
+Instance option_difference_with {A} : DifferenceWith A (option A) := λ f x y,
   match x, y with
   | Some a, Some b => f a b
   | Some a, None => Some a
diff --git a/theories/orders.v b/theories/orders.v
index b3ba0df3620d9e9dd2158af4df905215492f71cf..7c7790de3a13377d9431ed33e3e890f9ec0210ca 100644
--- a/theories/orders.v
+++ b/theories/orders.v
@@ -2,7 +2,6 @@
 (* This file is distributed under the terms of the BSD license. *)
 (** This file collects common properties of pre-orders and semi lattices. This
 theory will mainly be used for the theory on collections and finite maps. *)
-Require Import SetoidList.
 Require Export base decidable tactics list.
 
 (** * Pre-orders *)
@@ -15,9 +14,9 @@ Section preorder.
   Instance preorder_equivalence: @Equivalence A (≡).
   Proof.
     split.
-    * firstorder.
-    * firstorder.
-    * intros x y z; split; transitivity y; firstorder.
+    * done.
+    * by intros ?? [??].
+    * by intros x y z [??] [??]; split; transitivity y.
   Qed.
 
   Global Instance: Proper ((≡) ==> (≡) ==> iff) (⊆).
@@ -40,13 +39,13 @@ Section preorder.
 
   Lemma subset_subseteq X Y : X ⊂ Y → X ⊆ Y.
   Proof. by intros [? _]. Qed.
-  Lemma subset_trans_l X Y Z : X ⊂ Y → Y ⊆ Z → X ⊂ Z.
+  Lemma subset_transitive_l X Y Z : X ⊂ Y → Y ⊆ Z → X ⊂ Z.
   Proof.
     intros [? HXY] ?. split.
     * by transitivity Y.
     * contradict HXY. by transitivity Z.
   Qed.
-  Lemma subset_trans_r X Y Z : X ⊆ Y → Y ⊂ Z → X ⊂ Z.
+  Lemma subset_transitive_r X Y Z : X ⊆ Y → Y ⊂ Z → X ⊂ Z.
   Proof.
     intros ? [? HYZ]. split.
     * by transitivity Y.
@@ -57,7 +56,7 @@ Section preorder.
   Proof.
     split.
     * firstorder.
-    * eauto using subset_trans_r, subset_subseteq.
+    * eauto using subset_transitive_r, subset_subseteq.
   Qed.
   Global Instance: Proper ((≡) ==> (≡) ==> iff) (⊂).
   Proof. unfold subset, preorder_subset. solve_proper. Qed.
@@ -98,7 +97,7 @@ Hint Extern 0 (@Equivalence _ (≡)) =>
 
 (** * Partial orders *)
 Section partialorder.
-  Context `{PartialOrder A}.
+  Context `{SubsetEq A} `{!PartialOrder (⊆)}.
 
   Global Instance: LeibnizEquiv A.
   Proof.
@@ -121,26 +120,20 @@ Section bounded_join_sl.
   Proof. intros. transitivity x2; auto. Qed.
   Hint Resolve union_subseteq_l_alt union_subseteq_r_alt.
 
-  Lemma union_preserving_l x y1 y2 :
-    y1 ⊆ y2 →
-    x ∪ y1 ⊆ x ∪ y2.
+  Lemma union_preserving_l x y1 y2 : y1 ⊆ y2 → x ∪ y1 ⊆ x ∪ y2.
   Proof. auto. Qed.
-  Lemma union_preserving_r x1 x2 y :
-    x1 ⊆ x2 →
-    x1 ∪ y ⊆ x2 ∪ y.
+  Lemma union_preserving_r x1 x2 y : x1 ⊆ x2 → x1 ∪ y ⊆ x2 ∪ y.
   Proof. auto. Qed.
-  Lemma union_preserving x1 x2 y1 y2 :
-    x1 ⊆ x2 → y1 ⊆ y2 →
-    x1 ∪ y1 ⊆ x2 ∪ y2.
+  Lemma union_preserving x1 x2 y1 y2 : x1 ⊆ x2 → y1 ⊆ y2 → x1 ∪ y1 ⊆ x2 ∪ y2.
   Proof. auto. Qed.
 
   Lemma union_empty x : x ∪ ∅ ⊆ x.
   Proof. by apply union_least. Qed.
-  Lemma union_comm_1 x y : x ∪ y ⊆ y ∪ x.
+  Lemma union_commutative_1 x y : x ∪ y ⊆ y ∪ x.
   Proof. auto. Qed.
-  Lemma union_assoc_1 x y z : (x ∪ y) ∪ z ⊆ x ∪ (y ∪ z).
+  Lemma union_associative_1 x y z : (x ∪ y) ∪ z ⊆ x ∪ (y ∪ z).
   Proof. auto. Qed.
-  Lemma union_assoc_2 x y z : x ∪ (y ∪ z) ⊆ (x ∪ y) ∪ z.
+  Lemma union_associative_2 x y z : x ∪ (y ∪ z) ⊆ (x ∪ y) ∪ z.
   Proof. auto. Qed.
 
   Global Instance union_proper: Proper ((≡) ==> (≡) ==> (≡)) (∪).
@@ -155,9 +148,9 @@ Section bounded_join_sl.
   Global Instance: RightId (≡) ∅ (∪).
   Proof. split; eauto. Qed.
   Global Instance: Commutative (≡) (∪).
-  Proof. split; apply union_comm_1. Qed.
+  Proof. split; apply union_commutative_1. Qed.
   Global Instance: Associative (≡) (∪).
-  Proof. split. apply union_assoc_2. apply union_assoc_1. Qed.
+  Proof. split. apply union_associative_2. apply union_associative_1. Qed.
 
   Lemma subseteq_union X Y : X ⊆ Y ↔ X ∪ Y ≡ Y.
   Proof. repeat split; eauto. intros E. rewrite <-E. auto. Qed.
@@ -169,8 +162,7 @@ Section bounded_join_sl.
   Lemma equiv_empty X : X ⊆ ∅ → X ≡ ∅.
   Proof. split; eauto. Qed.
 
-  Global Instance union_list_proper:
-    Proper (eqlistA (≡) ==> (≡)) union_list.
+  Global Instance union_list_proper: Proper (Forall2 (≡) ==> (≡)) union_list.
   Proof.
     induction 1; simpl.
     * done.
@@ -189,17 +181,13 @@ Section bounded_join_sl.
     * by rewrite (left_id ∅ _).
     * by rewrite IH, (associative _).
   Qed.
-  Lemma union_list_reverse (Xs : list A) :
-    ⋃ (reverse Xs) ≡ ⋃ Xs.
+  Lemma union_list_reverse (Xs : list A) : ⋃ (reverse Xs) ≡ ⋃ Xs.
   Proof.
     induction Xs as [|X Xs IH]; simpl; [done |].
     by rewrite reverse_cons, union_list_app,
       union_list_singleton, (commutative _), IH.
   Qed.
-
-  Lemma union_list_preserving (Xs Ys : list A) :
-    Forall2 (⊆) Xs Ys →
-    ⋃ Xs ⊆ ⋃ Ys.
+  Lemma union_list_preserving (Xs Ys : list A) : Xs ⊆* Ys → ⋃ Xs ⊆ ⋃ Ys.
   Proof. induction 1; simpl; auto using union_preserving. Qed.
 
   Lemma empty_union X Y : X ∪ Y ≡ ∅ ↔ X ≡ ∅ ∧ Y ≡ ∅.
@@ -282,24 +270,19 @@ Section meet_sl.
   Proof. intros. transitivity x1; auto. Qed.
   Hint Resolve intersection_subseteq_l_alt intersection_subseteq_r_alt.
 
-  Lemma intersection_preserving_l x y1 y2 :
-    y1 ⊆ y2 →
-    x ∩ y1 ⊆ x ∩ y2.
+  Lemma intersection_preserving_l x y1 y2 : y1 ⊆ y2 → x ∩ y1 ⊆ x ∩ y2.
   Proof. auto. Qed.
-  Lemma intersection_preserving_r x1 x2 y :
-    x1 ⊆ x2 →
-    x1 ∩ y ⊆ x2 ∩ y.
+  Lemma intersection_preserving_r x1 x2 y : x1 ⊆ x2 → x1 ∩ y ⊆ x2 ∩ y.
   Proof. auto. Qed.
   Lemma intersection_preserving x1 x2 y1 y2 :
-    x1 ⊆ x2 → y1 ⊆ y2 →
-    x1 ∩ y1 ⊆ x2 ∩ y2.
+    x1 ⊆ x2 → y1 ⊆ y2 → x1 ∩ y1 ⊆ x2 ∩ y2.
   Proof. auto. Qed.
 
-  Lemma intersection_comm_1 x y : x ∩ y ⊆ y ∩ x.
+  Lemma intersection_commutative_1 x y : x ∩ y ⊆ y ∩ x.
   Proof. auto. Qed.
-  Lemma intersection_assoc_1 x y z : (x ∩ y) ∩ z ⊆ x ∩ (y ∩ z).
+  Lemma intersection_associative_1 x y z : (x ∩ y) ∩ z ⊆ x ∩ (y ∩ z).
   Proof. auto. Qed.
-  Lemma intersection_assoc_2 x y z : x ∩ (y ∩ z) ⊆ (x ∩ y) ∩ z.
+  Lemma intersection_associative_2 x y z : x ∩ (y ∩ z) ⊆ (x ∩ y) ∩ z.
   Proof. auto. Qed.
 
   Global Instance: Proper ((≡) ==> (≡) ==> (≡)) (∩).
@@ -310,9 +293,11 @@ Section meet_sl.
   Global Instance: Idempotent (≡) (∩).
   Proof. split; eauto. Qed.
   Global Instance: Commutative (≡) (∩).
-  Proof. split; apply intersection_comm_1. Qed.
+  Proof. split; apply intersection_commutative_1. Qed.
   Global Instance: Associative (≡) (∩).
-  Proof. split. apply intersection_assoc_2. apply intersection_assoc_1. Qed.
+  Proof.
+    split. apply intersection_associative_2. apply intersection_associative_1.
+  Qed.
 
   Lemma subseteq_intersection X Y : X ⊆ Y ↔ X ∩ Y ≡ X.
   Proof. repeat split; eauto. intros E. rewrite <-E. auto. Qed.
diff --git a/theories/pmap.v b/theories/pmap.v
index 9b1ec6eb0c5c72ec79fcf7741a1f88054cd8d58f..f09b7ff94179f0ffebe13c00fce6d123cf5151cb 100644
--- a/theories/pmap.v
+++ b/theories/pmap.v
@@ -7,7 +7,7 @@ trees (uncompressed Patricia trees) and guarantees logarithmic-time operations.
 However, we extend Leroy's implementation by packing the trees into a Sigma
 type such that canonicity of representation is ensured. This is necesarry for
 Leibniz equality to become extensional. *)
-Require Import PArith.
+Require Import PArith mapset.
 Require Export prelude fin_maps.
 
 Local Open Scope positive_scope.
@@ -108,7 +108,7 @@ Lemma Pmap_wf_eq_get {A} (t1 t2 : Pmap_raw A) :
   Pmap_wf t1 → Pmap_wf t2 → (∀ i, t1 !! i = t2 !! i) → t1 = t2.
 Proof.
   intros t1wf. revert t2.
-  induction t1wf as [| ? x ? ? IHl ? IHr | l r ? IHl ? IHr Hne1 ].
+  induction t1wf as [| ? x ? ? IHl ? IHr | l r ? IHl ? IHr Hne1].
   * destruct 1 as [| | ???? [?|?]]; intros Hget.
     + done.
     + discriminate (Hget 1).
@@ -125,11 +125,11 @@ Proof.
     + specialize (Hget 1). simpl in *. congruence.
   * destruct 1; intros Hget.
     + destruct Hne1.
-      destruct (Pmap_ne_lookup l) as [i [??]]; trivial.
-      - specialize (Hget (i~0)). simpl in *. congruence.
+      - destruct (Pmap_ne_lookup l) as [i [??]]; trivial.
+        specialize (Hget (i~0)); simpl in *. congruence.
       - destruct (Pmap_ne_lookup r) as [i [??]]; trivial.
-        specialize (Hget (i~1)). simpl in *. congruence.
-    + specialize (Hget 1). simpl in *. congruence.
+        specialize (Hget (i~1)); simpl in *. congruence.
+    + specialize (Hget 1); simpl in *. congruence.
     + f_equal.
       - apply IHl; trivial. intros i. apply (Hget (i~0)).
       - apply IHr; trivial. intros i. apply (Hget (i~1)).
@@ -176,8 +176,7 @@ Lemma Pnode_canon_lookup_xI `(l : Pmap_raw A) o (r : Pmap_raw A) i :
   Pnode_canon l o r !! i~1 = r !! i.
 Proof. by destruct l,o,r. Qed.
 Ltac Pnode_canon_rewrite := repeat (
-  rewrite Pnode_canon_lookup_xH ||
-  rewrite Pnode_canon_lookup_xO ||
+  rewrite Pnode_canon_lookup_xH || rewrite Pnode_canon_lookup_xO ||
   rewrite Pnode_canon_lookup_xI).
 
 Instance Ppartial_alter_raw {A} : PartialAlter positive A (Pmap_raw A) :=
@@ -262,7 +261,7 @@ Fixpoint Pto_list_raw {A} (j : positive) (t : Pmap_raw A) :
     list (positive * A) :=
   match t with
   | Pleaf => []
-  | Pnode l o r => option_case (λ x, [(Preverse j, x)]) [] o ++ 
+  | Pnode l o r => default [] o (λ x, [(Preverse j, x)]) ++ 
      Pto_list_raw (j~0) l ++ Pto_list_raw (j~1) r
   end%list.
 
@@ -273,30 +272,30 @@ Proof.
   * revert j. induction t as [|? IHl [?|] ? IHr]; intros j; simpl.
     + by rewrite ?elem_of_nil.
     + rewrite elem_of_cons, !elem_of_app. intros [?|[?|?]].
-      - simplify_equality. exists 1. by rewrite (left_id 1 (++))%positive.
+      - simplify_equality. exists 1. by rewrite (left_id_L 1 (++))%positive.
       - destruct (IHl (j~0)) as (i' &?&?); trivial; subst.
-         exists (i' ~ 0). by rewrite Preverse_xO, (associative _).
+         exists (i' ~ 0). by rewrite Preverse_xO, (associative_L _).
       - destruct (IHr (j~1)) as (i' &?&?); trivial; subst.
-         exists (i' ~ 1). by rewrite Preverse_xI, (associative _).
+         exists (i' ~ 1). by rewrite Preverse_xI, (associative_L _).
     + rewrite !elem_of_app. intros [?|?].
       - destruct (IHl (j~0)) as (i' &?&?); trivial; subst.
-         exists (i' ~ 0). by rewrite Preverse_xO, (associative _).
+         exists (i' ~ 0). by rewrite Preverse_xO, (associative_L _).
       - destruct (IHr (j~1)) as (i' &?&?); trivial; subst.
-         exists (i' ~ 1). by rewrite Preverse_xI, (associative _).
+         exists (i' ~ 1). by rewrite Preverse_xI, (associative_L _).
   * intros (i' & ?& Hi'); subst. revert i' j Hi'.
     induction t as [|? IHl [?|] ? IHr]; intros i j; simpl.
     + done.
     + rewrite elem_of_cons, elem_of_app. destruct i as [i|i|]; simpl in *.
       - right. right. specialize (IHr i (j~1)).
-        rewrite Preverse_xI, (associative_eq _) in IHr. auto.
+        rewrite Preverse_xI, (associative_L _) in IHr. auto.
       - right. left. specialize (IHl i (j~0)).
-        rewrite Preverse_xO, (associative_eq _) in IHl. auto.
-      - left. simplify_equality. by rewrite (left_id_eq 1 (++))%positive.
+        rewrite Preverse_xO, (associative_L _) in IHl. auto.
+      - left. simplify_equality. by rewrite (left_id_L 1 (++))%positive.
     + rewrite elem_of_app. destruct i as [i|i|]; simpl in *.
       - right. specialize (IHr i (j~1)).
-        rewrite Preverse_xI, (associative_eq _) in IHr. auto.
+        rewrite Preverse_xI, (associative_L _) in IHr. auto.
       - left. specialize (IHl i (j~0)).
-        rewrite Preverse_xO, (associative_eq _) in IHl. auto.
+        rewrite Preverse_xO, (associative_L _) in IHl. auto.
       - done.
 Qed.
 Lemma Pelem_of_to_list_raw {A} (t : Pmap_raw A) i x :
@@ -320,12 +319,12 @@ Proof.
         rewrite !Papp_length in Hi. simpl in Hi. lia.
     + intros [??]. rewrite !Pelem_of_to_list_raw_aux.
       intros (i1&?&?) (i2&Hi&?); subst.
-      rewrite Preverse_xO, Preverse_xI, !(associative_eq _) in Hi.
+      rewrite Preverse_xO, Preverse_xI, !(associative_L _) in Hi.
       by apply (injective (++ _)) in Hi.
   * intros. rewrite NoDup_app. split_ands; trivial.
     intros [??]. rewrite !Pelem_of_to_list_raw_aux.
     intros (i1&?&?) (i2&Hi&?); subst.
-    rewrite Preverse_xO, Preverse_xI, !(associative_eq _) in Hi.
+    rewrite Preverse_xO, Preverse_xI, !(associative_L _) in Hi.
     by apply (injective (++ _)) in Hi.
 Qed.
 
@@ -394,3 +393,8 @@ Proof.
   * intros ? [??]. apply Pelem_of_to_list_raw.
   * intros ??? ?? [??] [??] ?. by apply Pmerge_raw_spec.
 Qed.
+
+(** Finally, we can construct sets of [positive]s satisfying extensional equality. *)
+Notation Pset := (mapset Pmap).
+Instance Pmap_dom {A} : Dom (Pmap A) Pset := mapset_dom.
+Instance: FinMapDom positive Pmap Pset := mapset_dom_spec.
diff --git a/theories/tactics.v b/theories/tactics.v
index a16cb29377dfee4a3e60641cd1ab807799501636..150d32d05fb0afc0a45edce8192cecc17fa2bfbe 100644
--- a/theories/tactics.v
+++ b/theories/tactics.v
@@ -24,26 +24,21 @@ unfolding setoid equalities. Note that this tactic performs much better than
 Coq's [easy] tactic as it does not perform [inversion]. *)
 Ltac done :=
   trivial; intros; solve
-    [ repeat first
-      [ solve [trivial]
-      | solve [symmetry; trivial]
-      | reflexivity
-      | discriminate
-      | contradiction
-      | solve [apply not_symmetry; trivial]
-      | split ]
-    | match goal with
-      H : ¬_ |- _ => solve [destruct H; trivial]
-      end ].
+  [ repeat first
+    [ solve [trivial]
+    | solve [symmetry; trivial]
+    | reflexivity
+    | discriminate
+    | contradiction
+    | solve [apply not_symmetry; trivial]
+    | split ]
+  | match goal with H : ¬_ |- _ => solve [destruct H; trivial] end ].
 Tactic Notation "by" tactic(tac) :=
   tac; done.
 
 (** Whereas the [split] tactic splits any inductive with one constructor, the
 tactic [split_and] only splits a conjunction. *)
-Ltac split_and :=
-  match goal with
-  | |- _ ∧ _ => split
-  end.
+Ltac split_and := match goal with |- _ ∧ _ => split end.
 Ltac split_ands := repeat split_and.
 
 (** The tactic [case_match] destructs an arbitrary match in the conclusion or
@@ -144,16 +139,12 @@ is already blocked, it will not be blocked again. The tactic [unblock_hyps]
 removes [blocked] everywhere. *)
 
 Ltac block_hyps := repeat_on_hyps (fun H =>
-  match type of H with
-  | block _ => idtac
-  | ?T => change (block T) in H
-  end).
+  match type of H with block _ => idtac | ?T => change (block T) in H end).
 Ltac unblock_hyps := unfold block in * |-.
 
 (** The tactic [injection' H] is a variant of injection that introduces the
 generated equalities. *)
-Ltac injection' H :=
-  block_goal; injection H; clear H; intros; unblock_goal.
+Ltac injection' H := block_goal; injection H; clear H; intros; unblock_goal.
 
 (** The tactic [simplify_equality] repeatedly substitutes, discriminates,
 and injects equalities, and tries to contradict impossible inequalities. *)
@@ -165,6 +156,7 @@ Ltac simplify_equality := repeat
   | H : _ = ?x |- _ => subst x
   | H : _ = _ |- _ => discriminate H
   | H : ?f _ = ?f _ |- _ => apply (injective f) in H
+  | H : ?f _ _ = ?f _ _ |- _ => apply (injective2 f) in H; destruct H
     (* before [injection'] to circumvent bug #2939 in some situations *)
   | H : _ = _ |- _ => injection' H
   | H : ?x = ?x |- _ => clear H
@@ -174,18 +166,14 @@ Ltac simplify_equality := repeat
 equality. The following tactic extends [remember] to do so. *)
 Tactic Notation "remember" constr(t) "as" "(" ident(x) "," ident(E) ")" :=
   remember t as x;
-  match goal with
-  | E' : x = _ |- _ => rename E' into E
-  end.
+  match goal with E' : x = _ |- _ => rename E' into E end.
 
 (** Given a tactic [tac2] generating a list of terms, [iter tac1 tac2]
 runs [tac x] for each element [x] until [tac x] succeeds. If it does not
 suceed for any element of the generated list, the whole tactic wil fail. *)
 Tactic Notation "iter" tactic(tac) tactic(l) :=
   let rec go l :=
-  match l with
-  | ?x :: ?l => tac x || go l
-  end in go l.
+  match l with ?x :: ?l => tac x || go l end in go l.
 
 (** Given H : [A_1 → ... → A_n → B] (where each [A_i] is non-dependent), the
 tactic [feed tac H tac_by] creates a subgoal for each [A_i] and calls [tac p]
diff --git a/theories/vector.v b/theories/vector.v
index a7dd52ee60f51c35147e95f09e3b6071a094a300..8c726a5daf8e79d8cced00547f52201d8c858d17 100644
--- a/theories/vector.v
+++ b/theories/vector.v
@@ -21,16 +21,11 @@ Notation FS := Fin.FS.
 Delimit Scope fin_scope with fin.
 Arguments Fin.FS _ _%fin.
 
-Notation "0" := Fin.F1 : fin_scope.
-Notation "1" := (FS 0) : fin_scope.
-Notation "2" := (FS 1) : fin_scope.
-Notation "3" := (FS 2) : fin_scope.
-Notation "4" := (FS 3) : fin_scope.
-Notation "5" := (FS 4) : fin_scope.
-Notation "6" := (FS 5) : fin_scope.
-Notation "7" := (FS 6) : fin_scope.
-Notation "8" := (FS 7) : fin_scope.
-Notation "9" := (FS 8) : fin_scope.
+Notation "0" := Fin.F1 : fin_scope. Notation "1" := (FS 0) : fin_scope.
+Notation "2" := (FS 1) : fin_scope. Notation "3" := (FS 2) : fin_scope.
+Notation "4" := (FS 3) : fin_scope. Notation "5" := (FS 4) : fin_scope.
+Notation "6" := (FS 5) : fin_scope. Notation "7" := (FS 6) : fin_scope.
+Notation "8" := (FS 7) : fin_scope. Notation "9" := (FS 8) : fin_scope.
 Notation "10" := (FS 9) : fin_scope.
 
 Fixpoint fin_to_nat {n} (i : fin n) : nat :=
@@ -76,14 +71,10 @@ Ltac inv_fin i :=
   match type of i with
   | fin 0 =>
     revert dependent i;
-    match goal with
-    |- ∀ i, @?P i => apply (fin_0_inv P)
-    end
+    match goal with |- ∀ i, @?P i => apply (fin_0_inv P) end
   | fin (S ?n) =>
     revert dependent i;
-    match goal with
-    |- ∀ i, @?P i => apply (fin_S_inv P)
-    end
+    match goal with |- ∀ i, @?P i => apply (fin_S_inv P) end
   end.
 
 (** * Vectors *)
@@ -117,13 +108,10 @@ Ltac vec_double_ind v1 v2 :=
   match type of v1 with
   | vec _ ?n =>
     repeat match goal with
-    | H' : context [ n ] |- _ =>
-      var_neq v1 H'; var_neq v2 H'; revert H'
+    | H' : context [ n ] |- _ => var_neq v1 H'; var_neq v2 H'; revert H'
     end;
     revert n v1 v2;
-    match goal with
-    | |- ∀ n v1 v2, @?P n v1 v2 => apply (vec_rect2 P)
-    end
+    match goal with |- ∀ n v1 v2, @?P n v1 v2 => apply (vec_rect2 P) end
   end.
 
 Notation vcons_inj := VectorSpec.cons_inj.
@@ -132,8 +120,7 @@ Proof. apply vcons_inj. Qed.
 Lemma vcons_inj_2 {A n} x y (v w : vec A n) : x ::: v = y ::: w → v = w.
 Proof. apply vcons_inj. Qed.
 
-Lemma vec_eq {A n} (v w : vec A n) :
-  (∀ i, v !!! i = w !!! i) → v = w.
+Lemma vec_eq {A n} (v w : vec A n) : (∀ i, v !!! i = w !!! i) → v = w.
 Proof.
   vec_double_ind v w; [done|]. intros n v w IH x y Hi. f_equal.
   * apply (Hi 0%fin).
@@ -168,14 +155,10 @@ Ltac inv_vec v :=
   match type of v with
   | vec _ 0 =>
     revert dependent v;
-    match goal with
-    |- ∀ v, @?P v => apply (vec_0_inv P)
-    end
+    match goal with |- ∀ v, @?P v => apply (vec_0_inv P) end
   | vec _ (S ?n) =>
     revert dependent v;
-    match goal with
-    |- ∀ v, @?P v => apply (vec_S_inv P)
-    end
+    match goal with |- ∀ v, @?P v => apply (vec_S_inv P) end
   end.
 
 (** The following tactic performs case analysis on all hypotheses of the shape
@@ -213,7 +196,7 @@ Proof. by induction l; simpl; f_equal. Qed.
 Lemma vec_to_list_length {A n} (v : vec A n) : length (vec_to_list v) = n.
 Proof. induction v; simpl; by f_equal. Qed.
 Lemma vec_to_list_same_length {A B n} (v : vec A n) (w : vec B n) :
-  same_length v w.
+  v `same_length` w.
 Proof. apply same_length_length. by rewrite !vec_to_list_length. Qed.
 
 Lemma vec_to_list_inj1 {A n m} (v : vec A n) (w : vec A m) :
@@ -253,12 +236,10 @@ Qed.
 Lemma vec_to_list_drop_lookup {A n} (v : vec A n) (i : fin n) :
   drop i v = v !!! i :: drop (S i) v.
 Proof. induction i; inv_vec v; simpl; intros; [done | by rewrite IHi]. Qed.
-
 Lemma vec_to_list_take_drop_lookup {A n} (v : vec A n) (i : fin n) :
   vec_to_list v = take i v ++ v !!! i :: drop (S i) v.
 Proof.
-  rewrite <-(take_drop i v) at 1. f_equal.
-  apply vec_to_list_drop_lookup.
+  rewrite <-(take_drop i v) at 1. f_equal. apply vec_to_list_drop_lookup.
 Qed. 
 
 Lemma elem_of_vlookup {A n} (v : vec A n) x :
@@ -273,31 +254,21 @@ Proof.
     + by left.
     + right. apply IHv.
 Qed.
-
 Lemma Forall_vlookup {A} (P : A → Prop) {n} (v : vec A n) :
   Forall P (vec_to_list v) ↔ ∀ i, P (v !!! i).
-Proof.
-  rewrite Forall_forall.
-  setoid_rewrite elem_of_vlookup. naive_solver.
-Qed.
+Proof. rewrite Forall_forall. setoid_rewrite elem_of_vlookup. naive_solver. Qed.
 Lemma Forall_vlookup_1 {A} (P : A → Prop) {n} (v : vec A n) i :
   Forall P (vec_to_list v) → P (v !!! i).
 Proof. by rewrite Forall_vlookup. Qed.
 Lemma Forall_vlookup_2 {A} (P : A → Prop) {n} (v : vec A n) :
   (∀ i, P (v !!! i)) → Forall P (vec_to_list v).
 Proof. by rewrite Forall_vlookup. Qed.
-
 Lemma Exists_vlookup {A} (P : A → Prop) {n} (v : vec A n) :
   Exists P (vec_to_list v) ↔ ∃ i, P (v !!! i).
-Proof.
-  rewrite Exists_exists.
-  setoid_rewrite elem_of_vlookup. naive_solver.
-Qed.
-
-Lemma Forall2_vlookup {A B} (P : A → B → Prop)
-    {n} (v1 : vec A n) (v2 : vec B n) :
-  Forall2 P (vec_to_list v1) (vec_to_list v2) ↔
-    ∀ i, P (v1 !!! i) (v2 !!! i).
+Proof. rewrite Exists_exists. setoid_rewrite elem_of_vlookup. naive_solver. Qed.
+Lemma Forall2_vlookup {A B} (P : A → B → Prop) {n}
+    (v1 : vec A n) (v2 : vec B n) :
+  Forall2 P (vec_to_list v1) (vec_to_list v2) ↔ ∀ i, P (v1 !!! i) (v2 !!! i).
 Proof.
   split.
   * vec_double_ind v1 v2.
@@ -310,13 +281,12 @@ Proof.
       constructor. apply (H 0%fin). apply IH, (λ i, H (FS i)).
 Qed.
 
-(** The function [vmap f v] applies a funlocks (mem_unlock (⋃ Ωs) (⋃ ms)) ≡ ∅ction [f] element wise to [v]. *)
+(** The function [vmap f v] applies a function [f] element wise to [v]. *)
 Notation vmap := Vector.map.
 
 Lemma vlookup_map `(f : A → B) {n} (v : vec A n) i :
   vmap f v !!! i = f (v !!! i).
 Proof. by apply Vector.nth_map. Qed.
-
 Lemma vec_to_list_map `(f : A → B) {n} (v : vec A n) :
   vec_to_list (vmap f v) = f <$> vec_to_list v.
 Proof. induction v; simpl. done. by rewrite IHv. Qed.
@@ -328,15 +298,12 @@ Notation vzip_with := Vector.map2.
 Lemma vlookup_zip_with `(f : A → B → C) {n} (v1 : vec A n) (v2 : vec B n) i :
   vzip_with f v1 v2 !!! i = f (v1 !!! i) (v2 !!! i).
 Proof. by apply Vector.nth_map2. Qed.
-
-Lemma vec_to_list_zip_with `(f : A → B → C) {n}
-    (v1 : vec A n) (v2 : vec B n) :
+Lemma vec_to_list_zip_with `(f : A → B → C) {n} (v1 : vec A n) (v2 : vec B n) :
   vec_to_list (vzip_with f v1 v2) =
     zip_with f (vec_to_list v1) (vec_to_list v2).
 Proof.
-  revert v2. induction v1; intros v2; inv_vec v2; intros; simpl.
-  * done.
-  * by rewrite IHv1.
+  revert v2. induction v1; intros v2; inv_vec v2; intros; simpl; [done|].
+  by rewrite IHv1.
 Qed.
 
 (** Similar to vlookup, we cannot define [vinsert] as an instance of the
@@ -350,13 +317,13 @@ Fixpoint vinsert {A n} (i : fin n) (x : A) : vec A n → vec A n :=
 Lemma vec_to_list_insert {A n} i x (v : vec A n) :
   vec_to_list (vinsert i x v) = insert (fin_to_nat i) x (vec_to_list v).
 Proof. induction v; inv_fin i. done. simpl. intros. by rewrite IHv. Qed.
-
 Lemma vlookup_insert {A n} i x (v : vec A n) : vinsert i x v !!! i = x.
 Proof. by induction i; inv_vec v. Qed.
-
 Lemma vlookup_insert_ne {A n} i j x (v : vec A n) :
   i ≠ j → vinsert i x v !!! j = v !!! j.
 Proof.
   induction i; inv_fin j; inv_vec v; simpl; try done.
   intros. apply IHi. congruence.
 Qed.
+Lemma vlookup_insert_self {A n} i (v : vec A n) : vinsert i (v !!! i) v = v.
+Proof. by induction v; inv_fin i; simpl; intros; f_equal. Qed.