(*
 * Vericert: Verified high-level synthesis.
 * Copyright (C) 2020 Yann Herklotz <yann@yannherklotz.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *)

From vericert Require Import Vericertlib ValueInt.
From compcert Require Import Maps.

Definition reg := positive.

Module AssocMap := Maps.PTree.

Module AssocMapExt.
  Import AssocMap.

  Hint Resolve elements_correct elements_complete
       elements_keys_norepet : assocmap.
  Hint Resolve gso gss : assocmap.

  Section Operations.

    Variable A : Type.

    Definition get_default (a : A) (k : reg) (m : t A) : A :=
      match get k m with
      | None => a
      | Some v => v
      end.

    Fixpoint merge (m1 m2 : t A) : t A :=
      match m1, m2 with
      | Node l1 (Some a) r1, Node l2 _ r2 => Node (merge l1 l2) (Some a) (merge r1 r2)
      | Node l1 None r1, Node l2 o r2 => Node (merge l1 l2) o (merge r1 r2)
      | Leaf, _ => m2
      | _, _ => m1
      end.

    
A:Type

forall am : t A, merge (empty A) am = am
A:Type

forall am : t A, merge (empty A) am = am
auto. Qed. Hint Resolve merge_base_1 : assocmap.
A:Type

forall am : t A, merge am (empty A) = am
A:Type

forall am : t A, merge am (empty A) = am
A:Type

forall am : t A, (fix merge (m1 m2 : t A) {struct m1} : t A := match m1 with | Leaf => m2 | Node l1 (Some a) r1 => match m2 with | Leaf => m1 | Node l2 _ r2 => Node (merge l1 l2) (Some a) (merge r1 r2) end | Node l1 None r1 => match m2 with | Leaf => m1 | Node l2 o r2 => Node (merge l1 l2) o (merge r1 r2) end end) am (empty A) = am
A:Type
am1:tree A
o:option A
am2:tree A

match o with | Some a => match empty A with | Leaf => Node am1 o am2 | Node l2 _ r2 => Node ((fix merge (m1 m2 : t A) {struct m1} : t A := match m1 with | Leaf => m2 | Node l1 (Some a0) r1 => match m2 with | Leaf => m1 | Node l3 _ r3 => Node (merge l1 l3) (Some a0) (merge r1 r3) end | Node l1 None r1 => match m2 with | Leaf => m1 | Node l3 o1 r3 => Node (merge l1 l3) o1 (merge r1 r3) end end) am1 l2) (Some a) ((fix merge (m1 m2 : t A) {struct m1} : t A := match m1 with | Leaf => m2 | Node l1 (Some a0) r1 => match m2 with | Leaf => m1 | Node l3 _ r3 => Node (merge l1 l3) (Some a0) (merge r1 r3) end | Node l1 None r1 => match m2 with | Leaf => m1 | Node l3 o1 r3 => Node (merge l1 l3) o1 (merge r1 r3) end end) am2 r2) end | None => match empty A with | Leaf => Node am1 o am2 | Node l2 o r2 => Node ((fix merge (m1 m2 : t A) {struct m1} : t A := match m1 with | Leaf => m2 | Node l1 (Some a) r1 => match m2 with | Leaf => m1 | Node l3 _ r3 => Node (merge l1 l3) (Some a) (merge r1 r3) end | Node l1 None r1 => match m2 with | Leaf => m1 | Node l3 o1 r3 => Node (merge l1 l3) o1 (merge r1 r3) end end) am1 l2) o ((fix merge (m1 m2 : t A) {struct m1} : t A := match m1 with | Leaf => m2 | Node l1 (Some a) r1 => match m2 with | Leaf => m1 | Node l3 _ r3 => Node (merge l1 l3) (Some a) (merge r1 r3) end | Node l1 None r1 => match m2 with | Leaf => m1 | Node l3 o1 r3 => Node (merge l1 l3) o1 (merge r1 r3) end end) am2 r2) end end = Node am1 o am2
destruct o; trivial. Qed. Hint Resolve merge_base_2 : assocmap.
A:Type

forall (r : positive) (am am' : t A) (v : A), merge (set r v am) am' = set r v (merge am am')
A:Type

forall (r : positive) (am am' : t A) (v : A), merge (set r v am) am' = set r v (merge am am')
induction r; intros; destruct am; destruct am'; try (destruct o); simpl; try rewrite IHr; try reflexivity. Qed. Hint Resolve merge_add_assoc : assocmap.
A:Type

forall (am bm : t A) (k : positive) (v : A), am ! k = Some v -> (merge am bm) ! k = Some v
A:Type

forall (am bm : t A) (k : positive) (v : A), am ! k = Some v -> (merge am bm) ! k = Some v
induction am; intros; destruct k; destruct bm; try (destruct o); simpl; try rewrite gempty in H; try discriminate; try assumption; auto. Qed. Hint Resolve merge_correct_1 : assocmap.
A:Type

forall (am bm : t A) (k : positive) (v : A), am ! k = None -> bm ! k = Some v -> (merge am bm) ! k = Some v
A:Type

forall (am bm : t A) (k : positive) (v : A), am ! k = None -> bm ! k = Some v -> (merge am bm) ! k = Some v
induction am; intros; destruct k; destruct bm; try (destruct o); simpl; try rewrite gempty in H; try discriminate; try assumption; auto. Qed. Hint Resolve merge_correct_2 : assocmap. Definition merge_fold (am bm : t A) : t A := fold_right (fun p a => set (fst p) (snd p) a) bm (elements am).
A:Type

forall (k : elt) (v : A) (l : list (elt * A)) (bm : t A), In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
A:Type

forall (k : elt) (v : A) (l : list (elt * A)) (bm : t A), In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
A:Type
k:elt
v:A
bm:t A
H:In (k, v) nil
H0:list_norepet (List.map fst nil)

(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm nil) ! k = Some v
A:Type
k:elt
v:A
a:(elt * A)%type
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) (a :: l)
H0:list_norepet (List.map fst (a :: l))
(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm (a :: l)) ! k = Some v
A:Type
k:elt
v:A
bm:t A
H:In (k, v) nil
H0:list_norepet (List.map fst nil)

(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm nil) ! k = Some v
contradiction.
A:Type
k:elt
v:A
a:(elt * A)%type
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) (a :: l)
H0:list_norepet (List.map fst (a :: l))

(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm (a :: l)) ! k = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))

(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k', v') :: l)) ! k = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
e:k = k'

(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k', v') :: l)) ! k = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
n:k <> k'
(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k', v') :: l)) ! k = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
e:k = k'

(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k', v') :: l)) ! k = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
e:k = k'
H1:(k', v') = (k, v)

(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k', v') :: l)) ! k = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
e:k = k'
H1:In (k, v) l
(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k', v') :: l)) ! k = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
e:k = k'
H1:(k', v') = (k, v)
H3:k' = k
H4:v' = v

(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k, v) :: l)) ! k = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
e:k = k'
H1:In (k, v) l
(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k', v') :: l)) ! k = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
e:k = k'
H1:(k', v') = (k, v)
H3:k' = k
H4:v' = v
hd:elt
tl:list elt
H6:~ In k' (List.map fst l)
H7:list_norepet (List.map fst l)
H2:hd = k'
H5:tl = List.map fst l

(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k, v) :: l)) ! k = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
e:k = k'
H1:In (k, v) l
(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k', v') :: l)) ! k = Some v
A:Type
v:A
k':elt
l:list (elt * A)
IHl:forall bm : t A, In (k', v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k' = Some v
bm:t A
H0:list_norepet (List.map fst ((k', v) :: l))
H:In (k', v) ((k', v) :: l)
H3:k' = k'
H1:(k', v) = (k', v)
H6:~ In k' (List.map fst l)
H7:list_norepet (List.map fst l)

(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k', v) :: l)) ! k' = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
e:k = k'
H1:In (k, v) l
(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k', v') :: l)) ! k = Some v
A:Type
v:A
k':elt
l:list (elt * A)
IHl:forall bm : t A, In (k', v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k' = Some v
bm:t A
H0:list_norepet (List.map fst ((k', v) :: l))
H:In (k', v) ((k', v) :: l)
H3:k' = k'
H1:(k', v) = (k', v)
H6:~ In k' (List.map fst l)
H7:list_norepet (List.map fst l)

(set k' v (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l)) ! k' = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
e:k = k'
H1:In (k, v) l
(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k', v') :: l)) ! k = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
e:k = k'
H1:In (k, v) l

(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k', v') :: l)) ! k = Some v
A:Type
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k', v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k' = Some v
bm:t A
H:In (k', v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
H1:In (k', v) l
H4:~ In k' (List.map fst l)
H5:list_norepet (List.map fst l)

(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k', v') :: l)) ! k' = Some v
A:Type
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k', v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k' = Some v
bm:t A
H:In (k', v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
H1:In (fst (k', v)) (List.map fst l)
H4:~ In k' (List.map fst l)
H5:list_norepet (List.map fst l)

(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k', v') :: l)) ! k' = Some v
contradiction.
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
n:k <> k'

(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k', v') :: l)) ! k = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
n:k <> k'
H1:(k', v') = (k, v)

(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k', v') :: l)) ! k = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
n:k <> k'
H1:In (k, v) l
(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k', v') :: l)) ! k = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
n:k <> k'
H1:(k', v') = (k, v)
H3:k' = k
H4:v' = v

(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k, v) :: l)) ! k = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
n:k <> k'
H1:In (k, v) l
(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k', v') :: l)) ! k = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
n:k <> k'
H1:(k', v') = (k, v)
H3:k' = k
H4:v' = v
hd:elt
tl:list elt
H6:~ In k' (List.map fst l)
H7:list_norepet (List.map fst l)
H2:hd = k'
H5:tl = List.map fst l

(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k, v) :: l)) ! k = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
n:k <> k'
H1:In (k, v) l
(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k', v') :: l)) ! k = Some v
A:Type
k:elt
v:A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H1:(k, v) = (k, v)
n:k <> k
H:In (k, v) ((k, v) :: l)
H0:list_norepet (List.map fst ((k, v) :: l))
H6:~ In k (List.map fst l)
H7:list_norepet (List.map fst l)

(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k, v) :: l)) ! k = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
n:k <> k'
H1:In (k, v) l
(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k', v') :: l)) ! k = Some v
A:Type
k:elt
v:A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H1:(k, v) = (k, v)
n:k <> k
H:In (k, v) ((k, v) :: l)
H0:list_norepet (List.map fst ((k, v) :: l))
H6:~ In k (List.map fst l)
H7:list_norepet (List.map fst l)

(set k v (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l)) ! k = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
n:k <> k'
H1:In (k, v) l
(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k', v') :: l)) ! k = Some v
A:Type
k:elt
v:A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H1:(k, v) = (k, v)
n:k <> k
H:In (k, v) ((k, v) :: l)
H0:list_norepet (List.map fst ((k, v) :: l))
H6:~ In k (List.map fst l)
H7:list_norepet (List.map fst l)

(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
n:k <> k'
H1:In (k, v) l
(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k', v') :: l)) ! k = Some v
A:Type
k:elt
v:A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H1:(k, v) = (k, v)
n:k <> k
H:In (k, v) ((k, v) :: l)
H0:list_norepet (List.map fst ((k, v) :: l))
H6:~ In k (List.map fst l)
H7:list_norepet (List.map fst l)

In (k, v) l
A:Type
k:elt
v:A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H1:(k, v) = (k, v)
n:k <> k
H:In (k, v) ((k, v) :: l)
H0:list_norepet (List.map fst ((k, v) :: l))
H6:~ In k (List.map fst l)
H7:list_norepet (List.map fst l)
list_norepet (List.map fst l)
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
n:k <> k'
H1:In (k, v) l
(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k', v') :: l)) ! k = Some v
A:Type
k:elt
v:A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H1:(k, v) = (k, v)
n:k <> k
H:In (k, v) ((k, v) :: l)
H0:list_norepet (List.map fst ((k, v) :: l))
H6:~ In k (List.map fst l)
H7:list_norepet (List.map fst l)

list_norepet (List.map fst l)
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
n:k <> k'
H1:In (k, v) l
(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k', v') :: l)) ! k = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
n:k <> k'
H1:In (k, v) l

(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k', v') :: l)) ! k = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
n:k <> k'
H1:In (k, v) l

(set k' v' (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l)) ! k = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
n:k <> k'
H1:In (k, v) l

(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
n:k <> k'
H1:In (k, v) l

In (k, v) l
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
n:k <> k'
H1:In (k, v) l
list_norepet (List.map fst l)
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
n:k <> k'
H1:In (k, v) l

list_norepet (List.map fst l)
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
n:k <> k'
H1:In (k, v) l
hd:elt
tl:list elt
H4:~ In k' (List.map fst l)
H5:list_norepet (List.map fst l)
H2:hd = k'
H3:tl = List.map fst l

list_norepet (List.map fst l)
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, In (k, v) l -> list_norepet (List.map fst l) -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:In (k, v) ((k', v') :: l)
H0:list_norepet (List.map fst ((k', v') :: l))
n:k <> k'
H1:In (k, v) l
H4:~ In k' (List.map fst l)
H5:list_norepet (List.map fst l)

list_norepet (List.map fst l)
assumption. Qed. Hint Resolve add_assoc : assocmap.
A:Type

forall (k : elt) (v : A) (l : list (elt * A)) (bm : t A), ~ In k (List.map fst l) -> bm ! k = Some v -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
A:Type

forall (k : elt) (v : A) (l : list (elt * A)) (bm : t A), ~ In k (List.map fst l) -> bm ! k = Some v -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
A:Type
k:elt
v:A
bm:t A
H:~ In k (List.map fst nil)
H0:bm ! k = Some v

(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm nil) ! k = Some v
A:Type
k:elt
v:A
a:(elt * A)%type
l:list (elt * A)
IHl:forall bm : t A, ~ In k (List.map fst l) -> bm ! k = Some v -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:~ In k (List.map fst (a :: l))
H0:bm ! k = Some v
(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm (a :: l)) ! k = Some v
A:Type
k:elt
v:A
bm:t A
H:~ In k (List.map fst nil)
H0:bm ! k = Some v

(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm nil) ! k = Some v
assumption.
A:Type
k:elt
v:A
a:(elt * A)%type
l:list (elt * A)
IHl:forall bm : t A, ~ In k (List.map fst l) -> bm ! k = Some v -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:~ In k (List.map fst (a :: l))
H0:bm ! k = Some v

(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm (a :: l)) ! k = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, ~ In k (List.map fst l) -> bm ! k = Some v -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:~ In k (List.map fst ((k', v') :: l))
H0:bm ! k = Some v

(fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm ((k', v') :: l)) ! k = Some v
A:Type
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, ~ In k' (List.map fst l) -> bm ! k' = Some v -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k' = Some v
bm:t A
H0:bm ! k' = Some v
H:k' <> k'
H1:~ In k' (List.map fst l)

(set k' v' (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l)) ! k' = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, ~ In k (List.map fst l) -> bm ! k = Some v -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:k' <> k
H1:~ In k (List.map fst l)
H0:bm ! k = Some v
n:k <> k'
(set k' v' (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l)) ! k = Some v
A:Type
k:elt
v:A
k':elt
v':A
l:list (elt * A)
IHl:forall bm : t A, ~ In k (List.map fst l) -> bm ! k = Some v -> (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l) ! k = Some v
bm:t A
H:k' <> k
H1:~ In k (List.map fst l)
H0:bm ! k = Some v
n:k <> k'

(set k' v' (fold_right (fun (p : positive * A) (a : t A) => set (fst p) (snd p) a) bm l)) ! k = Some v
rewrite AssocMap.gso; auto. Qed. Hint Resolve not_in_assoc : assocmap.
A:Type

forall (am : t A) (k : positive), (exists v : A, am ! k = Some v) <-> In k (List.map fst (elements am))
A:Type

forall (am : t A) (k : positive), (exists v : A, am ! k = Some v) <-> In k (List.map fst (elements am))
A:Type
am:t A
k:positive
H:exists v : A, am ! k = Some v

In k (List.map fst (elements am))
A:Type
am:t A
k:positive
H:In k (List.map fst (elements am))
exists v : A, am ! k = Some v
A:Type
am:t A
k:positive
x:A
H:am ! k = Some x

In k (List.map fst (elements am))
A:Type
am:t A
k:positive
H:In k (List.map fst (elements am))
exists v : A, am ! k = Some v
A:Type
am:t A
k:positive
x:A
H:In (k, x) (elements am)

In k (List.map fst (elements am))
A:Type
am:t A
k:positive
H:In k (List.map fst (elements am))
exists v : A, am ! k = Some v
A:Type
am:t A
k:positive
x:A
H:In (fst (k, x)) (List.map fst (elements am))

In k (List.map fst (elements am))
A:Type
am:t A
k:positive
H:In k (List.map fst (elements am))
exists v : A, am ! k = Some v
A:Type
am:t A
k:positive
H:In k (List.map fst (elements am))

exists v : A, am ! k = Some v
A:Type
am:t A
k:positive
H:exists x : positive * A, k = fst x /\ In x (elements am)

exists v : A, am ! k = Some v
A:Type
am:t A
k:positive
x:(positive * A)%type
H:k = fst x /\ In x (elements am)

exists v : A, am ! k = Some v
A:Type
am:t A
k:positive
x:(positive * A)%type
H:k = fst x
H0:In x (elements am)

exists v : A, am ! k = Some v
A:Type
am:t A
x:(positive * A)%type
H0:In x (elements am)

exists v : A, am ! (fst x) = Some v
A:Type
am:t A
x:(positive * A)%type
H0:In x (elements am)

am ! (fst x) = Some (snd x)
A:Type
am:t A
x:(positive * A)%type
H0:In x (elements am)

In (fst x, snd x) (elements am)
A:Type
am:t A
x:(positive * A)%type
H0:In x (elements am)
H:x = (fst x, snd x)

In (fst x, snd x) (elements am)
rewrite H in H0; assumption. Qed. Hint Resolve elements_iff : assocmap.
A:Type

forall (am : t A) (k : positive), ~ (exists v : A, am ! k = Some v) <-> ~ In k (List.map fst (elements am))
A:Type

forall (am : t A) (k : positive), ~ (exists v : A, am ! k = Some v) <-> ~ In k (List.map fst (elements am))
auto using not_iff_compat with assocmap. Qed. Hint Resolve elements_correct' : assocmap.
A:Type

forall (am : t A) (k : positive), am ! k = None -> ~ In k (List.map fst (elements am))
A:Type

forall (am : t A) (k : positive), am ! k = None -> ~ In k (List.map fst (elements am))
A:Type
am:t A
k:positive
H:am ! k = None

~ In k (List.map fst (elements am))
A:Type
am:t A
k:positive
H:am ! k = None

~ (exists v : A, am ! k = Some v)
A:Type
am:t A
k:positive
H:am ! k = None

(exists v : A, am ! k = Some v) -> False
A:Type
am:t A
k:positive
H:am ! k = None
H0:exists v : A, am ! k = Some v

False
A:Type
am:t A
k:positive
H:am ! k = None
x:A
H0:am ! k = Some x

False
A:Type
am:t A
k:positive
H:am ! k = None
x:A
H0:None = Some x

False
discriminate. Qed. Hint Resolve elements_correct_none : assocmap.
A:Type

forall (k : positive) (v : A) (am : PTree.t A) (bm : t A), am ! k = Some v -> (merge_fold am bm) ! k = Some v
A:Type

forall (k : positive) (v : A) (am : PTree.t A) (bm : t A), am ! k = Some v -> (merge_fold am bm) ! k = Some v
unfold merge_fold; auto with assocmap. Qed. Hint Resolve merge_fold_add : assocmap.
A:Type

forall (k : positive) (v : A) (am bm : t A), am ! k = None -> bm ! k = Some v -> (merge_fold am bm) ! k = Some v
A:Type

forall (k : positive) (v : A) (am bm : t A), am ! k = None -> bm ! k = Some v -> (merge_fold am bm) ! k = Some v
A:Type
k:positive
v:A
am, bm:t A
H:am ! k = None
H0:bm ! k = Some v

(merge_fold am bm) ! k = Some v
apply not_in_assoc; auto with assocmap. Qed. Hint Resolve merge_fold_not_in : assocmap.
A:Type

forall am : t A, merge_fold (empty A) am = am
A:Type

forall am : t A, merge_fold (empty A) am = am
auto. Qed. Hint Resolve merge_fold_base : assocmap. End Operations. End AssocMapExt. Import AssocMapExt. Definition assocmap := AssocMap.t value. Definition find_assocmap (n : nat) : reg -> assocmap -> value := get_default value (ZToValue 0). Definition empty_assocmap : assocmap := AssocMap.empty value. Definition merge_assocmap : assocmap -> assocmap -> assocmap := merge value. Ltac unfold_merge := unfold merge_assocmap; try (repeat (rewrite merge_add_assoc)); rewrite AssocMapExt.merge_base_1. Declare Scope assocmap. Notation "a ! b" := (AssocMap.get b a) (at level 1) : assocmap. Notation "a # ( b , c )" := (find_assocmap c b a) (at level 1) : assocmap. Notation "a # b" := (find_assocmap 32 b a) (at level 1) : assocmap. Notation "a ## b" := (List.map (fun c => find_assocmap 32 c a) b) (at level 1) : assocmap. Notation "a # b '<-' c" := (AssocMap.set b c a) (at level 1, b at next level) : assocmap. Local Open Scope assocmap.

forall (assoc : AssocMap.t value) (r : positive) (v : value), assoc ! r = Some v -> assoc # r = v

forall (assoc : AssocMap.t value) (r : positive) (v : value), assoc ! r = Some v -> assoc # r = v
assoc:AssocMap.t value
r:positive
v:value
H:assoc ! r = Some v

assoc # r = v
assoc:AssocMap.t value
r:positive
v:value
H:assoc ! r = Some v

match assoc ! r with | Some v => v | None => ZToValue 0 end = v
assoc:AssocMap.t value
r:positive
v:value
H:assoc ! r = Some v

v = v
trivial. Qed.