Module pmap

This files implements an efficient implementation of finite maps whose keys range over Coq's data type of positive binary naturals positive. The implementation is based on Xavier Leroy's implementation of radix-2 search 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 mapset.
Require Export fin_maps.

Local Open Scope positive_scope.
Local Hint Extern 0 (@eq positive _ _) => congruence.
Local Hint Extern 0 (¬@eq positive _ _) => congruence.

The tree data structure

The data type Pmap_raw specifies radix-2 search trees. These trees do not ensure canonical representations of maps. For example the empty map can be represented as a binary tree of an arbitrary size that contains None at all nodes.
Inductive Pmap_raw A :=
  | PLeaf: Pmap_raw A
  | PNode: Pmap_raw Aoption APmap_raw APmap_raw A.
Arguments PLeaf {_}.
Arguments PNode {_} _ _ _.

Instance Pmap_raw_eq_dec `{∀ x y : A, Decision (x = y)} (x y : Pmap_raw A) :
  Decision (x = y).
Proof. solve_decision. Defined.

The following decidable predicate describes non empty trees.
Inductive Pmap_ne {A} : Pmap_raw AProp :=
  | Pmap_ne_val l x r : Pmap_ne (PNode l (Some x) r)
  | Pmap_ne_l l r : Pmap_ne lPmap_ne (PNode l None r)
  | Pmap_ne_r l r : Pmap_ne rPmap_ne (PNode l None r).
Local Hint Constructors Pmap_ne.

Instance Pmap_ne_dec {A} : ∀ t : Pmap_raw A, Decision (Pmap_ne t).
Proof.
 refine (
  fix go t :=
  match t return Decision (Pmap_ne t) with
  | PLeaf => right _
  | PNode _ (Some x) _ => left _
  | PNode l Node r => cast_if_or (go l) (go r)
  end); clear go; abstract first [constructor (by auto)|by inversion 1].
Defined.


The following predicate describes well well formed trees. A tree is well formed if it is empty or if all nodes at the bottom contain a value.
Inductive Pmap_wf {A} : Pmap_raw AProp :=
  | Pmap_wf_leaf : Pmap_wf PLeaf
  | Pmap_wf_node l x r : Pmap_wf lPmap_wf rPmap_wf (PNode l (Some x) r)
  | Pmap_wf_empty l r :
     Pmap_wf lPmap_wf r → (Pmap_ne lPmap_ne r) → Pmap_wf (PNode l None r).
Local Hint Constructors Pmap_wf.

Instance Pmap_wf_dec {A} : ∀ t : Pmap_raw A, Decision (Pmap_wf t).
Proof.
 refine (
  fix go t :=
  match t return Decision (Pmap_wf t) with
  | PLeaf => left _
  | PNode l (Some x) r => cast_if_and (go l) (go r)
  | PNode l Node r =>
     cast_if_and3 (decide (Pmap_ne lPmap_ne r)) (go l) (go r)
  end); clear go; abstract first [constructor (by auto)|by inversion 1].
Defined.


Now we restrict the data type of trees to those that are well formed and thereby obtain a data type that ensures canonicity.
Inductive Pmap A := PMap {
  pmap_car : Pmap_raw A;
  pmap_bool_prf : bool_decide (Pmap_wf pmap_car)
}.
Arguments PMap {_} _ _.
Arguments pmap_car {_} _.
Arguments pmap_bool_prf {_} _.
Definition PMap' {A} (t : Pmap_raw A) (p : Pmap_wf t) : Pmap A :=
  PMap t (bool_decide_pack _ p).
Definition pmap_prf {A} (m : Pmap A) : Pmap_wf (pmap_car m) :=
  bool_decide_unpack _ (pmap_bool_prf m).
Lemma Pmap_eq {A} (m1 m2 : Pmap A) : m1 = m2pmap_car m1 = pmap_car m2.
Proof.
  split; [by intros ->|intros]; destruct m1 as [t1 ?], m2 as [t2 ?].
  simplify_equality'; f_equal; apply proof_irrel.
Qed.


Operations on the data structure

Global Instance Pmap_eq_dec `{∀ x y : A, Decision (x = y)}
    (m1 m2 : Pmap A) : Decision (m1 = m2) :=
  match Pmap_raw_eq_dec (pmap_car m1) (pmap_car m2) with
  | left H => left (proj2 (Pmap_eq m1 m2) H)
  | right H => right (Hproj1 (Pmap_eq m1 m2))
  end.
Instance Pempty_raw {A} : Empty (Pmap_raw A) := PLeaf.
Global Instance Pempty {A} : Empty (Pmap A) := PMap' ∅ Pmap_wf_leaf.

Instance Plookup_raw {A} : Lookup positive A (Pmap_raw A) :=
  fix go (i : positive) (t : Pmap_raw A) {struct t} : option A :=
  match t with
  | PLeaf => None
  | PNode l o r =>
     match i with
     | 1 => o | i~0 => @lookup _ _ _ go i l | i~1 => @lookup _ _ _ go i r
     end
  end.
Instance Plookup {A} : Lookup positive A (Pmap A) := λ i m, pmap_car m !! i.

Lemma Plookup_empty {A} i : (∅ : Pmap_raw A) !! i = None.
Proof. by destruct i. Qed.

Lemma Pmap_ne_lookup {A} (t : Pmap_raw A) : Pmap_ne t → ∃ i x, t !! i = Some x.
Proof.
  induction 1 as [? x ?| l r ? IHl | l r ? IHr].
  * intros. by exists 1 x.
  * destruct IHl as [i [x ?]]. by exists (i~0) x.
  * destruct IHr as [i [x ?]]. by exists (i~1) x.
Qed.


Lemma Pmap_wf_eq_get {A} (t1 t2 : Pmap_raw A) :
  Pmap_wf t1Pmap_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].
  * destruct 1 as [| | ???? [?|?]]; intros Hget.
    + done.
    + discriminate (Hget 1).
    + 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.
  * destruct 1; intros Hget.
    + discriminate (Hget xH).
    + f_equal.
      - apply IHl; trivial. intros i. apply (Hget (i~0)).
      - apply (Hget 1).
      - apply IHr; trivial. intros i. apply (Hget (i~1)).
    + 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 r) as [i [??]]; trivial.
        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)).
Qed.


Fixpoint Psingleton_raw {A} (i : positive) (x : A) : Pmap_raw A :=
  match i with
  | 1 => PNode PLeaf (Some x) PLeaf
  | i~0 => PNode (Psingleton_raw i x) None PLeaf
  | i~1 => PNode PLeaf None (Psingleton_raw i x)
  end.
Lemma Psingleton_ne {A} i (x : A) : Pmap_ne (Psingleton_raw i x).
Proof. induction i; simpl; intuition. Qed.
Local Hint Resolve Psingleton_ne.
Lemma Psingleton_wf {A} i (x : A) : Pmap_wf (Psingleton_raw i x).
Proof. induction i; simpl; intuition. Qed.
Local Hint Resolve Psingleton_wf.
Lemma Plookup_singleton {A} i (x : A) : Psingleton_raw i x !! i = Some x.
Proof. by induction i. Qed.
Lemma Plookup_singleton_ne {A} i j (x : A) :
  ijPsingleton_raw i x !! j = None.
Proof. revert j. induction i; intros [?|?|]; simpl; auto. congruence. Qed.

Definition PNode_canon {A} (l : Pmap_raw A) (o : option A) (r : Pmap_raw A) :=
  match l, o, r with PLeaf, None, PLeaf => PLeaf | _, _, _ => PNode l o r end.
Lemma PNode_canon_wf {A} (l : Pmap_raw A) (o : option A) (r : Pmap_raw A) :
  Pmap_wf lPmap_wf rPmap_wf (PNode_canon l o r).
Proof. intros H1 H2. destruct H1, o, H2; simpl; intuition. Qed.
Local Hint Resolve PNode_canon_wf.
Lemma PNode_canon_lookup_xH {A} (l : Pmap_raw A) o (r : Pmap_raw A) :
  PNode_canon l o r !! 1 = o.
Proof. by destruct l,o,r. Qed.
Lemma PNode_canon_lookup_xO {A} (l : Pmap_raw A) o (r : Pmap_raw A) i :
  PNode_canon l o r !! i~0 = l !! i.
Proof. by destruct l,o,r. Qed.
Lemma PNode_canon_lookup_xI {A} (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 first
  [ 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) :=
  fix go f i t {struct t} : Pmap_raw A :=
  match t with
  | PLeaf => match f None with None => PLeaf | Some x => Psingleton_raw i x end
  | PNode l o r =>
     match i with
     | 1 => PNode_canon l (f o) r
     | i~0 => PNode_canon (@partial_alter _ _ _ go f i l) o r
     | i~1 => PNode_canon l o (@partial_alter _ _ _ go f i r)
     end
  end.
Lemma Ppartial_alter_wf {A} f i (t : Pmap_raw A) :
  Pmap_wf tPmap_wf (partial_alter f i t).
Proof.
  intros twf. revert i. induction twf.
  * unfold partial_alter. simpl. case (f None); intuition.
  * intros [?|?|]; simpl; intuition.
  * intros [?|?|]; simpl; intuition.
Qed.

Instance Ppartial_alter {A} : PartialAlter positive A (Pmap A) := λ f i m,
  PMap' (partial_alter f i (pmap_car m)) (Ppartial_alter_wf f i _ (pmap_prf m)).
Lemma Plookup_alter {A} f i (t : Pmap_raw A) :
  partial_alter f i t !! i = f (t !! i).
Proof.
  revert i. induction t.
  * intros i. change (match f None with Some x => Psingleton_raw i x
      | None => PLeaf end !! i = f None); destruct (f None).
    + intros. apply Plookup_singleton.
    + by destruct i.
  * intros [?|?|]; simpl; by PNode_canon_rewrite.
Qed.

Lemma Plookup_alter_ne {A} f i j (t : Pmap_raw A) :
  ijpartial_alter f i t !! j = t !! j.
Proof.
  revert i j. induction t as [|l IHl ? r IHr].
  * intros. change (match f None with Some x => Psingleton_raw i x
      | None => PLeaf end !! j = None); destruct (f None); [|done].
    intros. by apply Plookup_singleton_ne.
  * intros [?|?|] [?|?|]; simpl; PNode_canon_rewrite; auto; congruence.
Qed.


Instance Pfmap_raw : FMap Pmap_raw := λ A B f,
  fix go t :=
  match t with
  | PLeaf => PLeaf | PNode l x r => PNode (go l) (f <$> x) (go r)
  end.
Lemma Pfmap_ne `(f : AB) (t : Pmap_raw A) : Pmap_ne tPmap_ne (fmap f t).
Proof. induction 1; csimpl; auto. Qed.
Local Hint Resolve Pfmap_ne.
Lemma Pfmap_wf `(f : AB) (t : Pmap_raw A) : Pmap_wf tPmap_wf (fmap f t).
Proof. induction 1; csimpl; intuition. Qed.
Global Instance Pfmap : FMap Pmap := λ A B f m,
  PMap' (f <$> pmap_car m) (Pfmap_wf f _ (pmap_prf m)).
Lemma Plookup_fmap {A B} (f : AB) (t : Pmap_raw A) i :
  (f <$> t) !! i = f <$> t !! i.
Proof. revert i. induction t. done. by intros [?|?|]; simpl. Qed.

Fixpoint Pto_list_raw {A} (j : positive) (t : Pmap_raw A)
    (acc : list (positive * A)) : list (positive * A) :=
  match t with
  | PLeaf => acc
  | PNode l o r => default [] ox, [(Preverse j, x)]) ++
     Pto_list_raw (j~0) l (Pto_list_raw (j~1) r acc)
  end%list.
Lemma Pelem_of_to_list {A} (t : Pmap_raw A) j i acc x :
  (i,x) ∈ Pto_list_raw j t acc
    (∃ i', i = i' ++ Preverse jt !! i' = Some x) ∨ (i,x) ∈ acc.
Proof.
  split.
  { revert j acc. induction t as [|l IHl [y|] r IHr]; intros j acc; simpl.
    * by right.
    * rewrite elem_of_cons. intros [?|?]; simplify_equality.
      { left; exists 1. by rewrite (left_id_L 1 (++))%positive. }
      destruct (IHl (j~0) (Pto_list_raw j~1 r acc)) as [(i'&->&?)|?]; auto.
      { left; exists (i' ~ 0). by rewrite Preverse_xO, (associative_L _). }
      destruct (IHr (j~1) acc) as [(i'&->&?)|?]; auto.
      left; exists (i' ~ 1). by rewrite Preverse_xI, (associative_L _).
    * intros.
      destruct (IHl (j~0) (Pto_list_raw j~1 r acc)) as [(i'&->&?)|?]; auto.
      { left; exists (i' ~ 0). by rewrite Preverse_xO, (associative_L _). }
      destruct (IHr (j~1) acc) as [(i'&->&?)|?]; auto.
      left; exists (i' ~ 1). by rewrite Preverse_xI, (associative_L _). }
  revert t j i acc. assert (∀ t j i acc,
    (i, x) ∈ acc → (i, x) ∈ Pto_list_raw j t acc) as help.
  { intros t; induction t as [|l IHl [y|] r IHr]; intros j i acc;
      simpl; rewrite ?elem_of_cons; auto. }
  intros t j ? acc [(i&->&Hi)|?]; [|by auto]. revert j i acc Hi.
  induction t as [|l IHl [y|] r IHr]; intros j i acc ?; simpl.
  * done.
  * rewrite elem_of_cons. destruct i as [i|i|]; simplify_equality'.
    + right. apply help. specialize (IHr (j~1) i).
      rewrite Preverse_xI, (associative_L _) in IHr. by apply IHr.
    + right. specialize (IHl (j~0) i).
      rewrite Preverse_xO, (associative_L _) in IHl. by apply IHl.
    + left. by rewrite (left_id_L 1 (++))%positive.
  * destruct i as [i|i|]; simplify_equality'.
    + apply help. specialize (IHr (j~1) i).
      rewrite Preverse_xI, (associative_L _) in IHr. by apply IHr.
    + specialize (IHl (j~0) i).
      rewrite Preverse_xO, (associative_L _) in IHl. by apply IHl.
Qed.

Lemma Pto_list_nodup {A} j (t : Pmap_raw A) acc :
  (∀ i x, (i ++ Preverse j, x) ∈ acct !! i = None) →
  NoDup accNoDup (Pto_list_raw j t acc).
Proof.
  revert j acc. induction t as [|l IHl [y|] r IHr]; simpl; intros j acc Hin ?.
  * done.
  * repeat constructor.
    { rewrite Pelem_of_to_list. intros [(i&Hi&?)|Hj].
      { apply (f_equal Plength) in Hi.
        rewrite Preverse_xO, !Papp_length in Hi; simpl in *; lia. }
      rewrite Pelem_of_to_list in Hj. destruct Hj as [(i&Hi&?)|Hj].
      { apply (f_equal Plength) in Hi.
        rewrite Preverse_xI, !Papp_length in Hi; simpl in *; lia. }
      specialize (Hin 1 y). rewrite (left_id_L 1 (++))%positive in Hin.
      discriminate (Hin Hj). }
   apply IHl.
   { intros i x. rewrite Pelem_of_to_list. intros [(?&Hi&?)|Hi].
     + rewrite Preverse_xO, Preverse_xI, !(associative_L _) in Hi.
       by apply (injective (++ _)) in Hi.
     + apply (Hin (i~0) x). by rewrite Preverse_xO, (associative_L _) in Hi. }
   apply IHr; auto. intros i x Hi.
   apply (Hin (i~1) x). by rewrite Preverse_xI, (associative_L _) in Hi.
 * apply IHl.
   { intros i x. rewrite Pelem_of_to_list. intros [(?&Hi&?)|Hi].
     + rewrite Preverse_xO, Preverse_xI, !(associative_L _) in Hi.
       by apply (injective (++ _)) in Hi.
     + apply (Hin (i~0) x). by rewrite Preverse_xO, (associative_L _) in Hi. }
   apply IHr; auto. intros i x Hi.
   apply (Hin (i~1) x). by rewrite Preverse_xI, (associative_L _) in Hi.
Qed.

Global Instance Pto_list {A} : FinMapToList positive A (Pmap A) := λ m,
  Pto_list_raw 1 (pmap_car m) [].

Instance Pomap_raw : OMap Pmap_raw := λ A B f,
  fix go t :=
  match t with
  | PLeaf => PLeaf | PNode l o r => PNode_canon (go l) (o ≫= f) (go r)
  end.
Lemma Pomap_wf {A B} (f : Aoption B) (t : Pmap_raw A) :
  Pmap_wf tPmap_wf (omap f t).
Proof. induction 1; csimpl; auto. Qed.
Local Hint Resolve Pomap_wf.
Lemma Pomap_lookup {A B} (f : Aoption B) (t : Pmap_raw A) i :
  omap f t !! i = t !! i ≫= f.
Proof.
  revert i. induction t as [| l IHl o r IHr ]; [done |].
  intros [?|?|]; csimpl; PNode_canon_rewrite; auto.
Qed.

Global Instance Pomap: OMap Pmap := λ A B f m,
  PMap' (omap f (pmap_car m)) (Pomap_wf f _ (pmap_prf m)).

Instance Pmerge_raw : Merge Pmap_raw :=
  fix Pmerge_raw A B C f t1 t2 : Pmap_raw C :=
  match t1, t2 with
  | PLeaf, t2 => omap (f NoneSome) t2
  | t1, PLeaf => omap (flip f NoneSome) t1
  | PNode l1 o1 r1, PNode l2 o2 r2 =>
     PNode_canon (@merge _ Pmerge_raw A B C f l1 l2)
      (f o1 o2) (@merge _ Pmerge_raw A B C f r1 r2)
  end.
Local Arguments omap _ _ _ _ _ _ : simpl never.
Lemma Pmerge_wf {A B C} (f : option Aoption Boption C) t1 t2 :
  Pmap_wf t1Pmap_wf t2Pmap_wf (merge f t1 t2).
Proof. intros t1wf. revert t2. induction t1wf; destruct 1; simpl; auto. Qed.
Global Instance Pmerge: Merge Pmap := λ A B C f m1 m2,
  PMap' _ (Pmerge_wf f _ _ (pmap_prf m1) (pmap_prf m2)).
Lemma Pmerge_spec {A B C} (f : option Aoption Boption C)
    (Hf : f None None = None) (t1 : Pmap_raw A) t2 i :
  merge f t1 t2 !! i = f (t1 !! i) (t2 !! i).
Proof.
  revert t2 i. induction t1 as [|l1 IHl1 o1 r1 IHr1]; intros t2 i.
  { unfold merge; simpl. rewrite Pomap_lookup. by destruct (t2 !! i). }
  destruct t2 as [|l2 o2 r2].
  * unfold merge, Pmerge_raw. rewrite Pomap_lookup.
    by destruct (PNode _ _ _ !! i).
  * destruct i; simpl; by PNode_canon_rewrite.
Qed.


Instantiation of the finite map interface

Global Instance: FinMap positive Pmap.
Proof.
  split.
  * intros ? [t1 ?] [t2 ?] ?. apply Pmap_eq; simpl.
    apply Pmap_wf_eq_get; trivial; by apply (bool_decide_unpack _).
  * by intros ? [].
  * intros ?? [??] ?. by apply Plookup_alter.
  * intros ?? [??] ??. by apply Plookup_alter_ne.
  * intros ??? [??]. by apply Plookup_fmap.
  * intros ? [??]. apply Pto_list_nodup; [|constructor].
    intros ??. by rewrite elem_of_nil.
  * intros ? [??] i x; unfold map_to_list, Pto_list.
    rewrite Pelem_of_to_list, elem_of_nil.
    split. by intros [(?&->&?)|]. by left; exists i.
  * intros ?? ? [??] ?. by apply Pomap_lookup.
  * intros ??? ?? [??] [??] ?. by apply Pmerge_spec.
Qed.


Finite sets

We construct sets of positivess satisfying extensional equality.
Notation Pset := (mapset (Pmap unit)).
Instance Pmap_dom {A} : Dom (Pmap A) Pset := mapset_dom.
Instance: FinMapDom positive Pmap Pset := mapset_dom_spec.

Fresh numbers

Fixpoint Pdepth {A} (m : Pmap_raw A) : nat :=
  match m with
  | PLeaf | PNode _ None _ => O | PNode l _ _ => S (Pdepth l)
  end.
Fixpoint Pfresh_at_depth {A} (m : Pmap_raw A) (d : nat) : option positive :=
  match d, m with
  | O, (PLeaf | PNode _ None _) => Some 1
  | S d, PNode l _ r =>
     match Pfresh_at_depth l d with
     | Some i => Some (i~0) | None => (~1) <$> Pfresh_at_depth r d
     end
  | _, _ => None
  end.
Fixpoint Pfresh_go {A} (m : Pmap_raw A) (d : nat) : option positive :=
  match d with
  | O => None
  | S d =>
     match Pfresh_go m d with
     | Some i => Some i | None => Pfresh_at_depth m d
     end
  end.
Definition Pfresh {A} (m : Pmap A) : positive :=
  let d := Pdepth (pmap_car m) in
  match Pfresh_go (pmap_car m) d with
  | Some i => i | None => Pos.shiftl_nat 1 d
  end.

Lemma Pfresh_at_depth_fresh {A} (m : Pmap_raw A) d i :
  Pfresh_at_depth m d = Some im !! i = None.
Proof.
  revert i m; induction d as [|d IH].
  { intros i [|l [] r] ?; naive_solver. }
  intros i [|l o r] ?; simplify_equality'.
  destruct (Pfresh_at_depth l d) as [i'|] eqn:?,
    (Pfresh_at_depth r d) as [i''|] eqn:?; simplify_equality'; auto.
Qed.

Lemma Pfresh_go_fresh {A} (m : Pmap_raw A) d i :
  Pfresh_go m d = Some im !! i = None.
Proof.
  induction d as [|d IH]; intros; simplify_equality'.
  destruct (Pfresh_go m d); eauto using Pfresh_at_depth_fresh.
Qed.

Lemma Pfresh_depth {A} (m : Pmap_raw A) :
  m !! Pos.shiftl_nat 1 (Pdepth m) = None.
Proof. induction m as [|l IHl [x|] r IHr]; auto. Qed.
Lemma Pfresh_fresh {A} (m : Pmap A) : m !! Pfresh m = None.
Proof.
  destruct m as [m ?]; unfold lookup, Plookup, Pfresh; simpl.
  destruct (Pfresh_go m _) eqn:?; eauto using Pfresh_go_fresh, Pfresh_depth.
Qed.


Instance Pset_fresh : Fresh positive Pset := λ X,
  let (m) := X in Pfresh m.
Instance Pset_fresh_spec : FreshSpec positive Pset.
Proof.
  split.
  * apply _.
  * intros X Y; rewrite <-elem_of_equiv_L. by intros ->.
  * unfold elem_of, mapset_elem_of, fresh; intros [m]; simpl.
    by rewrite Pfresh_fresh.
Qed.