Library stdpp.fin_maps

Finite maps associate data to keys. This file defines an interface for finite maps and collects some theory on it. Most importantly, it proves useful induction principles for finite maps and implements the tactic simplify_map_eq to simplify goals involving finite maps.
From Coq Require Import Permutation.
From stdpp Require Export relations orders vector fin_sets.
From stdpp Require Import options.

Unset Default Proof Using.

Axiomatization of finite maps

We require Leibniz equality of finite maps to be extensional, i.e., to enjoy (∀ i, m1 !! i = m2 !! i) m1 = m2. This is a very useful property as it avoids the need for setoid rewriting in proof. However, it comes at the cost of restricting what map implementations we support. Since Coq does not have quotient types, it rules out balanced search trees (AVL, red-black, etc.). We do provide a reasonably efficient implementation of binary tries (see gmap and Pmap).
Finiteness is axiomatized through a fold operation map_fold f b m, which folds a function f over each element of the map m. The order in which the elements are passed to f is unspecified.

Class MapFold K A M := map_fold B : (K A B B) B M B.
Global Arguments map_fold {_ _ _ _ _} _ _ _.
Global Hint Mode MapFold - - ! : typeclass_instances.
Global Hint Mode MapFold ! - - : typeclass_instances.

Make sure that map_fold (and definitions based on it) are not unfolded too eagerly by unification. See only_evens_Some in tests/pmap_gmap for an example. We use level 1 because it is the least level for which the test works.
Global Strategy 1 [map_fold].

Finite map implementations are required to implement the merge function which enables us to give a generic implementation of union_with, intersection_with, and difference_with.
The function diag_None f is used in the specification and lemmas of merge f. It lifts a function f : option A option B option C by returning None if both arguments are None, to make sure that in merge f m1 m2, the function f can only operate on elements that are in the domain of either m1 or m2.
Definition diag_None {A B C} (f : option A option B option C)
    (mx : option A) (my : option B) : option C :=
  match mx, my with None, NoneNone | _, _f mx my end.

We need the insert operation as part of the map_fold_ind rule in the FinMap interface. Hence we define it before the other derived operations.
Global Instance map_insert `{PartialAlter K A M} : Insert K A M :=
  λ i x, partial_alter (λ _, Some x) i.

Class FinMap K M `{FMap M, A, Lookup K A (M A), A, Empty (M A), A,
    PartialAlter K A (M A), OMap M, Merge M, A, MapFold K A (M A),
    EqDecision K} := {
  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;
  lookup_omap {A B} (f : A option B) (m : M A) i :
    omap f m !! i = m !! i ≫= f;
  lookup_merge {A B C} (f : option A option B option C) (m1 : M A) (m2 : M B) i :
    merge f m1 m2 !! i = diag_None f (m1 !! i) (m2 !! i);
  map_fold_ind {A B} (P : B M A Prop) (f : K A B B) (b : B) :
    P b
    ( i x m r, m !! i = None P r m P (f i x r) (<[i:=x]> m))
     m, P (map_fold f b m) m
}.

Derived operations

All of the following functions are defined in a generic way for arbitrary finite map implementations. These generic implementations do not cause a significant performance loss, which justifies including them in the finite map interface as primitive operations.
Global Instance map_alter `{PartialAlter K A M} : Alter K A M :=
  λ f, partial_alter (fmap f).
Global Instance map_delete `{PartialAlter K A M} : Delete K M :=
  partial_alter (λ _, None).
Global Instance map_singleton `{PartialAlter K A M, Empty M} :
  SingletonM K A M := λ i x, <[i:=x]> .

Definition list_to_map `{Insert K A M, Empty M} : list (K × A) M :=
  fold_right (λ p, <[p.1:=p.2]>) .

Global Instance map_size `{MapFold K A M} : Size M :=
  map_fold (λ _ _, S) 0.
Definition map_to_list `{MapFold K A M} : M list (K × A) :=
  map_fold (λ i x, ((i,x) ::.)) [].

Definition map_to_set `{MapFold K A M,
    Singleton B C, Empty C, Union C} (f : K A B) (m : M) : C :=
  list_to_set (uncurry f <$> map_to_list m).
Definition set_to_map `{Elements B C, Insert K A M, Empty M}
    (f : B K × A) (X : C) : M :=
  list_to_map (f <$> elements X).

Global Instance map_union_with `{Merge M} {A} : UnionWith A (M A) :=
  λ f, merge (union_with f).
Global Instance map_intersection_with `{Merge M} {A} : IntersectionWith A (M A) :=
  λ f, merge (intersection_with f).
Global Instance map_difference_with `{Merge M} {A} : DifferenceWith A (M A) :=
  λ f, merge (difference_with f).

Higher precedence to make sure it's not used for other types with a Lookup instance, such as lists.
Global Instance map_equiv `{ A, Lookup K A (M A), Equiv A} : Equiv (M A) | 20 :=
  λ m1 m2, i, m1 !! i m2 !! i.

Definition map_Forall `{Lookup K A M} (P : K A Prop) : M Prop :=
  λ m, i x, m !! i = Some x P i x.

Definition map_Exists `{Lookup K A M} (P : K A Prop) : M Prop :=
  λ m, i x, m !! i = Some x P i x.

Definition map_relation `{ A, Lookup K A (M A)} {A B} (R : A B Prop)
    (P : A Prop) (Q : B Prop) (m1 : M A) (m2 : M B) : Prop := i,
  option_relation R P Q (m1 !! i) (m2 !! i).
Definition map_included `{ A, Lookup K A (M A)} {A}
  (R : relation A) : relation (M A) := map_relation R (λ _, False) (λ _, True).
Definition map_agree `{ A, Lookup K A (M A)} {A} : relation (M A) :=
  map_relation (=) (λ _, True) (λ _, True).
Definition map_disjoint `{ A, Lookup K A (M A)} {A} : relation (M A) :=
  map_relation (λ _ _, False) (λ _, True) (λ _, True).
Infix "##ₘ" := map_disjoint (at level 70) : stdpp_scope.
Global Hint Extern 0 (_ ##ₘ _) ⇒ symmetry; eassumption : core.
Notation "( m ##ₘ.)" := (map_disjoint m) (only parsing) : stdpp_scope.
Notation "(.##ₘ m )" := (λ m2, m2 ##ₘ m) (only parsing) : stdpp_scope.
Global Instance map_subseteq `{ A, Lookup K A (M A)} {A} : SubsetEq (M A) :=
  map_included (=).

The union of two finite maps only has a meaningful definition for maps 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.
Global Instance map_union `{Merge M} {A} : Union (M A) := union_with (λ x _, Some x).
Global Instance map_intersection `{Merge M} {A} : Intersection (M A) :=
  intersection_with (λ x _, Some x).

The difference operation removes all values from the first map whose index contains a value in the second map as well.
Global Instance map_difference `{Merge M} {A} : Difference (M A) :=
  difference_with (λ _ _, None).

A stronger variant of map that allows the mapped function to use the index of the elements. Implemented by conversion to lists, so not very efficient.
Definition map_imap `{ A, Insert K A (M A), A, Empty (M A),
     A, MapFold K A (M A)} {A B} (f : K A option B) (m : M A) : M B :=
  list_to_map (omap (λ ix, (fst ix ,.) <$> uncurry f ix) (map_to_list m)).

Given a function f : K1 K2, the function kmap f turns a maps with keys of type K1 into a map with keys of type K2. The function kmap f is only well-behaved if f is injective, as otherwise it could map multiple entries into the same entry. All lemmas about kmap f thus have the premise Inj (=) (=) f.
Definition kmap `{ A, Insert K2 A (M2 A), A, Empty (M2 A),
     A, MapFold K1 A (M1 A)} {A} (f : K1 K2) (m : M1 A) : M2 A :=
  list_to_map (fmap (prod_map f id) (map_to_list m)).

Definition map_zip_with `{Merge M} {A B C} (f : A B C) : M A M B M C :=
  merge (λ mx my,
    match mx, my with Some x, Some ySome (f x y) | _, _None end).
Notation map_zip := (map_zip_with pair).

Global Instance map_filter
    `{MapFold K A M, Insert K A M, Empty M} : Filter (K × A) M :=
  λ P _, map_fold (λ k v m, if decide (P (k,v)) then <[k := v]>m else m) .

Fixpoint map_seq `{Insert nat A M, Empty M} (start : nat) (xs : list A) : M :=
  match xs with
  | []
  | x :: xs<[start:=x]> (map_seq (S start) xs)
  end.

Fixpoint map_seqZ `{Insert Z A M, Empty M} (start : Z) (xs : list A) : M :=
  match xs with
  | []
  | x :: xs<[start:=x]> (map_seqZ (Z.succ start) xs)
  end.

Global Instance map_lookup_total `{!Lookup K A (M A), !Inhabited A} :
  LookupTotal K A (M A) | 20 := λ i m, default inhabitant (m !! i).
Global Typeclasses Opaque map_lookup_total.

Given a finite map m : M with keys K and values A, the image map_img m gives a finite set containing with the values A of m. The type of map_img is generic to support different map and set implementations. A possible instance is SA:=gset A.
Definition map_img `{MapFold K A M,
  Singleton A SA, Empty SA, Union SA} : M SA := map_to_set (λ _ x, x).
Global Typeclasses Opaque map_img.

Given a finite map m with keys K and values A, the preimage map_preimg m gives a finite map with keys A and values being sets of K. The type of map_preimg is very generic to support different map and set implementations. A possible instance is MKA:=gmap K A, MASK:=gmap A (gset K), and SK:=gset K.
Definition map_preimg `{MapFold K A MKA, Empty MASK,
    PartialAlter A SK MASK, Empty SK, Singleton K SK, Union SK}
    (m : MKA) : MASK :=
  map_fold (λ i, partial_alter (λ mX, Some $ {[ i ]} default mX)) m.
Global Typeclasses Opaque map_preimg.

Definition map_compose `{OMap MA, Lookup B C MB}
  (m : MB) (n : MA B) : MA C := omap (m !!.) n.

Infix "∘ₘ" := map_compose (at level 65, right associativity) : stdpp_scope.
Notation "(∘ₘ)" := map_compose (only parsing) : stdpp_scope.
Notation "( m ∘ₘ.)" := (map_compose m) (only parsing) : stdpp_scope.
Notation "(.∘ₘ m )" := (λ n, map_compose n m) (only parsing) : stdpp_scope.

Theorems

Section theorems.
Context `{FinMap K M}.

General properties

Lemma map_eq_iff {A} (m1 m2 : M A) : m1 = m2 i, m1 !! i = m2 !! i.
Proof. split; [by intros ->|]. apply map_eq. Qed.
Lemma map_subseteq_spec {A} (m1 m2 : M A) :
  m1 m2 i x, m1 !! i = Some x m2 !! i = Some x.
Proof.
  unfold subseteq, map_subseteq, map_relation. split; intros Hm i;
    specialize (Hm i); destruct (m1 !! i), (m2 !! i); naive_solver.
Qed.
Global Instance map_included_preorder {A} (R : relation A) :
  PreOrder R PreOrder (map_included R : relation (M A)).
Proof.
  split; [intros m i; by destruct (m !! i); simpl|].
  intros m1 m2 m3 Hm12 Hm23 i; specialize (Hm12 i); specialize (Hm23 i).
  destruct (m1 !! i), (m2 !! i), (m3 !! i); simplify_eq/=;
    done || etrans; eauto.
Qed.
Global Instance map_subseteq_po {A} : PartialOrder (⊆@{M A}).
Proof.
  split; [apply _|].
  intros m1 m2; rewrite !map_subseteq_spec.
  intros; apply map_eq; intros i; apply option_eq; naive_solver.
Qed.
Lemma lookup_total_alt `{!Inhabited A} (m : M A) i :
  m !!! i = default inhabitant (m !! i).
Proof. reflexivity. Qed.
Lemma lookup_total_correct `{!Inhabited A} (m : M A) i x :
  m !! i = Some x m !!! i = x.
Proof. rewrite lookup_total_alt. by intros →. Qed.
Lemma lookup_lookup_total `{!Inhabited A} (m : M A) i :
  is_Some (m !! i) m !! i = Some (m !!! i).
Proof. intros [x Hx]. by rewrite (lookup_total_correct m i x). Qed.
Lemma lookup_weaken {A} (m1 m2 : M A) i x :
  m1 !! i = Some x m1 m2 m2 !! i = Some x.
Proof. rewrite !map_subseteq_spec. auto. Qed.
Lemma lookup_weaken_is_Some {A} (m1 m2 : M A) i :
  is_Some (m1 !! i) m1 m2 is_Some (m2 !! i).
Proof. inv 1. eauto using lookup_weaken. Qed.
Lemma lookup_weaken_None {A} (m1 m2 : M A) i :
  m2 !! i = None m1 m2 m1 !! i = None.
Proof.
  rewrite map_subseteq_spec, !eq_None_not_Some.
  intros Hm2 Hm [??]; destruct Hm2; eauto.
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.
Lemma lookup_ne {A} (m : M A) i j : m !! i m !! j i j.
Proof. congruence. Qed.
Lemma map_empty {A} (m : M A) : m = i, m !! i = None.
Proof.
  split.
  - introsi. by rewrite lookup_empty.
  - intros Hm. apply map_eq. intros i. by rewrite Hm, lookup_empty.
Qed.
Lemma lookup_empty_is_Some {A} i : ¬is_Some (( : M A) !! i).
Proof. rewrite lookup_empty. by inv 1. Qed.
Lemma lookup_empty_Some {A} i (x : A) : ¬( : M A) !! i = Some x.
Proof. by rewrite lookup_empty. Qed.
Lemma lookup_total_empty `{!Inhabited A} i : ( : M A) !!! i = inhabitant.
Proof. by rewrite lookup_total_alt, lookup_empty. Qed.
Lemma map_subset_empty {A} (m : M A) : m .
Proof.
  intros [_ []]. rewrite map_subseteq_spec. intros ??. by rewrite lookup_empty.
Qed.
Lemma map_empty_subseteq {A} (m : M A) : m.
Proof. apply map_subseteq_spec. intros k v []%lookup_empty_Some. Qed.

NoDup_map_to_list and NoDup_map_to_list need to be proved mutually, hence a Local helper lemma.
Local Lemma map_to_list_spec {A} (m : M A) :
  NoDup (map_to_list m) ( i x, (i,x) map_to_list m m !! i = Some x).
Proof.
  apply (map_fold_ind (λ l m,
    NoDup l i x, (i,x) l m !! i = Some x)); clear m.
  { split; [constructor|]. intros i x. by rewrite elem_of_nil, lookup_empty. }
  intros i x m l ? [IH1 IH2]. split; [constructor; naive_solver|].
  intros j y. rewrite elem_of_cons, IH2.
  unfold insert, map_insert. destruct (decide (i = j)) as [->|].
  - rewrite lookup_partial_alter. naive_solver.
  - rewrite lookup_partial_alter_ne by done. naive_solver.
Qed.
Lemma NoDup_map_to_list {A} (m : M A) : NoDup (map_to_list m).
Proof. apply map_to_list_spec. Qed.
Lemma elem_of_map_to_list {A} (m : M A) i x :
  (i,x) map_to_list m m !! i = Some x.
Proof. apply map_to_list_spec. Qed.

Lemma map_subset_alt {A} (m1 m2 : M A) :
  m1 m2 m1 m2 i, m1 !! i = None is_Some (m2 !! i).
Proof.
  rewrite strict_spec_alt. split.
  - intros [? Heq]; split; [done|].
    destruct (decide (Exists (λ ix, m1 !! ix.1 = None) (map_to_list m2)))
      as [[[i x] [?%elem_of_map_to_list ?]]%Exists_exists
         |Hm%(not_Exists_Forall _)]; [eauto|].
    destruct Heq; apply (anti_symm (⊆)), map_subseteq_spec; [done|intros i x Hi].
    assert (is_Some (m1 !! i)) as [x' ?].
    { by apply not_eq_None_Some,
        (proj1 (Forall_forall _ _) Hm (i,x)), elem_of_map_to_list. }
    by rewrite <-(lookup_weaken_inv m1 m2 i x' x).
  - intros [? (i&?&x&?)]; split; [done|]. congruence.
Qed.

Properties of the partial_alter operation

Lemma partial_alter_ext {A} (f g : option A option A) (m : M A) i :
  ( x, m !! i = x f x = g x) partial_alter f i m = partial_alter g i m.
Proof.
  intros. apply map_eq; intros j. by destruct (decide (i = j)) as [->|?];
    rewrite ?lookup_partial_alter, ?lookup_partial_alter_ne; auto.
Qed.
Lemma partial_alter_compose {A} f g (m : M A) i:
  partial_alter (f g) i m = partial_alter f i (partial_alter g i m).
Proof.
  intros. apply map_eq. intros ii. by destruct (decide (i = ii)) as [->|?];
    rewrite ?lookup_partial_alter, ?lookup_partial_alter_ne.
Qed.
Lemma partial_alter_commute {A} f g (m : M A) i j :
  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)) as [->|?].
  { by rewrite lookup_partial_alter_ne,
      !lookup_partial_alter, lookup_partial_alter_ne. }
  destruct (decide (jj = i)) as [->|?].
  - by rewrite lookup_partial_alter,
     !lookup_partial_alter_ne, lookup_partial_alter by congruence.
  - by rewrite !lookup_partial_alter_ne by congruence.
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. by destruct (decide (i = ii)) as [->|];
    rewrite ?lookup_partial_alter, ?lookup_partial_alter_ne.
Qed.
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} f (m : M A) i :
  m !! i = None m partial_alter f i m.
Proof.
  rewrite map_subseteq_spec. intros Hi j x Hj.
  rewrite lookup_partial_alter_ne; congruence.
Qed.
Lemma partial_alter_subset {A} f (m : M A) i :
  m !! i = None is_Some (f (m !! i)) m partial_alter f i m.
Proof.
  intros Hi Hfi. apply map_subset_alt; split; [by apply partial_alter_subseteq|].
   i. by rewrite lookup_partial_alter.
Qed.
Lemma lookup_partial_alter_Some {A} (f : option A option A) (m : M A) i j x :
  partial_alter f i m !! j = Some x
    (i = j f (m !! i) = Some x) (i j m !! j = Some x).
Proof.
  destruct (decide (i = j)); subst.
  - rewrite lookup_partial_alter. naive_solver.
  - rewrite lookup_partial_alter_ne; naive_solver.
Qed.
Lemma lookup_total_partial_alter {A} `{Inhabited A}
    (f : option A option A) (m : M A) i:
  partial_alter f i m !!! i = default inhabitant (f (m !! i)).
Proof. by rewrite lookup_total_alt, lookup_partial_alter. Qed.

Properties of the alter operation

Lemma lookup_alter {A} (f : A A) (m : M A) i : alter f i m !! i = f <$> m !! i.
Proof. unfold alter. apply lookup_partial_alter. Qed.
Lemma lookup_alter_ne {A} (f : A A) (m : M A) i j :
  i j alter f i m !! j = m !! j.
Proof. unfold alter. apply lookup_partial_alter_ne. Qed.
Lemma alter_ext {A} (f g : A A) (m : M A) i :
  ( x, m !! i = Some x f x = g x) alter f i m = alter g i m.
Proof. intro. apply partial_alter_ext. intros [x|] ?; f_equal/=; auto. Qed.
Lemma alter_compose {A} (f g : A A) (m : M A) i:
  alter (f g) i m = alter f i (alter g i m).
Proof.
  unfold alter, map_alter. rewrite <-partial_alter_compose.
  apply partial_alter_ext. by intros [?|].
Qed.
Lemma alter_commute {A} (f g : A A) (m : M A) i j :
  i j alter f i (alter g j m) = alter g j (alter f i m).
Proof. apply partial_alter_commute. Qed.
Lemma alter_insert {A} (m : M A) i f x :
  alter f i (<[i := x]> m) = <[i := f x]> m.
Proof.
  unfold alter, insert, map_alter, map_insert.
  by rewrite <-partial_alter_compose.
Qed.
Lemma alter_insert_ne {A} (m : M A) i j f x :
  i j
  alter f i (<[j := x]> m) = <[j := x]> (alter f i m).
Proof. intros. symmetry. by apply partial_alter_commute. Qed.
Lemma lookup_alter_Some {A} (f : A A) (m : M A) i j y :
  alter f i m !! j = Some y
    (i = j x, m !! j = Some x y = f x) (i j m !! j = Some y).
Proof.
  destruct (decide (i = j)) as [->|?].
  - rewrite lookup_alter. naive_solver (simplify_option_eq; eauto).
  - rewrite lookup_alter_ne by done. naive_solver.
Qed.
Lemma lookup_alter_None {A} (f : A A) (m : M A) i j :
  alter f i m !! j = None m !! j = None.
Proof.
  by destruct (decide (i = j)) as [->|?];
    rewrite ?lookup_alter, ?fmap_None, ?lookup_alter_ne.
Qed.
Lemma lookup_alter_is_Some {A} (f : A A) (m : M A) i j :
  is_Some (alter f i m !! j) is_Some (m !! j).
Proof. by rewrite <-!not_eq_None_Some, lookup_alter_None. Qed.
Lemma alter_id {A} (f : A A) (m : M A) i :
  ( x, m !! i = Some x f x = x) alter f i m = m.
Proof.
  intros Hi; apply map_eq; intros j; destruct (decide (i = j)) as [->|?].
  { rewrite lookup_alter; destruct (m !! j); f_equal/=; auto. }
  by rewrite lookup_alter_ne by done.
Qed.
Lemma alter_mono {A} f (m1 m2 : M A) i : m1 m2 alter f i m1 alter f i m2.
Proof.
  rewrite !map_subseteq_spec. intros ? j x.
  rewrite !lookup_alter_Some. naive_solver.
Qed.
Lemma alter_strict_mono {A} f (m1 m2 : M A) i :
  m1 m2 alter f i m1 alter f i m2.
Proof.
  rewrite !map_subset_alt.
  intros [? (j&?&?)]; split; auto using alter_mono.
   j. by rewrite lookup_alter_None, lookup_alter_is_Some.
Qed.

Properties of the delete operation

Lemma lookup_delete {A} (m : M A) i : delete i m !! i = None.
Proof. apply lookup_partial_alter. Qed.
Lemma lookup_total_delete `{!Inhabited A} (m : M A) i :
  delete i m !!! i = inhabitant.
Proof. by rewrite lookup_total_alt, lookup_delete. Qed.
Lemma lookup_delete_ne {A} (m : M A) i j : i j delete i m !! j = m !! j.
Proof. apply lookup_partial_alter_ne. Qed.
Lemma lookup_total_delete_ne `{!Inhabited A} (m : M A) i j :
  i j delete i m !!! j = m !!! j.
Proof. intros. by rewrite lookup_total_alt, lookup_delete_ne. Qed.
Lemma lookup_delete_Some {A} (m : M A) i j y :
  delete i m !! j = Some y i j m !! j = Some y.
Proof.
  split.
  - destruct (decide (i = j)) as [->|?];
      rewrite ?lookup_delete, ?lookup_delete_ne; intuition congruence.
  - intros [??]. by rewrite lookup_delete_ne.
Qed.
Lemma lookup_delete_is_Some {A} (m : M A) i j :
  is_Some (delete i m !! j) i j is_Some (m !! j).
Proof. unfold is_Some; setoid_rewrite lookup_delete_Some; naive_solver. Qed.
Lemma lookup_delete_None {A} (m : M A) i j :
  delete i m !! j = None i = j m !! j = None.
Proof.
  destruct (decide (i = j)) as [->|?];
    rewrite ?lookup_delete, ?lookup_delete_ne; tauto.
Qed.
Lemma delete_empty {A} i : delete i =@{M A} .
Proof. rewrite <-(partial_alter_self ) at 2. by rewrite lookup_empty. Qed.
Lemma delete_commute {A} (m : M A) i j :
  delete i (delete j m) = delete j (delete i m).
Proof.
  destruct (decide (i = j)) as [->|]; [done|]. by apply partial_alter_commute.
Qed.
Lemma delete_notin {A} (m : M A) i : m !! i = None delete i m = m.
Proof.
  intros. apply map_eq. intros j. by destruct (decide (i = j)) as [->|?];
    rewrite ?lookup_delete, ?lookup_delete_ne.
Qed.
Lemma delete_idemp {A} (m : M A) i :
  delete i (delete i m) = delete i m.
Proof. by setoid_rewrite <-partial_alter_compose. Qed.
Lemma delete_partial_alter {A} (m : M A) i f :
  m !! i = None delete i (partial_alter f i m) = m.
Proof.
  intros. unfold delete, map_delete. rewrite <-partial_alter_compose.
  unfold compose. by apply partial_alter_self_alt.
Qed.
Lemma delete_insert {A} (m : M A) i x :
  m !! i = None delete i (<[i:=x]>m) = m.
Proof. apply delete_partial_alter. Qed.
Lemma delete_insert_delete {A} (m : M A) i x :
  delete i (<[i:=x]>m) = delete i m.
Proof. by setoid_rewrite <-partial_alter_compose. Qed.
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_alter {A} (m : M A) i f :
  delete i (alter f i m) = delete i m.
Proof.
  unfold delete, alter, map_delete, map_alter.
  by rewrite <-partial_alter_compose.
Qed.
Lemma delete_alter_ne {A} (m : M A) i j f :
  i j delete i (alter f j m) = alter f j (delete i m).
Proof. intro. by apply partial_alter_commute. Qed.
Lemma delete_subseteq {A} (m : M A) i : delete i m m.
Proof.
  rewrite !map_subseteq_spec. intros j x. rewrite lookup_delete_Some. tauto.
Qed.
Lemma delete_subset {A} (m : M A) i : is_Some (m !! i) delete i m m.
Proof.
  intros [x ?]; apply map_subset_alt; split; [apply delete_subseteq|].
   i. rewrite lookup_delete; eauto.
Qed.
Lemma delete_mono {A} (m1 m2 : M A) i : m1 m2 delete i m1 delete i m2.
Proof.
  rewrite !map_subseteq_spec. intros ? j x.
  rewrite !lookup_delete_Some. intuition eauto.
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_total_insert `{!Inhabited A} (m : M A) i x : <[i:=x]>m !!! i = x.
Proof. by rewrite lookup_total_alt, lookup_insert. Qed.
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.
Proof. unfold insert. apply lookup_partial_alter_ne. Qed.
Lemma lookup_total_insert_ne `{!Inhabited A} (m : M A) i j x :
  i j <[i:=x]>m !!! j = m !!! j.
Proof. intros. by rewrite lookup_total_alt, lookup_insert_ne. Qed.
Lemma insert_insert {A} (m : M A) i x y : <[i:=x]>(<[i:=y]>m) = <[i:=x]>m.
Proof. unfold insert, map_insert. by rewrite <-partial_alter_compose. Qed.
Lemma insert_commute {A} (m : M A) i j x y :
  i j <[i:=x]>(<[j:=y]>m) = <[j:=y]>(<[i:=x]>m).
Proof. apply partial_alter_commute. Qed.
Lemma lookup_insert_Some {A} (m : M A) i j x y :
  <[i:=x]>m !! j = Some y (i = j x = y) (i j m !! j = Some y).
Proof.
  split.
  - destruct (decide (i = j)) as [->|?];
      rewrite ?lookup_insert, ?lookup_insert_ne; intuition congruence.
  - intros [[-> ->]|[??]]; [apply lookup_insert|]. by rewrite lookup_insert_ne.
Qed.
Lemma lookup_insert_is_Some {A} (m : M A) i j x :
  is_Some (<[i:=x]>m !! j) i = j i j is_Some (m !! j).
Proof. unfold is_Some; setoid_rewrite lookup_insert_Some; naive_solver. Qed.
Lemma lookup_insert_is_Some' {A} (m : M A) i j x :
  is_Some (<[i:=x]>m !! j) i = j is_Some (m !! j).
Proof. rewrite lookup_insert_is_Some. destruct (decide (i=j)); naive_solver. Qed.
Lemma lookup_insert_None {A} (m : M A) i j x :
  <[i:=x]>m !! j = None m !! j = None i j.
Proof.
  split; [|by intros [??]; rewrite lookup_insert_ne].
  destruct (decide (i = j)) as [->|];
    rewrite ?lookup_insert, ?lookup_insert_ne; intuition congruence.
Qed.
Lemma insert_id {A} (m : M A) i x : m !! i = Some x <[i:=x]>m = m.
Proof.
  intros; apply map_eq; intros j; destruct (decide (i = j)) as [->|];
    by rewrite ?lookup_insert, ?lookup_insert_ne by done.
Qed.
Lemma insert_included {A} R `{!Reflexive R} (m : M A) i x :
  ( y, m !! i = Some y R y x) map_included R m (<[i:=x]>m).
Proof.
  intros ? j; destruct (decide (i = j)) as [->|].
  - rewrite lookup_insert. destruct (m !! j); simpl; eauto.
  - rewrite lookup_insert_ne by done. by destruct (m !! j); simpl.
Qed.
Lemma insert_empty {A} i (x : A) : <[i:=x]> =@{M A} {[i := x]}.
Proof. done. Qed.
Lemma insert_non_empty {A} (m : M A) i x : <[i:=x]>m .
Proof.
  intros Hi%(f_equal (.!! i)). by rewrite lookup_insert, lookup_empty in Hi.
Qed.
Lemma insert_delete_insert {A} (m : M A) i x : <[i:=x]>(delete i m) = <[i:=x]> m.
Proof. symmetry; apply (partial_alter_compose (λ _, Some x)). Qed.
Lemma insert_delete {A} (m : M A) i x :
  m !! i = Some x <[i:=x]> (delete i m) = m.
Proof. intros. rewrite insert_delete_insert, insert_id; done. Qed.

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.
Proof. intro. apply partial_alter_subset; eauto. Qed.
Lemma insert_mono {A} (m1 m2 : M A) i x : m1 m2 <[i:=x]> m1 <[i:=x]>m2.
Proof.
  rewrite !map_subseteq_spec.
  intros Hm j y. rewrite !lookup_insert_Some. naive_solver.
Qed.
Lemma insert_subseteq_r {A} (m1 m2 : M A) i x :
  m1 !! i = None m1 m2 m1 <[i:=x]>m2.
Proof.
  intros. trans (<[i:=x]> m1); eauto using insert_subseteq, insert_mono.
Qed.
Lemma insert_subseteq_l {A} (m1 m2 : M A) i x :
  m2 !! i = Some x m1 m2 <[i:=x]> m1 m2.
Proof.
  intros Hi Hincl. etrans; [apply insert_mono, Hincl|]. by rewrite insert_id.
Qed.

Lemma insert_delete_subseteq {A} (m1 m2 : M A) i x :
  m1 !! i = None <[i:=x]> m1 m2 m1 delete i m2.
Proof.
  rewrite !map_subseteq_spec. intros Hi Hix j y Hj.
  destruct (decide (i = j)) as [->|]; [congruence|].
  rewrite lookup_delete_ne by done.
  apply Hix; 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.
Proof.
  rewrite !map_subseteq_spec.
  intros Hix Hi j y Hj. destruct (decide (i = j)) as [->|?].
  - rewrite lookup_insert. congruence.
  - 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.
Proof.
  intros ? [Hm12 Hm21]; split; [eauto using insert_delete_subseteq|].
  contradict Hm21. apply delete_insert_subseteq; auto.
  eapply lookup_weaken, Hm12. by rewrite lookup_insert.
Qed.
Lemma insert_subset_inv {A} (m1 m2 : M A) i x :
  m1 !! i = None <[i:=x]> m1 m2
   m2', m2 = <[i:=x]>m2' m1 m2' m2' !! i = None.
Proof.
  intros Hi Hm1m2. (delete i m2). split_and?.
  - rewrite insert_delete; [done|].
    eapply lookup_weaken, strict_include; eauto. by rewrite lookup_insert.
  - eauto using insert_delete_subset.
  - by rewrite lookup_delete.
Qed.

Properties of the singleton maps

Lemma lookup_singleton_Some {A} i j (x y : A) :
  ({[i := x]} : M A) !! j = Some y i = j x = y.
Proof.
  rewrite <-insert_empty,lookup_insert_Some, lookup_empty; intuition congruence.
Qed.
Lemma lookup_singleton_None {A} i j (x : A) :
  ({[i := x]} : M A) !! j = None i j.
Proof. rewrite <-insert_empty,lookup_insert_None, lookup_empty; tauto. Qed.
Lemma lookup_singleton {A} i (x : A) : ({[i := x]} : M A) !! i = Some x.
Proof. by rewrite lookup_singleton_Some. Qed.
Lemma lookup_total_singleton `{!Inhabited A} i (x : A) :
  ({[i := x]} : M A) !!! i = x.
Proof. by rewrite lookup_total_alt, lookup_singleton. Qed.
Lemma lookup_singleton_ne {A} i j (x : A) :
  i j ({[i := x]} : M A) !! j = None.
Proof. by rewrite lookup_singleton_None. Qed.
Lemma lookup_total_singleton_ne `{!Inhabited A} i j (x : A) :
  i j ({[i := x]} : M A) !!! j = inhabitant.
Proof. intros. by rewrite lookup_total_alt, lookup_singleton_ne. Qed.

Global Instance map_singleton_inj {A} : Inj2 (=) (=) (=) (singletonM (M:=M A)).
Proof.
  intros i1 x1 i2 x2 Heq%(f_equal (lookup i1)).
  rewrite lookup_singleton in Heq. destruct (decide (i1 = i2)) as [->|].
  - rewrite lookup_singleton in Heq. naive_solver.
  - rewrite lookup_singleton_ne in Heq by done. naive_solver.
Qed.

Lemma map_non_empty_singleton {A} i (x : A) : {[i := x]} ≠@{M A} .
Proof.
  intros Hix. apply (f_equal (.!! i)) in Hix.
  by rewrite lookup_empty, lookup_singleton in Hix.
Qed.
Lemma insert_singleton {A} i (x y : A) : <[i:=y]> {[i := x]} =@{M A} {[i := y]}.
Proof.
  unfold singletonM, 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]} =@{M A} {[i := f x]}.
Proof.
  intros. apply map_eq. intros i'. destruct (decide (i = i')) as [->|?].
  - 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]}=@{M A} {[j := x]}.
Proof.
  intros. apply map_eq; intros i'. by destruct (decide (i = i')) as [->|?];
    rewrite ?lookup_alter, ?lookup_singleton_ne, ?lookup_alter_ne by done.
Qed.
Lemma singleton_non_empty {A} i (x : A) : {[i:=x]} ≠@{M A} .
Proof. apply insert_non_empty. Qed.
Lemma delete_singleton {A} i (x : A) : delete i {[i := x]} =@{M A} .
Proof. setoid_rewrite <-partial_alter_compose. apply delete_empty. Qed.
Lemma delete_singleton_ne {A} i j (x : A) :
  i j delete i {[j := x]} =@{M A} {[j := x]}.
Proof. intro. apply delete_notin. by apply lookup_singleton_ne. Qed.

Lemma map_singleton_subseteq_l {A} i (x : A) (m : M A) :
  {[i := x]} m m !! i = Some x.
Proof.
  rewrite map_subseteq_spec. setoid_rewrite lookup_singleton_Some. naive_solver.
Qed.
Lemma map_singleton_subseteq {A} i j (x y : A) :
  {[i := x]} ⊆@{M A} {[j := y]} i = j x = y.
Proof.
  rewrite map_subseteq_spec. setoid_rewrite lookup_singleton_Some. naive_solver.
Qed.

Properties of the map operations

Global Instance map_fmap_inj {A B} (f : A B) :
  Inj (=) (=) f Inj (=@{M A}) (=@{M B}) (fmap f).
Proof.
  intros ? m1 m2 Hm. apply map_eq; intros i.
  apply (inj (fmap (M:=option) f)). by rewrite <-!lookup_fmap, Hm.
Qed.

Lemma lookup_fmap_Some {A B} (f : A B) (m : M A) i y :
  (f <$> m) !! i = Some y x, f x = y m !! i = Some x.
Proof. rewrite lookup_fmap, fmap_Some. naive_solver. Qed.
Lemma lookup_omap_Some {A B} (f : A option B) (m : M A) i y :
  omap f m !! i = Some y x, f x = Some y m !! i = Some x.
Proof. rewrite lookup_omap, bind_Some. naive_solver. Qed.
Lemma lookup_omap_id_Some {A} (m : M (option A)) i x :
  omap id m !! i = Some x m !! i = Some (Some x).
Proof. rewrite lookup_omap_Some. naive_solver. Qed.

Lemma fmap_empty {A B} (f : A B) : f <$> =@{M B} .
Proof. apply map_empty; intros i. by rewrite lookup_fmap, lookup_empty. Qed.
Lemma omap_empty {A B} (f : A option B) : omap f =@{M B} .
Proof. apply map_empty; intros i. by rewrite lookup_omap, lookup_empty. Qed.

Lemma fmap_empty_iff {A B} (f : A B) m : f <$> m =@{M B} m = .
Proof.
  split; [|intros ->; by rewrite fmap_empty].
  intros Hm. apply map_eq; intros i. generalize (f_equal (lookup i) Hm).
  by rewrite lookup_fmap, !lookup_empty, fmap_None.
Qed.
Lemma fmap_empty_inv {A B} (f : A B) m : f <$> m =@{M B} m = .
Proof. apply fmap_empty_iff. Qed.

Lemma fmap_insert {A B} (f: A B) (m : M A) i x :
  f <$> <[i:=x]>m = <[i:=f x]>(f <$> m).
Proof.
  apply map_eq; intros i'; destruct (decide (i' = i)) as [->|].
  - by rewrite lookup_fmap, !lookup_insert.
  - by rewrite lookup_fmap, !lookup_insert_ne, lookup_fmap by done.
Qed.
Lemma omap_insert {A B} (f : A option B) (m : M A) i x :
  omap f (<[i:=x]>m) =
    (match f x with Some y<[i:=y]> | Nonedelete i end) (omap f m).
Proof.
  intros; apply map_eq; intros i'; destruct (decide (i' = i)) as [->|].
  - rewrite lookup_omap, !lookup_insert. destruct (f x) as [y|] eqn:Hx; simpl.
    + by rewrite lookup_insert.
    + by rewrite lookup_delete, Hx.
  - rewrite lookup_omap, !lookup_insert_ne by done.
    destruct (f x) as [y|] eqn:Hx; simpl.
    + by rewrite lookup_insert_ne, lookup_omap by done.
    + by rewrite lookup_delete_ne, lookup_omap by done.
Qed.
Lemma omap_insert_Some {A B} (f : A option B) (m : M A) i x y :
  f x = Some y omap f (<[i:=x]>m) = <[i:=y]>(omap f m).
Proof. intros Hx. by rewrite omap_insert, Hx. Qed.
Lemma omap_insert_None {A B} (f : A option B) (m : M A) i x :
  f x = None omap f (<[i:=x]>m) = delete i (omap f m).
Proof. intros Hx. by rewrite omap_insert, Hx. Qed.

Lemma fmap_delete {A B} (f: A B) (m : M A) i :
  f <$> delete i m = delete i (f <$> m).
Proof.
  apply map_eq; intros i'; destruct (decide (i' = i)) as [->|].
  - by rewrite lookup_fmap, !lookup_delete.
  - by rewrite lookup_fmap, !lookup_delete_ne, lookup_fmap by done.
Qed.
Lemma omap_delete {A B} (f: A option B) (m : M A) i :
  omap f (delete i m) = delete i (omap f m).
Proof.
  apply map_eq; intros i'; destruct (decide (i' = i)) as [->|].
  - by rewrite lookup_omap, !lookup_delete.
  - by rewrite lookup_omap, !lookup_delete_ne, lookup_omap by done.
Qed.

Lemma map_fmap_singleton {A B} (f : A B) i x :
  f <$> {[i := x]} =@{M B} {[i := f x]}.
Proof.
  by unfold singletonM, map_singleton; rewrite fmap_insert, fmap_empty.
Qed.
Lemma map_fmap_singleton_inv {A B} (f : A B) (m : M A) i y :
  f <$> m = {[i := y]} x, y = f x m = {[ i := x ]}.
Proof.
  intros Hm. pose proof (f_equal (.!! i) Hm) as Hmi.
  rewrite lookup_fmap, lookup_singleton, fmap_Some in Hmi.
  destruct Hmi as (x&?&->). x. split; [done|].
  apply map_eq; intros j. destruct (decide (i = j)) as[->|?].
  - by rewrite lookup_singleton.
  - rewrite lookup_singleton_ne by done.
    apply (fmap_None f). by rewrite <-lookup_fmap, Hm, lookup_singleton_ne.
Qed.

Lemma omap_singleton {A B} (f : A option B) i x :
  omap f {[ i := x ]} =@{M B} match f x with Some y{[ i:=y ]} | None end.
Proof.
  rewrite <-insert_empty, omap_insert, omap_empty. destruct (f x) as [y|]; simpl.
  - by rewrite insert_empty.
  - by rewrite delete_empty.
Qed.
Lemma omap_singleton_Some {A B} (f : A option B) i x y :
  f x = Some y omap f {[ i := x ]} =@{M B} {[ i := y ]}.
Proof. intros Hx. by rewrite omap_singleton, Hx. Qed.
Lemma omap_singleton_None {A B} (f : A option B) i x :
  f x = None omap f {[ i := x ]} =@{M B} .
Proof. intros Hx. by rewrite omap_singleton, Hx. Qed.

Lemma map_fmap_id {A} (m : M A) : id <$> m = m.
Proof. apply map_eq; intros i; by rewrite lookup_fmap, option_fmap_id. Qed.
Lemma map_fmap_compose {A B C} (f : A B) (g : B C) (m : M A) :
  g f <$> m = g <$> (f <$> m).
Proof. apply map_eq; intros i; by rewrite !lookup_fmap,option_fmap_compose. Qed.
Lemma map_fmap_ext {A B} (f1 f2 : A B) (m : M A) :
  ( i x, m !! i = Some x f1 x = f2 x) f1 <$> m = f2 <$> m.
Proof.
  intros Hi; apply map_eq; intros i; rewrite !lookup_fmap.
  by destruct (m !! i) eqn:?; simpl; erewrite ?Hi by eauto.
Qed.
Lemma omap_ext {A B} (f1 f2 : A option B) (m : M A) :
  ( i x, m !! i = Some x f1 x = f2 x) omap f1 m = omap f2 m.
Proof.
  intros Hi; apply map_eq; intros i; rewrite !lookup_omap.
  by destruct (m !! i) eqn:?; simpl; erewrite ?Hi by eauto.
Qed.

Lemma map_fmap_omap {A B C} (f : A option B) (g : B C) (m : M A) :
  g <$> omap f m = omap (λ x, g <$> f x) m.
Proof.
  apply map_eq. intros i.
  rewrite !lookup_fmap, !lookup_omap. destruct (m !! i); done.
Qed.

Lemma map_fmap_alt {A B} (f : A B) (m : M A) :
  f <$> m = omap (λ x, Some (f x)) m.
Proof.
  apply map_eq. intros i.
  rewrite lookup_fmap, lookup_omap. destruct (m !! i); done.
Qed.

Lemma map_fmap_mono {A B} (f : A B) (m1 m2 : M A) :
  m1 m2 f <$> m1 f <$> m2.
Proof.
  rewrite !map_subseteq_spec; intros Hm i x.
  rewrite !lookup_fmap, !fmap_Some. naive_solver.
Qed.
Lemma map_fmap_strict_mono {A B} (f : A B) (m1 m2 : M A) :
  m1 m2 f <$> m1 f <$> m2.
Proof.
  rewrite !map_subset_alt.
  intros [? (j&?&?)]; split; auto using map_fmap_mono.
   j. by rewrite !lookup_fmap, fmap_None, fmap_is_Some.
Qed.
Lemma map_omap_mono {A B} (f : A option B) (m1 m2 : M A) :
  m1 m2 omap f m1 omap f m2.
Proof.
  rewrite !map_subseteq_spec; intros Hm i x.
  rewrite !lookup_omap, !bind_Some. naive_solver.
Qed.

Properties of conversion to lists

Lemma elem_of_map_to_list' {A} (m : M A) ix :
  ix map_to_list m m !! ix.1 = Some (ix.2).
Proof. destruct ix as [i x]. apply elem_of_map_to_list. Qed.
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.
Proof. rewrite !elem_of_map_to_list. congruence. Qed.
Lemma NoDup_fst_map_to_list {A} (m : M A) : NoDup ((map_to_list m).*1).
Proof. eauto using NoDup_fmap_fst, map_to_list_unique, NoDup_map_to_list. Qed.
Lemma elem_of_list_to_map_1' {A} (l : list (K × A)) i x :
  ( y, (i,y) l x = y) (i,x) l (list_to_map l : M A) !! i = Some x.
Proof.
  induction l as [|[j y] l IH]; csimpl; [by rewrite elem_of_nil|].
  setoid_rewrite elem_of_cons.
  intros Hdup [?|?]; simplify_eq; [by rewrite lookup_insert|].
  destruct (decide (i = j)) as [->|].
  - rewrite lookup_insert; f_equal; eauto using eq_sym.
  - rewrite lookup_insert_ne by done; eauto.
Qed.
Lemma elem_of_list_to_map_1 {A} (l : list (K × A)) i x :
  NoDup (l.*1) (i,x) l (list_to_map l : M A) !! i = Some x.
Proof.
  intros ? Hx; apply elem_of_list_to_map_1'; eauto using NoDup_fmap_fst.
  intros y; revert Hx. rewrite !elem_of_list_lookup; intros [i' Hi'] [j' Hj'].
  cut (i' = j'); [naive_solver|]. apply NoDup_lookup with (l.*1) i;
    by rewrite ?list_lookup_fmap, ?Hi', ?Hj'.
Qed.
Lemma elem_of_list_to_map_2 {A} (l : list (K × A)) i x :
  (list_to_map l : M A) !! i = Some x (i,x) l.
Proof.
  induction l as [|[j y] l IH]; simpl; [by rewrite lookup_empty|].
  rewrite elem_of_cons. destruct (decide (i = j)) as [->|];
    rewrite ?lookup_insert, ?lookup_insert_ne; intuition congruence.
Qed.
Lemma elem_of_list_to_map' {A} (l : list (K × A)) i x :
  ( x', (i,x) l (i,x') l x = x')
  (i,x) l (list_to_map l : M A) !! i = Some x.
Proof. split; auto using elem_of_list_to_map_1', elem_of_list_to_map_2. Qed.
Lemma elem_of_list_to_map {A} (l : list (K × A)) i x :
  NoDup (l.*1) (i,x) l (list_to_map l : M A) !! i = Some x.
Proof. split; auto using elem_of_list_to_map_1, elem_of_list_to_map_2. Qed.

Lemma not_elem_of_list_to_map_1 {A} (l : list (K × A)) i :
  i l.*1 (list_to_map l : M A) !! i = None.
Proof.
  rewrite elem_of_list_fmap, eq_None_not_Some. intros Hi [x ?]; destruct Hi.
   (i,x); simpl; auto using elem_of_list_to_map_2.
Qed.
Lemma not_elem_of_list_to_map_2 {A} (l : list (K × A)) i :
  (list_to_map l : M A) !! i = None i l.*1.
Proof.
  induction l as [|[j y] l IH]; csimpl; [rewrite elem_of_nil; tauto|].
  rewrite elem_of_cons. destruct (decide (i = j)); simplify_eq.
  - by rewrite lookup_insert.
  - by rewrite lookup_insert_ne; intuition.
Qed.
Lemma not_elem_of_list_to_map {A} (l : list (K × A)) i :
  i l.*1 (list_to_map l : M A) !! i = None.
Proof. red; auto using not_elem_of_list_to_map_1,not_elem_of_list_to_map_2. Qed.
Lemma list_to_map_proper {A} (l1 l2 : list (K × A)) :
  NoDup (l1.*1) l1 ≡ₚ l2 (list_to_map l1 : M A) = list_to_map l2.
Proof.
  intros ? Hperm. apply map_eq. intros i. apply option_eq. intros x.
  by rewrite <-!elem_of_list_to_map; rewrite <-?Hperm.
Qed.
Lemma list_to_map_inj {A} (l1 l2 : list (K × A)) :
  NoDup (l1.*1) NoDup (l2.*1)
  (list_to_map l1 : M A) = list_to_map l2 l1 ≡ₚ l2.
Proof.
  intros ?? Hl1l2. apply NoDup_Permutation; [by eauto using NoDup_fmap_1..|].
  intros [i x]. by rewrite !elem_of_list_to_map, Hl1l2.
Qed.
Lemma list_to_map_to_list {A} (m : M A) : list_to_map (map_to_list m) = m.
Proof.
  apply map_eq. intros i. apply option_eq. intros x.
  by rewrite <-elem_of_list_to_map, elem_of_map_to_list
    by auto using NoDup_fst_map_to_list.
Qed.
Lemma map_to_list_to_map {A} (l : list (K × A)) :
  NoDup (l.*1) map_to_list (list_to_map l) ≡ₚ l.
Proof. auto using list_to_map_inj, NoDup_fst_map_to_list, list_to_map_to_list. Qed.
Lemma map_to_list_inj {A} (m1 m2 : M A) :
  map_to_list m1 ≡ₚ map_to_list m2 m1 = m2.
Proof.
  intros. rewrite <-(list_to_map_to_list m1), <-(list_to_map_to_list m2).
  auto using list_to_map_proper, NoDup_fst_map_to_list.
Qed.
Lemma list_to_map_flip {A} (m1 : M A) l2 :
  map_to_list m1 ≡ₚ l2 m1 = list_to_map l2.
Proof.
  intros. rewrite <-(list_to_map_to_list m1).
  auto using list_to_map_proper, NoDup_fst_map_to_list.
Qed.

Lemma list_to_map_nil {A} : list_to_map [] =@{M A} .
Proof. done. Qed.
Lemma list_to_map_cons {A} (l : list (K × A)) i x :
  list_to_map ((i, x) :: l) =@{M A} <[i:=x]>(list_to_map l).
Proof. done. Qed.
Lemma list_to_map_snoc {A} (l : list (K × A)) i x :
  i l.*1 list_to_map (l ++ [(i, x)]) =@{M A} <[i:=x]>(list_to_map l).
Proof.
  induction l as [|[k y] l IH]; [done|]. csimpl.
  intros [Hneq Hni]%not_elem_of_cons.
  by rewrite (IH Hni), insert_commute by done.
Qed.
Lemma list_to_map_fmap {A B} (f : A B) l :
  list_to_map (prod_map id f <$> l) = f <$> (list_to_map l : M A).
Proof.
  induction l as [|[i x] l IH]; csimpl; rewrite ?fmap_empty; auto.
  rewrite <-list_to_map_cons; simpl. by rewrite IH, <-fmap_insert.
Qed.

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 map_to_list (<[i:=x]>m) ≡ₚ (i,x) :: map_to_list m.
Proof.
  intros. apply list_to_map_inj; csimpl.
  - apply NoDup_fst_map_to_list.
  - constructor; [|by auto using NoDup_fst_map_to_list].
    rewrite elem_of_list_fmap. intros [[??] [? Hlookup]]; subst; simpl in ×.
    rewrite elem_of_map_to_list in Hlookup. congruence.
  - by rewrite !list_to_map_to_list.
Qed.
Lemma map_to_list_singleton {A} i (x : A) :
  map_to_list ({[i:=x]} : M A) = [(i,x)].
Proof.
  apply Permutation_singleton_r. unfold singletonM, map_singleton.
  by rewrite map_to_list_insert, map_to_list_empty by eauto using lookup_empty.
Qed.
Lemma map_to_list_delete {A} (m : M A) i x :
  m !! i = Some x (i,x) :: map_to_list (delete i m) ≡ₚ map_to_list m.
Proof.
  intros. rewrite <-map_to_list_insert by (by rewrite lookup_delete).
  by rewrite insert_delete.
Qed.

Lemma map_to_list_submseteq {A} (m1 m2 : M A) :
  m1 m2 map_to_list m1 ⊆+ map_to_list m2.
Proof.
  intros; apply NoDup_submseteq; [by eauto using NoDup_map_to_list|].
  intros [i x]. rewrite !elem_of_map_to_list; eauto using lookup_weaken.
Qed.
Lemma map_to_list_fmap {A B} (f : A B) (m : M A) :
  map_to_list (f <$> m) ≡ₚ prod_map id f <$> map_to_list m.
Proof.
  assert (NoDup ((prod_map id f <$> map_to_list m).*1)).
  { erewrite <-list_fmap_compose, (list_fmap_ext _ fst) by done.
    apply NoDup_fst_map_to_list. }
  rewrite <-(list_to_map_to_list m) at 1.
  by rewrite <-list_to_map_fmap, map_to_list_to_map.
Qed.

Lemma map_to_list_empty_iff {A} (m : M A) : map_to_list m = [] m = .
Proof.
  split.
  - rewrite <-Permutation_nil_r, <-map_to_list_empty. apply map_to_list_inj.
  - intros →. apply map_to_list_empty.
Qed.

Lemma map_to_list_insert_inv {A} (m : M A) l i x :
  map_to_list m ≡ₚ (i,x) :: l m = <[i:=x]>(list_to_map l).
Proof.
  intros Hperm. apply map_to_list_inj.
  assert (i l.*1 NoDup (l.*1)) as [].
  { rewrite <-NoDup_cons. change (NoDup (((i,x)::l).*1)). rewrite <-Hperm.
    auto using NoDup_fst_map_to_list. }
  rewrite Hperm, map_to_list_insert, map_to_list_to_map;
    auto using not_elem_of_list_to_map_1.
Qed.

Lemma map_to_list_length {A} (m : M A) :
  length (map_to_list m) = size m.
Proof.
  apply (map_fold_ind (λ n m, length (map_to_list m) = n)); clear m.
  { by rewrite map_to_list_empty. }
  intros i x m n ? IH. by rewrite map_to_list_insert, <-IH by done.
Qed.

Lemma map_choose {A} (m : M A) : m i x, m !! i = Some x.
Proof.
  rewrite <-map_to_list_empty_iff.
  intros Hemp. destruct (map_to_list m) as [|[i x] l] eqn:Hm; [done|].
   i, x. rewrite <-elem_of_map_to_list, Hm. by left.
Qed.

Global Instance map_eq_dec_empty {A} (m : M A) : Decision (m = ) | 20.
Proof.
  refine (cast_if (decide (map_to_list m = [])));
    by rewrite <-?map_to_list_empty_iff.
Defined.

Lemma map_choose_or_empty {A} (m : M A) : ( i x, m !! i = Some x) m = .
Proof. destruct (decide (m = )); [right|left]; auto using map_choose. Qed.

Properties of the imap function
Lemma map_lookup_imap {A B} (f : K A option B) (m : M A) i :
  map_imap f m !! i = m !! i ≫= f i.
Proof.
  unfold map_imap; destruct (m !! i ≫= f i) as [y|] eqn:Hi; simpl.
  - destruct (m !! i) as [x|] eqn:?; simplify_eq/=.
    apply elem_of_list_to_map_1'.
    { intros y'; rewrite elem_of_list_omap; intros ([i' x']&Hi'&?).
      by rewrite elem_of_map_to_list in Hi'; simplify_option_eq. }
    apply elem_of_list_omap; (i,x); split;
      [by apply elem_of_map_to_list|by simplify_option_eq].
  - apply not_elem_of_list_to_map; rewrite elem_of_list_fmap.
    intros ([i' x]&->&Hi'); simplify_eq/=.
    rewrite elem_of_list_omap in Hi'; destruct Hi' as ([j y]&Hj&?).
    rewrite elem_of_map_to_list in Hj; simplify_option_eq.
Qed.

Lemma map_imap_Some {A} (m : M A) : map_imap (λ _, Some) m = m.
Proof.
  apply map_eq. intros i. rewrite map_lookup_imap. by destruct (m !! i).
Qed.

Lemma map_imap_insert {A B} (f : K A option B) i x (m : M A) :
  map_imap f (<[i:=x]> m) =
    (match f i x with Some y<[i:=y]> | Nonedelete i end) (map_imap f m).
Proof.
  destruct (f i x) as [y|] eqn:Hw; simpl.
  - apply map_eq. intros k. rewrite map_lookup_imap.
    destruct (decide (k = i)) as [->|Hk_not_i].
    + by rewrite lookup_insert, lookup_insert.
    + rewrite !lookup_insert_ne by done.
      by rewrite map_lookup_imap.
  - apply map_eq. intros k. rewrite map_lookup_imap.
    destruct (decide (k = i)) as [->|Hk_not_i].
    + by rewrite lookup_insert, lookup_delete.
    + rewrite lookup_insert_ne, lookup_delete_ne by done.
      by rewrite map_lookup_imap.
Qed.
Lemma map_imap_insert_Some {A B} (f : K A option B) i x (m : M A) y :
  f i x = Some y map_imap f (<[i:=x]> m) = <[i:=y]> (map_imap f m).
Proof. intros Hi. by rewrite map_imap_insert, Hi. Qed.
Lemma map_imap_insert_None {A B} (f : K A option B) i x (m : M A) :
  f i x = None map_imap f (<[i:=x]> m) = delete i (map_imap f m).
Proof. intros Hi. by rewrite map_imap_insert, Hi. Qed.

Lemma map_imap_delete {A B} (f : K A option B) (m : M A) (i : K) :
  map_imap f (delete i m) = delete i (map_imap f m).
Proof.
  apply map_eq. intros k. rewrite map_lookup_imap.
  destruct (decide (k = i)) as [->|Hk_not_i].
  - by rewrite !lookup_delete.
  - rewrite !lookup_delete_ne by done.
    by rewrite map_lookup_imap.
Qed.

Lemma map_imap_ext {A1 A2 B} (f1 : K A1 option B)
    (f2 : K A2 option B) (m1 : M A1) (m2 : M A2) :
  ( k, f1 k <$> (m1 !! k) = f2 k <$> (m2 !! k))
  map_imap f1 m1 = map_imap f2 m2.
Proof.
  intros HExt. apply map_eq. intros i. rewrite !map_lookup_imap.
  specialize (HExt i). destruct (m1 !! i), (m2 !! i); naive_solver.
Qed.

Lemma map_imap_compose {A1 A2 B} (f1 : K A1 option B)
    (f2 : K A2 option A1) (m : M A2) :
  map_imap f1 (map_imap f2 m) = map_imap (λ k x, f2 k x ≫= f1 k) m.
Proof.
  apply map_eq. intros i. rewrite !map_lookup_imap. by destruct (m !! i).
Qed.

Lemma map_imap_empty {A B} (f : K A option B) :
  map_imap f =@{M B} .
Proof. unfold map_imap. by rewrite map_to_list_empty. Qed.

Properties of the size operation

Lemma map_size_empty {A} : size ( : M A) = 0.
Proof. by rewrite <-map_to_list_length, map_to_list_empty. Qed.
Lemma map_size_empty_iff {A} (m : M A) : size m = 0 m = .
Proof.
  by rewrite <-map_to_list_length, length_zero_iff_nil, map_to_list_empty_iff.
Qed.
Lemma map_size_empty_inv {A} (m : M A) : size m = 0 m = .
Proof. apply map_size_empty_iff. Qed.
Lemma map_size_non_empty_iff {A} (m : M A) : size m 0 m .
Proof. by rewrite map_size_empty_iff. Qed.

Lemma map_size_singleton {A} i (x : A) : size ({[ i := x ]} : M A) = 1.
Proof. by rewrite <-map_to_list_length, map_to_list_singleton. Qed.

Lemma map_size_ne_0_lookup {A} (m : M A) :
  size m 0 i, is_Some (m !! i).
Proof.
  rewrite map_size_non_empty_iff. split.
  - intros Hsz. apply map_choose. intros Hemp. done.
  - intros [i [k Hi]] →. rewrite lookup_empty in Hi. done.
Qed.
Lemma map_size_ne_0_lookup_1 {A} (m : M A) :
  size m 0 i, is_Some (m !! i).
Proof. intros. by eapply map_size_ne_0_lookup. Qed.
Lemma map_size_ne_0_lookup_2 {A} (m : M A) i :
  is_Some (m !! i) size m 0.
Proof. intros. eapply map_size_ne_0_lookup. eauto. Qed.

Lemma map_size_insert {A} i x (m : M A) :
  size (<[i:=x]> m) = (match m !! i with Some _id | NoneS end) (size m).
Proof.
  destruct (m !! i) as [y|] eqn:?; simpl.
  - rewrite <-(insert_id m i y) at 2 by done. rewrite <-!(insert_delete_insert m).
    rewrite <-!map_to_list_length.
    by rewrite !map_to_list_insert by (by rewrite lookup_delete).
  - by rewrite <-!map_to_list_length, map_to_list_insert.
Qed.
Lemma map_size_insert_Some {A} i x (m : M A) :
  is_Some (m !! i) size (<[i:=x]> m) = size m.
Proof. intros [y Hi]. by rewrite map_size_insert, Hi. Qed.
Lemma map_size_insert_None {A} i x (m : M A) :
  m !! i = None size (<[i:=x]> m) = S (size m).
Proof. intros Hi. by rewrite map_size_insert, Hi. Qed.

Lemma map_size_delete {A} i (m : M A) :
  size (delete i m) = (match m !! i with Some _pred | Noneid end) (size m).
Proof.
  destruct (m !! i) as [y|] eqn:?; simpl.
  - by rewrite <-!map_to_list_length, <-(map_to_list_delete m).
  - by rewrite delete_notin.
Qed.
Lemma map_size_delete_Some {A} i (m : M A) :
  is_Some (m !! i) size (delete i m) = pred (size m).
Proof. intros [y Hi]. by rewrite map_size_delete, Hi. Qed.
Lemma map_size_delete_None {A} i (m : M A) :
  m !! i = None size (delete i m) = size m.
Proof. intros Hi. by rewrite map_size_delete, Hi. Qed.

Lemma map_size_fmap {A B} (f : A B) (m : M A) : size (f <$> m) = size m.
Proof.
  intros. by rewrite <-!map_to_list_length, map_to_list_fmap, fmap_length.
Qed.

Lemma map_size_list_to_map {A} (l : list (K × A)) :
  NoDup l.*1
  size (list_to_map l : M A) = length l.
Proof.
  induction l; csimpl; inv 1; simplify_eq/=; [by rewrite map_size_empty|].
  rewrite map_size_insert_None by eauto using not_elem_of_list_to_map_1.
  eauto with f_equal.
Qed.

Lemma map_subseteq_size_eq {A} (m1 m2 : M A) :
  m1 m2 size m2 size m1 m1 = m2.
Proof.
  intros. apply map_to_list_inj, submseteq_length_Permutation.
  - by apply map_to_list_submseteq.
  - by rewrite !map_to_list_length.
Qed.

Lemma map_subseteq_size {A} (m1 m2 : M A) : m1 m2 size m1 size m2.
Proof.
  intros. rewrite <-!map_to_list_length.
  by apply submseteq_length, map_to_list_submseteq.
Qed.

Lemma map_subset_size {A} (m1 m2 : M A) : m1 m2 size m1 < size m2.
Proof.
  intros [Hm12 Hm21]. apply Nat.le_neq. split.
  - by apply map_subseteq_size.
  - intros Hsize. destruct Hm21.
    apply reflexive_eq, symmetry, map_subseteq_size_eq; auto with lia.
Qed.

Induction principles

Lemma map_wf {A} : well_founded (⊂@{M A}).
Proof. apply (wf_projected (<) size); auto using map_subset_size, lt_wf. Qed.

Lemma map_ind {A} (P : M A Prop) :
  P ( i x m, m !! i = None P m P (<[i:=x]>m)) m, P m.
Proof.
  intros ? Hins m. induction (map_wf m) as [m _ IH].
  destruct (map_choose_or_empty m) as [(i&x&?)| ->]; [|done].
  rewrite <-(insert_delete m i x) by done.
  apply Hins; [by rewrite lookup_delete|]. by apply IH, delete_subset.
Qed.

Properties of conversion from sets

Section set_to_map.
  Context {A : Type} `{FinSet B C}.

  Lemma lookup_set_to_map (f : B K × A) (Y : C) i x :
    ( y y', y Y y' Y (f y).1 = (f y').1 y = y')
    (set_to_map f Y : M A) !! i = Some x y, y Y f y = (i,x).
  Proof.
    intros Hinj. assert ( x',
      (i, x) f <$> elements Y (i, x') f <$> elements Y x = x').
    { intros x'. intros (y&Hx&Hy)%elem_of_list_fmap (y'&Hx'&Hy')%elem_of_list_fmap.
      rewrite elem_of_elements in Hy, Hy'.
      cut (y = y'); [congruence|]. apply Hinj; auto. by rewrite <-Hx, <-Hx'. }
    unfold set_to_map; rewrite <-elem_of_list_to_map' by done.
    rewrite elem_of_list_fmap. setoid_rewrite elem_of_elements; naive_solver.
  Qed.
End set_to_map.

Lemma lookup_set_to_map_id `{FinSet (K × A) C} (X : C) i x :
  ( i y y', (i,y) X (i,y') X y = y')
  (set_to_map id X : M A) !! i = Some x (i,x) X.
Proof.
  intros. etrans; [apply lookup_set_to_map|naive_solver].
  intros [] [] ???; simplify_eq/=; eauto with f_equal.
Qed.

Section map_to_set.
  Context {A : Type} `{SemiSet B C}.

  Lemma elem_of_map_to_set (f : K A B) (m : M A) (y : B) :
    y map_to_set (C:=C) f m i x, m !! i = Some x f i x = y.
  Proof.
    unfold map_to_set; simpl.
    rewrite elem_of_list_to_set, elem_of_list_fmap. split.
    - intros ([i x] & ? & ?%elem_of_map_to_list); eauto.
    - intros (i&x&?&?). (i,x). by rewrite elem_of_map_to_list.
  Qed.
  Lemma map_to_set_empty (f : K A B) :
    map_to_set f ( : M A) = ( : C).
  Proof. unfold map_to_set; simpl. by rewrite map_to_list_empty. Qed.
  Lemma map_to_set_insert (f : K A B)(m : M A) i x :
    m !! i = None
    map_to_set f (<[i:=x]>m) ≡@{C} {[f i x]} map_to_set f m.
  Proof.
    intros. unfold map_to_set; simpl. by rewrite map_to_list_insert.
  Qed.
  Lemma map_to_set_insert_L `{!LeibnizEquiv C} (f : K A B) (m : M A) i x :
    m !! i = None
    map_to_set f (<[i:=x]>m) =@{C} {[f i x]} map_to_set f m.
  Proof. unfold_leibniz. apply map_to_set_insert. Qed.
End map_to_set.

Lemma elem_of_map_to_set_pair `{SemiSet (K × A) C} (m : M A) i x :
  (i,x) ∈@{C} map_to_set pair m m !! i = Some x.
Proof. rewrite elem_of_map_to_set. naive_solver. Qed.

The fold operation

Lemma map_fold_foldr {A B} (R : relation B) `{!PreOrder R} (l : list (K × A))
    (f : K A B B) (b : B) m :
  ( j z, Proper (R ==> R) (f j z))
  ( j1 j2 z1 z2 y,
    j1 j2 m !! j1 = Some z1 m !! j2 = Some z2
    R (f j1 z1 (f j2 z2 y)) (f j2 z2 (f j1 z1 y)))
  map_to_list m ≡ₚ l
  R (map_fold f b m) (foldr (uncurry f) b l).
Proof.
  intros Hf_proper. revert l. apply (map_fold_ind (λ r m, l,
    ( j1 j2 z1 z2 y,
      j1 j2 m !! j1 = Some z1 m !! j2 = Some z2
      R (f j1 z1 (f j2 z2 y)) (f j2 z2 (f j1 z1 y)))
    map_to_list m ≡ₚ l
    R r (foldr (uncurry f) b l))); clear m.
  { intros [|x l] _; simpl; [done|].
    by rewrite map_to_list_empty, Permutation_nil_l. }
  intros i x m r ? IH l Hf Hl. rewrite map_to_list_insert in Hl by done.
  etrans; [|apply (foldr_permutation R), Hl]; simpl.
  - f_equiv. apply IH; [|done]. intros j1 j2 z1 z2 y ???.
    apply Hf; [done|rewrite lookup_insert_Some; naive_solver..].
  - intros []; apply _.
  - intros j1 [k1 y1] j2 [k2 y2] c Hj Hj1 Hj2. apply Hf.
    + intros →. eapply Hj, (NoDup_lookup ((i,x) :: map_to_list m).*1).
      × csimpl. apply NoDup_cons_2, NoDup_fst_map_to_list.
        intros ([??]&?&?%elem_of_map_to_list)%elem_of_list_fmap; naive_solver.
      × by rewrite list_lookup_fmap, Hj1.
      × by rewrite list_lookup_fmap, Hj2.
    + apply elem_of_map_to_list. rewrite map_to_list_insert by done.
      by eapply elem_of_list_lookup_2.
    + apply elem_of_map_to_list. rewrite map_to_list_insert by done.
      by eapply elem_of_list_lookup_2.
Qed.

Lemma map_fold_empty {A B} (f : K A B B) (b : B) :
  map_fold f b = b.
Proof.
  apply (map_fold_foldr _ []); [solve_proper|..].
  - intros j1 j2 z1 z2 y. by rewrite !lookup_empty.
  - by rewrite map_to_list_empty.
Qed.

Lemma map_fold_singleton {A B} (f : K A B B) (b : B) i x :
  map_fold f b {[i:=x]} = f i x b.
Proof.
  apply (map_fold_foldr _ [(i,x)]); [solve_proper|..].
  - intros j1 j2 z1 z2 y ?. rewrite !lookup_singleton_Some. naive_solver.
  - by rewrite map_to_list_singleton.
Qed.

Lemma map_fold_insert {A B} (R : relation B) `{!PreOrder R}
    (f : K A B B) (b : B) (i : K) (x : A) (m : M A) :
  ( j z, Proper (R ==> R) (f j z))
  ( j1 j2 z1 z2 y,
    j1 j2 <[i:=x]> m !! j1 = Some z1 <[i:=x]> m !! j2 = Some z2
    R (f j1 z1 (f j2 z2 y)) (f j2 z2 (f j1 z1 y)))
  m !! i = None
  R (map_fold f b (<[i:=x]> m)) (f i x (map_fold f b m)).
Proof.
  intros Hf_proper Hf Hi. trans (f i x (foldr (uncurry f) b (map_to_list m))).
  - apply (map_fold_foldr _ ((i,x) :: map_to_list m)); [solve_proper|done|].
    by rewrite map_to_list_insert by done.
  - f_equiv. apply (map_fold_foldr (flip R)); [solve_proper| |done].
    intros j1 j2 z1 z2 y ???.
    apply Hf; rewrite ?lookup_insert_Some; naive_solver.
Qed.

Lemma map_fold_insert_L {A B} (f : K A B B) (b : B) (i : K) (x : A) (m : M A) :
  ( j1 j2 z1 z2 y,
    j1 j2 <[i:=x]> m !! j1 = Some z1 <[i:=x]> m !! j2 = Some z2
    f j1 z1 (f j2 z2 y) = f j2 z2 (f j1 z1 y))
  m !! i = None
  map_fold f b (<[i:=x]> m) = f i x (map_fold f b m).
Proof. apply map_fold_insert; apply _. Qed.

Lemma map_fold_delete {A B} (R : relation B) `{!PreOrder R}
    (f : K A B B) (b : B) (i : K) (x : A) (m : M A) :
  ( j z, Proper (R ==> R) (f j z))
  ( j1 j2 z1 z2 y,
    j1 j2 m !! j1 = Some z1 m !! j2 = Some z2
    R (f j1 z1 (f j2 z2 y)) (f j2 z2 (f j1 z1 y)))
  m !! i = Some x
  R (map_fold f b m) (f i x (map_fold f b (delete i m))).
Proof.
  intros Hf_proper Hf Hi.
  rewrite <-map_fold_insert; [|done|done| |].
  - rewrite insert_delete; done.
  - intros j1 j2 z1 z2 y. rewrite insert_delete_insert, insert_id by done. auto.
  - rewrite lookup_delete; done.
Qed.

Lemma map_fold_delete_L {A B} (f : K A B B) (b : B) (i : K) (x : A) (m : M A) :
  ( j1 j2 z1 z2 y,
    j1 j2 m !! j1 = Some z1 m !! j2 = Some z2
    f j1 z1 (f j2 z2 y) = f j2 z2 (f j1 z1 y))
  m !! i = Some x
  map_fold f b m = f i x (map_fold f b (delete i m)).
Proof. apply map_fold_delete; apply _. Qed.

This lemma for commuting g in/out of a map_fold requires g to be Proper (second premise) and f to be associative/commutative (third premise). Those requirements do not show up for the equivalent lemmas on sets/multisets because their fold operation is defined in terms of foldr on lists, so we know that both folds (set_fold f (g x) m and set_fold f x m) happen in the same order. The map_fold_ind principle does not guarantee this happens for map_fold too.
Lemma map_fold_comm_acc_strong {A B} (R : relation B) `{!PreOrder R}
    (f : K A B B) (g : B B) (x : B) (m : M A) :
  ( j z, Proper (R ==> R) (f j z))
  Proper (R ==> R) g
  ( j1 j2 z1 z2 y,
    j1 j2 m !! j1 = Some z1 m !! j2 = Some z2
    R (f j1 z1 (f j2 z2 y)) (f j2 z2 (f j1 z1 y)))
  ( j z y, m !! j = Some z R (f j