skylabs.prelude.gmap
(*
* Copyright (c) 2021-2025 BlueRock Security, Inc.
* This software is distributed under the terms of the BedRock Open-Source License.
* See the LICENSE-BedRock file in the repository root for details.
*)
Require Export stdpp.gmap.
Require Export stdpp.mapset.
Require Import skylabs.prelude.base.
Require Import skylabs.prelude.fin_sets.
Require Import skylabs.prelude.list_numbers.
(* To upstream to Iris: using mapseq_eq directly would unfold a TC opaque
definition and interfere with TC search. *)
Lemma gset_eq `{Countable A} (X1 X2 : gset A) : X1 = X2 ↔ ∀ x, x ∈ X1 ↔ x ∈ X2.
Proof. apply mapset_eq. Qed.
* Copyright (c) 2021-2025 BlueRock Security, Inc.
* This software is distributed under the terms of the BedRock Open-Source License.
* See the LICENSE-BedRock file in the repository root for details.
*)
Require Export stdpp.gmap.
Require Export stdpp.mapset.
Require Import skylabs.prelude.base.
Require Import skylabs.prelude.fin_sets.
Require Import skylabs.prelude.list_numbers.
(* To upstream to Iris: using mapseq_eq directly would unfold a TC opaque
definition and interfere with TC search. *)
Lemma gset_eq `{Countable A} (X1 X2 : gset A) : X1 = X2 ↔ ∀ x, x ∈ X1 ↔ x ∈ X2.
Proof. apply mapset_eq. Qed.
set_map specialized to gset; avoids awkward type annotations such as
set_map (C := gset _) (D := gset _).
#[global] Notation gset_map := (set_map (C := gset _) (D := gset _)) (only parsing).
#[global] Notation gset_concat_map :=
(set_concat_map (C := gset _) (D := gset _)) (only parsing).
Definition gmap_kvmap {A} `{Countable K1, Countable K2}
(f : K1 -> A -> option (K2 * A))
(m : gmap K1 A) : gmap K2 A :=
list_to_map (omap (uncurry f) (map_to_list m)).
#[global] Notation gset_concat_map :=
(set_concat_map (C := gset _) (D := gset _)) (only parsing).
Definition gmap_kvmap {A} `{Countable K1, Countable K2}
(f : K1 -> A -> option (K2 * A))
(m : gmap K1 A) : gmap K2 A :=
list_to_map (omap (uncurry f) (map_to_list m)).
Variant of kmap for gmap only.
Definition gmap_okmap {A} `{Countable K1, Countable K2}
(f : K1 -> option K2)
(m : gmap K1 A) : gmap K2 A :=
gmap_kvmap (fun k1 v => k2 ← f k1; Some (k2, v)) m.
Definition gmap_preimage `{Countable K, Countable A} : gmap K A -> gmap A (gset K) :=
map_preimg.
(* Like set_concat_map, but purely in terms of gsets.
*)
Section gset_bind.
Context `{Countable A} `{Countable B}.
Definition gset_bind (f : A → gset B) (xs : gset A) : gset B :=
gset_concat_map (elements ∘ f) xs.
Lemma gset_bind_empty f :
gset_bind f ∅ = ∅.
Proof. done. Qed.
Lemma elem_of_gset_bind f b xs :
b ∈ gset_bind f xs ↔ ∃ x, x ∈ xs ∧ b ∈ f x.
Proof. set_solver. Qed.
#[global] Instance set_unfold_gset_bind f b xs P Q :
(∀ x, SetUnfoldElemOf x xs (P x)) → (∀ x, SetUnfoldElemOf b (f x) (Q x)) →
SetUnfoldElemOf b (gset_bind f xs) (∃ x, P x ∧ Q x).
Proof. constructor. rewrite elem_of_gset_bind. set_solver. Qed.
Lemma gset_bind_union f xs1 xs2 :
gset_bind f (xs1 ∪ xs2) =
gset_bind f xs1 ∪ gset_bind f xs2.
Proof. set_solver. Qed.
Lemma gset_bind_singleton f a :
gset_bind f {[ a ]} = f a.
Proof. set_solver. Qed.
End gset_bind.
Section lookup_insert.
Lemma lookup_insert_iff `{Countable K, A} (m : gmap K A) k k' a :
<[ k := a ]> m !! k' = if bool_decide (k = k') then Some a else m !! k'.
Proof. by case: bool_decide_reflect => [<-|?]; rewrite (lookup_insert, lookup_insert_ne). Qed.
End lookup_insert.
Lemma gmap_dom_empty {K V} `{Countable K} {m : gmap K V} :
dom m = dom (empty (A := gmap K V)) -> m = ∅.
Proof. rewrite dom_empty_L. apply dom_empty_inv_L. Qed.
Lemma gmap_dom_insert {K V} `{Countable K} {k v} {m1 m2 : gmap K V}
(Hm1 : m1 !! k = Some v)
(Hm2 : m2 !! k = None)
(Hdom : dom m1 = {[k]} ∪ dom m2) :
∃ m1', m1 = <[k := v]> m1' ∧ m1' !! k = None ∧ dom m1' = dom m2.
Proof.
exists (delete k m1).
have Hm1' : delete k m1 !! k = None by exact: lookup_delete.
have Heq : m1 = <[k:=v]> (delete k m1) by rewrite insert_delete_insert insert_id.
rewrite Heq dom_insert_L in Hdom. split_and! => // {Hm1 Heq}.
move: m1 (delete _ _) Hm1' Hm2 Hdom => _ m1.
move=>/not_elem_of_dom Hm1 /not_elem_of_dom Hm2.
rewrite !set_eq.
(* set_solver Works, but I found the faster manual proof first. *)
move=> + k' => /(_ k').
rewrite !elem_of_union elem_of_singleton.
naive_solver.
Qed.
Lemma gmap_dom_insert' {K V} `{Countable K} (m1 : gmap K V) {k} {m2 : gmap K V}
(Hm2 : m2 !! k = None)
(Hdom : dom m1 = {[k]} ∪ dom m2) :
∃ v (m1' : gmap K V), m1 !! k = Some v ∧ m1 = <[k := v]> m1' ∧
m1' !! k = None ∧ dom m1' = dom m2.
Proof.
destruct (m1 !! k) as [v|] eqn:Hm1.
{ destruct (gmap_dom_insert Hm1 Hm2 Hdom) as [m1']. exists v, m1'. intuition. }
exfalso. move: Hm1 => /not_elem_of_dom.
set_solver.
Qed.
(f : K1 -> option K2)
(m : gmap K1 A) : gmap K2 A :=
gmap_kvmap (fun k1 v => k2 ← f k1; Some (k2, v)) m.
Definition gmap_preimage `{Countable K, Countable A} : gmap K A -> gmap A (gset K) :=
map_preimg.
(* Like set_concat_map, but purely in terms of gsets.
*)
Section gset_bind.
Context `{Countable A} `{Countable B}.
Definition gset_bind (f : A → gset B) (xs : gset A) : gset B :=
gset_concat_map (elements ∘ f) xs.
Lemma gset_bind_empty f :
gset_bind f ∅ = ∅.
Proof. done. Qed.
Lemma elem_of_gset_bind f b xs :
b ∈ gset_bind f xs ↔ ∃ x, x ∈ xs ∧ b ∈ f x.
Proof. set_solver. Qed.
#[global] Instance set_unfold_gset_bind f b xs P Q :
(∀ x, SetUnfoldElemOf x xs (P x)) → (∀ x, SetUnfoldElemOf b (f x) (Q x)) →
SetUnfoldElemOf b (gset_bind f xs) (∃ x, P x ∧ Q x).
Proof. constructor. rewrite elem_of_gset_bind. set_solver. Qed.
Lemma gset_bind_union f xs1 xs2 :
gset_bind f (xs1 ∪ xs2) =
gset_bind f xs1 ∪ gset_bind f xs2.
Proof. set_solver. Qed.
Lemma gset_bind_singleton f a :
gset_bind f {[ a ]} = f a.
Proof. set_solver. Qed.
End gset_bind.
Section lookup_insert.
Lemma lookup_insert_iff `{Countable K, A} (m : gmap K A) k k' a :
<[ k := a ]> m !! k' = if bool_decide (k = k') then Some a else m !! k'.
Proof. by case: bool_decide_reflect => [<-|?]; rewrite (lookup_insert, lookup_insert_ne). Qed.
End lookup_insert.
Lemma gmap_dom_empty {K V} `{Countable K} {m : gmap K V} :
dom m = dom (empty (A := gmap K V)) -> m = ∅.
Proof. rewrite dom_empty_L. apply dom_empty_inv_L. Qed.
Lemma gmap_dom_insert {K V} `{Countable K} {k v} {m1 m2 : gmap K V}
(Hm1 : m1 !! k = Some v)
(Hm2 : m2 !! k = None)
(Hdom : dom m1 = {[k]} ∪ dom m2) :
∃ m1', m1 = <[k := v]> m1' ∧ m1' !! k = None ∧ dom m1' = dom m2.
Proof.
exists (delete k m1).
have Hm1' : delete k m1 !! k = None by exact: lookup_delete.
have Heq : m1 = <[k:=v]> (delete k m1) by rewrite insert_delete_insert insert_id.
rewrite Heq dom_insert_L in Hdom. split_and! => // {Hm1 Heq}.
move: m1 (delete _ _) Hm1' Hm2 Hdom => _ m1.
move=>/not_elem_of_dom Hm1 /not_elem_of_dom Hm2.
rewrite !set_eq.
(* set_solver Works, but I found the faster manual proof first. *)
move=> + k' => /(_ k').
rewrite !elem_of_union elem_of_singleton.
naive_solver.
Qed.
Lemma gmap_dom_insert' {K V} `{Countable K} (m1 : gmap K V) {k} {m2 : gmap K V}
(Hm2 : m2 !! k = None)
(Hdom : dom m1 = {[k]} ∪ dom m2) :
∃ v (m1' : gmap K V), m1 !! k = Some v ∧ m1 = <[k := v]> m1' ∧
m1' !! k = None ∧ dom m1' = dom m2.
Proof.
destruct (m1 !! k) as [v|] eqn:Hm1.
{ destruct (gmap_dom_insert Hm1 Hm2 Hdom) as [m1']. exists v, m1'. intuition. }
exfalso. move: Hm1 => /not_elem_of_dom.
set_solver.
Qed.