From e0a0d5c18c8874c72f0cf1fce551a9fed243c03e Mon Sep 17 00:00:00 2001
From: Son Ho
Date: Thu, 1 Jun 2023 23:19:27 +0200
Subject: Finish the proofs of the hashmap

---
 tests/hol4/hashmap/hashmap_PropertiesScript.sml | 1022 +++++++++++++----------
 tests/hol4/hashmap/hashmap_PropertiesTheory.sig |  902 ++++++++++++++++++++
 2 files changed, 1461 insertions(+), 463 deletions(-)
 create mode 100644 tests/hol4/hashmap/hashmap_PropertiesTheory.sig

(limited to 'tests/hol4/hashmap')

diff --git a/tests/hol4/hashmap/hashmap_PropertiesScript.sml b/tests/hol4/hashmap/hashmap_PropertiesScript.sml
index 4eceed97..68ed2d4e 100644
--- a/tests/hol4/hashmap/hashmap_PropertiesScript.sml
+++ b/tests/hol4/hashmap/hashmap_PropertiesScript.sml
@@ -12,8 +12,8 @@ Theorem EVERY_quant_equiv:
   ∀p ls. EVERY p ls ⇔ ∀i. 0 ≤ i ⇒ i < len ls ⇒ p (index i ls)
 Proof
   strip_tac >> Induct_on ‘ls’
-  >-(rw [EVERY_DEF, len_def] >> int_tac) >>
-  rw [EVERY_DEF, len_def, index_eq] >>
+  >-(rw [EVERY_DEF] >> int_tac) >>
+  rw [EVERY_DEF, index_eq] >>
   equiv_tac
   >-(
     rw [] >>
@@ -38,8 +38,8 @@ Theorem pairwise_rel_quant_equiv:
     (∀i j. 0 ≤ i ⇒ i < j ⇒ j < len ls ⇒ p (index i ls) (index j ls))
 Proof
   strip_tac >> Induct_on ‘ls’
-  >-(rw [pairwise_rel_def, len_def] >> int_tac) >>
-  rw [pairwise_rel_def, len_def] >>
+  >-(rw [pairwise_rel_def] >> int_tac) >>
+  rw [pairwise_rel_def] >>
   equiv_tac
   >-(
     (* ==> *)
@@ -74,6 +74,12 @@ Proof
   fs []
 QED
 
+(* TODO: the context tends to quickly saturate. In particular:
+   - sg_prove_premise_tac leaves the proven assumption in the context, while it shouldn't
+   - maybe massage shouldn't leave the introduced inequalities in the context: it is very noisy.
+     For instance, int_tac could introduce those inequalities.
+ *)
+
 Type key_t = “:usize”
 
 val distinct_keys_def = Define ‘
@@ -86,27 +92,33 @@ Definition list_t_v_def:
   (list_t_v (ListNil : 't list_t) : (key_t # 't) list = []) /\
   (list_t_v (ListCons k v tl) = (k, v) :: list_t_v tl)
 End
+val _ = export_rewrites ["list_t_v_def"]
 
 (* Invariants *)
+(* TODO: add to srw_ss *)
 Definition lookup_def:
   lookup key [] = NONE /\
   lookup key ((k, v) :: ls) =
     if k = key then SOME v else lookup key ls
 End
+val _ = export_rewrites ["lookup_def"]
 
 Definition slot_t_lookup_def:
   slot_t_lookup key ls = lookup key (list_t_v ls)
 End
+val _ = export_rewrites ["slot_t_lookup_def"]
 
 Definition remove_def:
   remove key [] = [] ∧
   remove key ((k, v) :: ls) =
     if k = key then ls else (k, v) :: remove key ls
 End
+val _ = export_rewrites ["remove_def"]
 
 Definition slot_t_remove_def:
   slot_t_remove key ls = remove key (list_t_v ls)
 End
+val _ = export_rewrites ["slot_t_remove_def"]
 
 Definition hash_mod_key_def:
   hash_mod_key k (l : int) : int =
@@ -114,11 +126,14 @@ Definition hash_mod_key_def:
     | Return k => usize_to_int k % l
     | _ => ARB
 End
+val _ = export_rewrites ["hashmap_Funs.hash_key_fwd_def", "hash_mod_key_def"]
+val _ = export_rewrites ["primitives.mem_replace_fwd_def", "primitives.mem_replace_back_def"]
 
 Definition slot_s_inv_hash_def:
   slot_s_inv_hash (l : int) (i : int) (ls : (key_t # 'b) list) : bool =
     ∀ k v. MEM (k, v) ls ⇒ hash_mod_key k l = i
 End
+val _ = export_rewrites ["slot_s_inv_hash_def"]
 
 Definition slot_s_inv_def:
   slot_s_inv (l : int) (i : int) (ls : (key_t # 'b) list) : bool = (
@@ -126,6 +141,7 @@ Definition slot_s_inv_def:
     slot_s_inv_hash l i ls
   )
 End
+val _ = export_rewrites ["slot_s_inv_def"]
 
 (* TODO: try with this invariant:
 
@@ -159,6 +175,7 @@ Definition slots_s_inv_def:
   slots_s_inv (s : 'a list_t list) =
     ∀ (i : int). 0 ≤ i ⇒ i < len s ⇒ slot_t_inv (len s) i (index i s)
 End
+val _ = export_rewrites ["slots_s_inv_def"]
 
 Definition slots_t_inv_def:
   slots_t_inv (s : 'a list_t vec) = slots_s_inv (vec_to_list s)
@@ -173,6 +190,7 @@ Definition hash_map_t_base_inv_def:
     slots_t_inv hm.hash_map_slots ∧
     (* The capacity must be > 0 (otherwise we can't resize, because when we
        resize we multiply the capacity by two) *)
+    (* TODO: write it as 0 < ... *)
     len (vec_to_list hm.hash_map_slots) > 0 ∧
     (* Load computation *)
     (let capacity = len (vec_to_list hm.hash_map_slots) in
@@ -270,7 +288,7 @@ Proof
     sg ‘v = usize_to_int n - 1’ >- int_tac >> fs [] >>
     (* *)
     progress >>
-    fs [vec_len_def, len_append, len_def] >>
+    fs [vec_len_def] >>
     int_tac
   ) >>
   fs [] >>
@@ -288,11 +306,11 @@ Theorem hash_map_allocate_slots_fwd_spec:
 Proof
   rw [] >>
   pure_once_rewrite_tac [hash_map_allocate_slots_fwd_def] >>
-  progress >> gvs [len_def, slots_t_inv_def, slots_s_inv_def, slot_s_inv_hash_def] >>
-  rw [slot_t_inv_def, slot_s_inv_def, slot_s_inv_hash_def]
-  >- fs [EVERY_quant_equiv, distinct_keys_def, pairwise_rel_def, list_t_v_def] >>
+  progress >> gvs [slots_t_inv_def] >>
+  rw [slot_t_inv_def]
+  >- fs [EVERY_quant_equiv, distinct_keys_def, pairwise_rel_def] >>
   fs [EVERY_quant_equiv] >>
-  qpat_assum ‘∀i. _’ sg_dep_rewrite_all_tac >> gvs [list_t_v_def]
+  qpat_assum ‘∀i. _’ sg_dep_rewrite_all_tac >> gvs []
 QED
 val _ = save_spec_thm "hash_map_allocate_slots_fwd_spec"
 
@@ -300,7 +318,7 @@ val _ = save_spec_thm "hash_map_allocate_slots_fwd_spec"
 Theorem FLAT_ListNil_is_nil:
   EVERY (λx. x = ListNil) ls ⇒ FLAT (MAP list_t_v ls) = []
 Proof
-  Induct_on ‘ls’ >> fs [list_t_v_def]
+  Induct_on ‘ls’ >> fs []
 QED
 
 Theorem hash_map_new_with_capacity_fwd_spec:
@@ -323,27 +341,27 @@ Proof
   progress >>
   gvs [hash_map_t_inv_def, hash_map_t_base_inv_def, hash_map_t_al_v_def, hash_map_t_v_def] >>
   rw []
-  >-(massage >> sg_dep_rewrite_goal_tac FLAT_ListNil_is_nil >> fs [len_def])
+  >-(massage >> sg_dep_rewrite_goal_tac FLAT_ListNil_is_nil >> fs [])
   >-(int_tac)
   >-(massage >> metis_tac [])
   >-(fs [len_s_def, hash_map_t_al_v_def, hash_map_t_v_def] >>
-     sg_dep_rewrite_goal_tac FLAT_ListNil_is_nil >> fs [len_def]) >>
-  fs [lookup_s_def, slots_t_lookup_def, slot_t_lookup_def] >>
+     sg_dep_rewrite_goal_tac FLAT_ListNil_is_nil >> fs []) >>
+  fs [lookup_s_def, slots_t_lookup_def] >>
   fs [EVERY_quant_equiv] >>
   (* TODO: sg_dep_rewrite_goal_tac does weird things here *)
   first_x_assum (qspec_assume ‘hash_mod_key k (usize_to_int capacity)’) >>
   first_x_assum sg_premise_tac
   >- (
-    fs [hash_mod_key_def, hash_key_fwd_def] >>
+    fs [] >>
     massage >>
     irule pos_mod_pos_is_pos >> fs []) >>
   first_x_assum sg_premise_tac
   >-(
-    fs [hash_mod_key_def, hash_key_fwd_def] >>
+    fs [] >>
     massage >>
     irule pos_mod_pos_lt >> fs []
   ) >>  
-  fs [list_t_v_def, lookup_def]
+  fs []
 QED
 val _ = save_spec_thm "hash_map_new_with_capacity_fwd_spec"
 
@@ -442,19 +460,19 @@ Proof
   rw [hash_map_clear_fwd_back_def] >>
   progress >>
   fs [len_s_def, hash_map_t_al_v_def, hash_map_t_v_def, lookup_s_def] >>
-  fs [slots_t_lookup_def, slot_t_lookup_def, len_def] >> rw []
+  fs [slots_t_lookup_def] >> rw []
   >-((* Prove that the invariant is preserved *)
-   fs [hash_map_t_inv_def, hash_map_t_base_inv_def, hash_map_t_al_v_def, hash_map_t_v_def, len_def] >>
+   fs [hash_map_t_inv_def, hash_map_t_base_inv_def, hash_map_t_al_v_def, hash_map_t_v_def] >>
    massage >> fs [] >>
    conj_tac
    >-(
-     fs [slots_t_inv_def, slots_s_inv_def] >>
-     rw [slot_t_inv_def, slot_s_inv_def, slot_s_inv_hash_def, list_t_v_def, distinct_keys_def, pairwise_rel_def]) >>
+     fs [slots_t_inv_def] >>
+     rw [slot_t_inv_def, distinct_keys_def, pairwise_rel_def]) >>
    Cases_on ‘hm.hash_map_max_load_factor’ >> gvs [] >>
    disj1_tac >>
    irule pos_div_pos_is_pos >>
    int_tac) >>
-  fs [hash_mod_key_def, hash_key_fwd_def] >>
+  fs [] >>
   (* TODO: would like to do: qpat_assum ‘∀j. _’ sg_dep_rewrite_goal_tac >> *)
   first_x_assum (qspec_assume ‘usize_to_int k % len (vec_to_list hm.hash_map_slots)’) >>
   fs [] >>
@@ -465,7 +483,7 @@ Proof
   >-(fs [hash_map_t_inv_def, hash_map_t_base_inv_def] >> int_tac) >>
   fs [] >>
   sg ‘~(len (vec_to_list hm.hash_map_slots) < 0)’ >- int_tac >>
-  fs [list_t_v_def, lookup_def]
+  fs []
 QED
 val _ = save_spec_thm "hash_map_clear_fwd_back_spec"
   
@@ -495,7 +513,7 @@ Theorem hash_map_insert_in_list_loop_fwd_spec:
     (b ⇔ slot_t_lookup key ls = NONE)
 Proof
   Induct_on ‘ls’ >> pure_once_rewrite_tac [hash_map_insert_in_list_loop_fwd_def] >>
-  fs [slot_t_lookup_def, lookup_def, list_t_v_def] >>
+  fs [] >>
   rw []
 QED
 val _ = save_spec_thm "hash_map_insert_in_list_loop_fwd_spec"
@@ -524,17 +542,13 @@ Theorem hash_map_insert_in_list_loop_back_spec_aux:
      | NONE => len (list_t_v ls1) = len (list_t_v ls) + 1
      | SOME _ => len (list_t_v ls1) = len (list_t_v ls))
 Proof
-  Induct_on ‘ls’ >> rw [list_t_v_def] >~ [‘ListNil’] >>
+  Induct_on ‘ls’ >> rw [] >~ [‘ListNil’] >>
   pure_once_rewrite_tac [hash_map_insert_in_list_loop_back_def]
-  >- (rw [slot_t_lookup_def, lookup_def, list_t_v_def, len_def, slot_s_inv_hash_def]) >>
-  fs [] >>
-  case_tac >> fs []
-  >-(fs [slot_t_lookup_def, lookup_def, list_t_v_def, len_def, slot_s_inv_hash_def] >>
-     metis_tac []) >>
-  progress >>
-  fs [slot_t_lookup_def, lookup_def, list_t_v_def, len_def] >>
-  rw []
-  >-(fs [slot_s_inv_hash_def] >> metis_tac []) >>
+  >- (rw []) >>
+  fs [] >- metis_tac [] >>
+  progress >> fs [] >> rw []
+  >- (metis_tac [])
+  >- (metis_tac []) >>
   case_tac >> fs [] >> int_tac
 QED
 
@@ -548,13 +562,13 @@ Theorem hash_map_insert_in_list_loop_back_EVERY_distinct_keys:
   EVERY (λy. k1 ≠ FST y) (list_t_v ls1)
 Proof
   Induct_on ‘ls0’ >> rw [pairwise_rel_def] >~ [‘ListNil’] >>
-  gvs [list_t_v_def, pairwise_rel_def, EVERY_DEF]
-  >-(gvs [MK_BOUNDED hash_map_insert_in_list_loop_back_def 1, bind_def, list_t_v_def, EVERY_DEF]) >>
+  gvs [pairwise_rel_def, EVERY_DEF]
+  >-(gvs [MK_BOUNDED hash_map_insert_in_list_loop_back_def 1, bind_def, EVERY_DEF]) >>
   pat_undisch_tac ‘hash_map_insert_in_list_loop_back _ _ _ = _’ >>
   simp [MK_BOUNDED hash_map_insert_in_list_loop_back_def 1, bind_def] >>
-  Cases_on ‘u = k’ >> rw [] >> gvs [list_t_v_def, pairwise_rel_def, EVERY_DEF] >>
+  Cases_on ‘u = k’ >> rw [] >> gvs [pairwise_rel_def, EVERY_DEF] >>
   Cases_on ‘hash_map_insert_in_list_loop_back k v ls0’ >>
-  gvs [distinct_keys_def, list_t_v_def, pairwise_rel_def, EVERY_DEF] >>
+  gvs [distinct_keys_def, pairwise_rel_def, EVERY_DEF] >>
   metis_tac []
 QED
 
@@ -566,14 +580,14 @@ Theorem hash_map_insert_in_list_loop_back_distinct_keys:
 Proof
   Induct_on ‘ls0’ >> rw [distinct_keys_def] >~ [‘ListNil’]
   >-(
-    fs [list_t_v_def, hash_map_insert_in_list_loop_back_def] >>
-    gvs [list_t_v_def, pairwise_rel_def, EVERY_DEF]) >>
+    fs [hash_map_insert_in_list_loop_back_def] >>
+    gvs [pairwise_rel_def, EVERY_DEF]) >>
   last_x_assum (qspecl_assume [‘k’, ‘v’]) >>
   pat_undisch_tac ‘hash_map_insert_in_list_loop_back _ _ _ = _’ >>
   simp [MK_BOUNDED hash_map_insert_in_list_loop_back_def 1, bind_def] >>
-  Cases_on ‘u = k’ >> rw [] >> gvs [list_t_v_def, pairwise_rel_def, EVERY_DEF] >>
+  Cases_on ‘u = k’ >> rw [] >> gvs [pairwise_rel_def, EVERY_DEF] >>
   Cases_on ‘hash_map_insert_in_list_loop_back k v ls0’ >>
-  gvs [distinct_keys_def, list_t_v_def, pairwise_rel_def, EVERY_DEF] >>
+  gvs [distinct_keys_def, pairwise_rel_def, EVERY_DEF] >>
   metis_tac [hash_map_insert_in_list_loop_back_EVERY_distinct_keys]
 QED
 
@@ -599,11 +613,11 @@ Theorem hash_map_insert_in_list_loop_back_spec:
     (∀l. slot_s_inv_hash l (hash_mod_key key l) (list_t_v ls) ⇒
      insert_in_slot_t_rel l key value ls ls1)
 Proof
-  rw [slot_t_inv_def, slot_s_inv_def] >>
+  rw [slot_t_inv_def] >>
   qspecl_assume [‘ls’, ‘key’, ‘value’] hash_map_insert_in_list_loop_back_spec_aux >>
   fs [] >>
   qspecl_assume [‘key’, ‘value’, ‘ls’, ‘ls1’] hash_map_insert_in_list_loop_back_distinct_keys >>
-  gvs [insert_in_slot_t_rel_def, slot_t_inv_def, slot_s_inv_def]
+  gvs [insert_in_slot_t_rel_def, slot_t_inv_def] >> metis_tac []
 QED
 val _ = save_spec_thm "hash_map_insert_in_list_loop_back_spec"
 
@@ -655,6 +669,7 @@ Proof
   fs [hashmap_TypesTheory.recordtype_hash_map_t_seldef_hash_map_slots_fupd_def] >>
   fs [hashmap_TypesTheory.recordtype_hash_map_t_seldef_hash_map_num_entries_fupd_def]
 QED
+val hash_map_insert_no_resize_fwd_back_branches_eq = SIMP_RULE (srw_ss ()) [] hash_map_insert_no_resize_fwd_back_branches_eq
 
 Theorem hash_map_cond_incr_thm:
   ∀ hm key a.
@@ -713,14 +728,15 @@ Proof
   fs [hash_map_insert_no_resize_fwd_back_branches_eq] >>
   qspecl_assume [‘hm’, ‘key’, ‘a’] hash_map_cond_incr_thm >> gvs [] >>
   pop_assum sg_premise_tac
-  >- (fs [lookup_s_def, slots_t_lookup_def, slot_t_lookup_def, hash_mod_key_def, hash_key_fwd_def, vec_index_def, int_rem_def]) >>
+  >- (fs [lookup_s_def, slots_t_lookup_def, vec_index_def, int_rem_def]) >>
   fs [] >>
   (* TODO: lemma? *)
   sg ‘let l = len (vec_to_list hm.hash_map_slots) in
       slot_t_inv l (usize_to_int key % l) (index (usize_to_int key % l) (vec_to_list hm.hash_map_slots))’
-  >-(fs [hash_map_t_base_inv_def, slots_t_inv_def, slots_s_inv_def] >>
+  >-(fs [hash_map_t_base_inv_def, slots_t_inv_def] >>
      last_x_assum (qspec_assume ‘usize_to_int a’) >>
-     gvs [vec_index_def, int_rem_def, slot_t_inv_def, slot_s_inv_def]) >>
+     gvs [vec_index_def, int_rem_def, slot_t_inv_def] >>
+     metis_tac []) >>
   fs [] >>
   sg ‘usize_to_int a = usize_to_int key % len (vec_to_list hm.hash_map_slots)’
   >-(fs [int_rem_def]) >>
@@ -728,7 +744,7 @@ Proof
   >-(fs [int_rem_def]) >>
   fs [] >>
   sg ‘distinct_keys (list_t_v (vec_index hm.hash_map_slots a))’
-  >-(fs [slot_t_inv_def, slot_s_inv_def, vec_index_def]) >>
+  >-(fs [slot_t_inv_def, vec_index_def]) >>
   fs [hash_map_insert_in_list_back_def] >>
   progress >>
   progress >- ((* TODO: *) massage >> fs []) >>
@@ -737,12 +753,9 @@ Proof
   (* Prove the post-condition *)
   qexists ‘a'’ >>
   rw [hash_map_same_params_def]
-  >-(gvs [insert_in_slot_t_rel_def, hash_mod_key_def, hash_key_fwd_def, vec_index_def, vec_update_def, slot_t_inv_def, slot_s_inv_def] >>
-     metis_tac [])
-  >-(
-    fs [hash_mod_key_def, hash_key_fwd_def, vec_index_def, vec_update_def] >>
-    sg_dep_rewrite_goal_tac mk_vec_axiom >> gvs []) >>
-  gvs [lookup_s_def, slots_t_lookup_def, slot_t_lookup_def, hash_mod_key_def, hash_key_fwd_def, vec_index_def] >>
+  >-(gvs [insert_in_slot_t_rel_def, vec_index_def, vec_update_def, slot_t_inv_def] >>
+     metis_tac []) >>
+  gvs [lookup_s_def, slots_t_lookup_def, vec_index_def] >>
   case_tac >> fs []
 QED
 
@@ -755,12 +768,12 @@ Theorem len_FLAT_MAP_update:
 Proof
   strip_tac >>
   Induct_on ‘ls’
-  >-(rw [len_def] >> int_tac) >>
+  >-(rw [] >> int_tac) >>
   rw [] >>
-  fs [len_def, index_def, update_def] >>
-  Cases_on ‘i = 0’ >> fs [len_append]
+  fs [index_def, update_def] >>
+  Cases_on ‘i = 0’ >> fs []
   >- int_tac >>
-  sg ‘0 < i’ >- int_tac >> fs [len_append] >>
+  sg ‘0 < i’ >- int_tac >> fs [] >>
   first_x_assum (qspec_assume ‘i - 1’) >>
   fs [] >>
   (* TODO: automate *)
@@ -804,39 +817,39 @@ Proof
     rw []
     >-(
       sg_dep_rewrite_goal_tac len_FLAT_MAP_update
-      >-(fs [hash_mod_key_def, hash_key_fwd_def]) >>
+      >-(fs []) >>
       fs [insert_in_slot_t_rel_def] >>
-      fs [hash_mod_key_def, hash_key_fwd_def] >>
+      fs [] >>
       Cases_on ‘lookup_s hm key’ >>
-      fs [lookup_s_def, slots_t_lookup_def, slot_t_lookup_def, hash_mod_key_def, hash_key_fwd_def] >>
+      fs [lookup_s_def, slots_t_lookup_def] >>
       int_tac) >>
-    fs [slots_t_inv_def, slots_s_inv_def] >>
+    fs [slots_t_inv_def] >>
     rw [] >>
     (* Proof of the slot property: has the slot been updated∃ *)
     Cases_on ‘i = hash_mod_key key (len (vec_to_list hm.hash_map_slots))’ >> fs []
     >-(
       sg_dep_rewrite_goal_tac index_update_diff
-      >-(fs [hash_mod_key_def, hash_key_fwd_def]) >>
+      >-(fs []) >>
       fs [insert_in_slot_t_rel_def]) >>
     sg_dep_rewrite_goal_tac index_update_same
-    >-(fs [hash_mod_key_def, hash_key_fwd_def]) >>
+    >-(fs []) >>
     fs []) >>
   (* Prove the rest of the postcondition *)
   rw []
   >-((* The binding for key is updated *)
      fs [lookup_s_def, slots_t_lookup_def] >>
      sg_dep_rewrite_goal_tac index_update_diff
-     >-(fs [hash_mod_key_def, hash_key_fwd_def]) >>
+     >-(fs []) >>
      fs [insert_in_slot_t_rel_def])
   >-((* The other bindings are unchanged *)
      fs [lookup_s_def, slots_t_lookup_def] >>
      Cases_on ‘hash_mod_key k (len (vec_to_list hm.hash_map_slots)) = hash_mod_key key (len (vec_to_list hm.hash_map_slots))’ >> gvs []
      >-(
         sg_dep_rewrite_goal_tac index_update_diff
-        >-(fs [hash_mod_key_def, hash_key_fwd_def]) >>
+        >-(fs []) >>
         fs [insert_in_slot_t_rel_def]) >>
      sg_dep_rewrite_goal_tac index_update_same
-     >-(fs [hash_mod_key_def, hash_key_fwd_def] >> irule pos_mod_pos_lt >> massage >> fs []) >>
+     >-(fs [] >> irule pos_mod_pos_lt >> massage >> fs []) >>
      fs [insert_in_slot_t_rel_def]) >>
   (* Length *)
   Cases_on ‘lookup_s hm key’ >>
@@ -862,8 +875,8 @@ Theorem distinct_keys_lookup_NONE:
   lookup k ls = NONE
 Proof
   Induct_on ‘ls’ >> rw [] >>
-  fs [distinct_keys_def, pairwise_rel_def, EVERY_DEF, lookup_def] >>
-  Cases_on ‘h’ >> fs [lookup_def]
+  fs [distinct_keys_def, pairwise_rel_def, EVERY_DEF] >>
+  Cases_on ‘h’ >> fs []
 QED
 
 Theorem hash_map_move_elements_from_list_fwd_back_spec:
@@ -886,14 +899,11 @@ Proof
   (* TODO: improve massage to not only look at variables *)
   qspec_assume ‘hm.hash_map_num_entries’ usize_to_int_bounds >> fs [] >>
   (* TODO: automate *)
-  qspec_assume ‘list_t_v ls’ len_pos
-  >-(fs [slot_t_lookup_def, lookup_def, list_t_v_def])
-  >-(fs [len_def, list_t_v_def]) >>
+  qspec_assume ‘list_t_v ls’ len_pos >>
   (* Recursive case *)
+  progress >>
   progress
-  >-(fs [len_s_def, hash_map_t_base_inv_def, list_t_v_def, len_def] >> int_tac) >>
-  progress
-  >-(Cases_on ‘lookup_s hm u’ >> fs [len_s_def, hash_map_t_base_inv_def, list_t_v_def, len_def] >> int_tac) >>
+  >-(Cases_on ‘lookup_s hm u’ >> fs [len_s_def, hash_map_t_base_inv_def] >> int_tac) >>
   (* Prove the postcondition *)
   (* Drop the induction hypothesis *)
   last_x_assum ignore_tac >>
@@ -905,7 +915,7 @@ Proof
   fs [hash_map_same_params_def] >>
   (* *)
   sg ‘distinct_keys (list_t_v ls)’
-  >-(fs [distinct_keys_def, list_t_v_def, pairwise_rel_def, EVERY_DEF]) >>
+  >-(fs [distinct_keys_def, pairwise_rel_def, EVERY_DEF]) >>
   fs [] >>
   (* For some reason, if we introduce an assumption with [sg], the rewriting
      doesn't work (and I couldn't find any typing issue) *)
@@ -913,53 +923,33 @@ Proof
   first_assum sg_premise_tac
   >-(
     rw [] >>
-    sg ‘k ≠ u’ >-(irule distinct_keys_MEM_not_eq >> metis_tac [list_t_v_def]) >>
+    sg ‘k ≠ u’ >-(irule distinct_keys_MEM_not_eq >> metis_tac []) >>
     last_x_assum (qspec_assume ‘k’) >>
     gvs [] >>
     first_x_assum (qspecl_assume [‘k’, ‘v’]) >>
-    gvs [list_t_v_def]) >>
+    gvs []) >>
   gvs[] >>
   (* *)
   rw []
   >-(
-    sg ‘k ≠ u’ >-(fs [slot_t_lookup_def, lookup_def, list_t_v_def] >> Cases_on ‘u = k’ >> fs []) >>
-    last_x_assum (qspec_assume ‘k’) >> gvs [] >>
     first_x_assum (qspec_assume ‘k’) >>
     first_x_assum (qspec_assume ‘k’) >>
-    gvs [slot_t_lookup_def, list_t_v_def, lookup_def])
-  >-(
-    first_x_assum (qspec_assume ‘k’) >>
-    first_x_assum (qspec_assume ‘k’) >>
-    fs [slot_t_lookup_def, list_t_v_def, lookup_def] >>
-    Cases_on ‘u = k’ >> gvs [] >>  
+    fs [] >>
     sg ‘lookup k (list_t_v ls) = NONE’ >-(irule distinct_keys_lookup_NONE >> metis_tac []) >>
-    fs []) >>
+    gvs []) >>
   (* The length *)
-  fs [len_def, list_t_v_def] >>
+  fs [] >>
   int_tac
 QED
 val _ = save_spec_thm "hash_map_move_elements_from_list_fwd_back_spec"
 
-(* TODO: move *)
-Theorem drop_more_than_length:
-  ∀ ls i.
-  len ls ≤ i ⇒
-  drop i ls = []
-Proof
-  Induct_on ‘ls’ >>
-  rw [len_def, drop_def] >>
-  qspec_assume ‘ls’ len_pos >> try_tac int_tac >>
-  last_x_assum irule >>
-  int_tac
-QED
-
 (* TODO: induction theorem for vectors *)
 Theorem len_index_FLAT_MAP_list_t_v:
   ∀ slots i.
     0 ≤ i ⇒ i < len slots ⇒
     len (list_t_v (index i slots)) ≤ len (FLAT (MAP list_t_v (drop i slots)))
 Proof
-  Induct_on ‘slots’ >> rw [vec_index_def, drop_def, index_def, len_def, len_append, len_pos, update_def, drop_eq] >> try_tac int_tac >> fs [] >>
+  Induct_on ‘slots’ >> rw [vec_index_def, drop_def, index_def, len_pos, update_def, drop_eq] >> try_tac int_tac >> fs [] >>
   last_x_assum (qspec_assume ‘i - 1’) >>
   sg ‘0 ≤ i − 1 ∧ i − 1 < len slots’ >- int_tac >> fs []
 QED
@@ -971,24 +961,11 @@ Theorem len_vec_FLAT_drop_update:
   len (list_t_v (index i slots)) +
   len (FLAT (MAP list_t_v (drop (i + 1) (update slots i ListNil))))
 Proof
-  Induct_on ‘slots’ >> fs [len_def, drop_def, update_def, len_append, len_pos, index_def] >> rw [] >> try_tac int_tac >> fs [drop_eq, len_append] >>
+  Induct_on ‘slots’ >> fs [drop_def, update_def, len_pos, index_def] >> rw [] >> try_tac int_tac >> fs [drop_eq, len_append] >>
   last_x_assum (qspec_assume ‘i - 1’) >>
   sg ‘0 ≤ i − 1 ∧ i − 1 < len slots ∧ ~(i + 1 < 0) ∧ ~(i + 1 = 0)’ >- int_tac >> fs [] >> sg ‘i + 1 - 1 = i’ >- int_tac >> fs [drop_def]
 QED
 
-(* TODO: move *)
-(* TODO: add to srw_ss () ? *)
-Theorem vec_to_list_vec_update:
-  ∀ v i x. vec_to_list (vec_update v i x) = update (vec_to_list v) (usize_to_int i) x
-Proof
-  rw [vec_update_def] >>
-  qspec_assume ‘v’ vec_len_spec >>
-  qspecl_assume [‘v’, ‘i’, ‘x’] vec_update_eq >> fs [] >>
-  qspecl_assume [‘vec_to_list v’, ‘usize_to_int i’, ‘x’] update_len >>
-  sg_dep_rewrite_all_tac mk_vec_axiom >- fs [] >>
-  fs []  
-QED
-
 Theorem MEM_EVERY_not:
   ∀ k v ls.
     MEM (k, v) ls ⇒
@@ -1006,9 +983,9 @@ Theorem MEM_distinct_keys_lookup:
     distinct_keys ls ⇒
     lookup k ls = SOME v
 Proof
-  Induct_on ‘ls’ >> fs [lookup_def, distinct_keys_def, pairwise_rel_def] >>
-  rw [lookup_def] >> fs [lookup_def] >>
-  Cases_on ‘h’ >> fs [lookup_def] >> rw [] >>
+  Induct_on ‘ls’ >> fs [distinct_keys_def, pairwise_rel_def] >>
+  rw [] >> fs [] >>
+  Cases_on ‘h’ >> fs [] >> rw [] >>
   (* Absurd *)
   exfalso >>
   metis_tac [MEM_EVERY_not]
@@ -1020,9 +997,9 @@ Theorem lookup_distinct_keys_MEM:
     distinct_keys ls ⇒
     MEM (k, v) ls
 Proof
-  Induct_on ‘ls’ >> fs [lookup_def, distinct_keys_def, pairwise_rel_def] >>
-  rw [lookup_def] >> fs [lookup_def] >>
-  Cases_on ‘h’ >> fs [lookup_def] >> rw [] >>
+  Induct_on ‘ls’ >> fs [distinct_keys_def, pairwise_rel_def] >>
+  rw [] >> fs [] >>
+  Cases_on ‘h’ >> fs [] >> rw [] >>
   Cases_on ‘q = k’ >> fs []
 QED
 
@@ -1036,23 +1013,23 @@ Theorem key_MEM_j_lookup_i_is_NONE:
   slot_t_lookup k (index (usize_to_int i) (vec_to_list slots)) = NONE
 Proof
   rw [] >>
-  fs [slot_t_inv_def, slot_s_inv_def, slot_s_inv_hash_def] >>
+  fs [slot_t_inv_def] >>
   (* *)
   first_assum (qspec_assume ‘j’) >> fs [] >>
   pop_assum sg_premise_tac >- int_tac >> fs [] >>
   first_x_assum imp_res_tac >>
-  fs [hash_mod_key_def, hash_key_fwd_def] >>
+  fs [] >>
   (* Prove by contradiction *)
   first_assum (qspec_assume ‘usize_to_int i’) >> fs [] >>
   pop_assum sg_premise_tac >- int_tac >> fs [] >>
   Cases_on ‘slot_t_lookup k (index (usize_to_int i) (vec_to_list slots))’ >> fs [] >>
   sg ‘MEM (k,v) (list_t_v (index (usize_to_int i) (vec_to_list slots)))’
   >- (
-    fs [slot_t_lookup_def] >>
+    fs [] >>
     metis_tac [lookup_distinct_keys_MEM]
   ) >>
   qpat_x_assum ‘∀k. _’ imp_res_tac >>
-  fs [hash_mod_key_def, hash_key_fwd_def]
+  fs []
 QED  
 
 (* TODO: the names introduced by progress are random, which is confusing.
@@ -1095,31 +1072,31 @@ Proof
   massage
   >-(
     case_tac >- int_tac >> fs [] >>
-    sg_dep_rewrite_goal_tac drop_more_than_length >-(int_tac) >> fs [len_def] >>
+    sg_dep_rewrite_goal_tac drop_more_than_length >-(int_tac) >> fs [] >>
     strip_tac >> Cases_on ‘lookup_s hm k’ >> fs [] >>
-    fs [hash_mod_key_def, hash_key_fwd_def] >>
+    fs [] >>
     (* Contradiction *)
     rw [] >> int_tac
     )
   >-(
     (* Same as above - TODO: this is a bit annoying, update the invariant principle (maybe base case is ≤ 0 ?) *)
-    sg_dep_rewrite_goal_tac drop_more_than_length >-(int_tac) >> fs [len_def] >>
+    sg_dep_rewrite_goal_tac drop_more_than_length >-(int_tac) >> fs [] >>
     strip_tac >> Cases_on ‘lookup_s hm k’ >> fs [] >>
-    fs [hash_mod_key_def, hash_key_fwd_def] >>
+    fs [] >>
     (* Contradiction *)
     rw [] >> int_tac) >>
   (* Recursive case *)
   case_tac >> fs [] >>
   (* Eliminate the trivial case *)
   try_tac (
-    sg_dep_rewrite_goal_tac drop_more_than_length >-(int_tac) >> fs [len_def] >>
+    sg_dep_rewrite_goal_tac drop_more_than_length >-(int_tac) >> fs [] >>
     strip_tac >> Cases_on ‘lookup_s hm k’ >> fs [] >>
-    fs [hash_mod_key_def, hash_key_fwd_def] >>
+    fs [] >>
     (* Contradiction *)
     rw [] >> int_tac) >>
   progress >- (fs [vec_len_def] >> massage >> fs []) >>
   progress >- (
-    fs [mem_replace_fwd_def, vec_index_def] >>
+    fs [vec_index_def] >>
     qspecl_assume [‘vec_to_list slots’, ‘usize_to_int i’] len_index_FLAT_MAP_list_t_v >>
     gvs [] >> int_tac) >>
   (* We just evaluated the call to “hash_map_move_elements_from_list_fwd_back”: prove the assumptions
@@ -1127,27 +1104,26 @@ Proof
   qpat_x_assum ‘_ ⇒ _’ sg_premise_tac
   >-(
     first_x_assum (qspec_assume ‘usize_to_int i’) >> gvs [vec_index_def] >>
-    rw [mem_replace_fwd_def]
+    rw []
     >-(first_x_assum irule >> metis_tac []) >>
-    fs [slot_t_inv_def, slot_s_inv_def]) >>
-  gvs [mem_replace_fwd_def] >>
+    fs [slot_t_inv_def]) >>
+  gvs [] >>
   (* Continue going forward *)
   progress >>
   progress >- (fs [vec_len_def] >> massage >> fs []) >>
-  progress >> fs [mem_replace_back_def, mem_replace_fwd_def] >> qspecl_assume [‘slots’, ‘i’, ‘ListNil’] vec_update_eq >>
+  progress >> fs [] >> qspecl_assume [‘slots’, ‘i’, ‘ListNil’] vec_update_eq >>
   gvs [] >>
   (* Drop the induction hypothesis *)
   last_x_assum ignore_tac
   (* TODO: when we update the theorem, progress lookups the stored (deprecated) rather than
      the inductive hypothesis *)
   (* The preconditions of the recursive call *)
-  >- (int_tac)
   >- (
     qspecl_assume [‘vec_to_list slots’, ‘usize_to_int i’] len_vec_FLAT_drop_update >> gvs [] >>
-    gvs [vec_to_list_vec_update, vec_index_def] >>
+    gvs [vec_index_def] >>
     int_tac)
   >-(
-    fs [vec_to_list_vec_update] >>
+    fs [] >>
     sg_dep_rewrite_goal_tac index_update_same
     >-(fs [] >> int_tac) >>
     fs [] >>
@@ -1158,17 +1134,18 @@ Proof
     (* Prove that index j (update slots i) = index j slots *)
     pop_assum (qspec_assume ‘int_to_usize j’) >> gvs [] >> massage >> gvs [] >>
     fs [vec_len_def] >>
-    fs [vec_to_list_vec_update] >>
     massage >> gvs [] >>
     sg ‘j ≠ usize_to_int i’ >- int_tac >> gvs [vec_index_def, vec_update_def] >>
     massage >>
-    sg_dep_rewrite_all_tac mk_vec_axiom >- fs [] >> gvs [] >>
     (* Use the fact that slot_t_lookup k (index i ... slots) = NONE *)
     last_x_assum (qspec_assume ‘k’) >>
     first_assum sg_premise_tac
     >- (
-      sg ‘usize_to_int i < j’ >- int_tac >>
-      metis_tac [key_MEM_j_lookup_i_is_NONE]) >>
+      sg ‘usize_to_int i < j’ >- int_tac >> fs [] >>
+      (* TODO: we have to rewrite key_MEM_j_lookup_i_is_NONE before applying
+         metis_tac *)
+      assume_tac key_MEM_j_lookup_i_is_NONE >> fs [] >>
+      metis_tac []) >>
     gvs [] >>
     (* Use the fact that as the key is in the slots after i, it can't be in “hm” (yet) *)
     last_x_assum (qspec_assume ‘j’) >> gvs [] >>
@@ -1180,12 +1157,11 @@ Proof
     (* Reasoning about the length *)
     qspecl_assume [‘vec_to_list slots’, ‘usize_to_int i’] len_vec_FLAT_drop_update >>
     gvs [] >>
-    fs [vec_to_list_vec_update] >>
     fs [GSYM integerTheory.INT_ADD_ASSOC, vec_index_def]) >>
   (* Same params *)
   fs [hash_map_same_params_def] >>
   (* Lookup properties *)
-  strip_tac >> fs [hash_mod_key_def, hash_key_fwd_def] >>
+  strip_tac >> fs [] >>
   sg ‘usize_to_int k % len (vec_to_list slots) < len (vec_to_list slots)’
   >- (irule pos_mod_pos_lt >> massage >> fs [] >> int_tac) >> fs [] >>
   Cases_on ‘usize_to_int i = usize_to_int k % len (vec_to_list slots)’ >> fs []
@@ -1194,14 +1170,14 @@ Proof
     sg ‘~ (usize_to_int k % len (vec_to_list slots) + 1 ≤ usize_to_int k % len (vec_to_list slots))’ >- int_tac >> fs [] >>
     (* Is the key is slot i ? *)
     (* TODO: use key_MEM_j_lookup_i_is_NONE? *)
-    Cases_on ‘slot_t_lookup k (vec_index slots i)’ >> gvs [slot_t_lookup_def, vec_index_def] >>
+    Cases_on ‘slot_t_lookup k (vec_index slots i)’ >> gvs [vec_index_def] >>
     (* The key can't be in “hm” *)
     last_x_assum (qspec_assume ‘usize_to_int i’) >>
     pop_assum sg_premise_tac >> fs [] >>
     pop_assum sg_premise_tac >> fs [] >>
     pop_assum (qspecl_assume [‘k’, ‘x’]) >>
     pop_assum sg_premise_tac
-    >-(irule lookup_distinct_keys_MEM >> gvs [slot_t_inv_def, slot_s_inv_def]) >> fs []) >>
+    >-(irule lookup_distinct_keys_MEM >> gvs [slot_t_inv_def]) >> fs []) >>
   (* Here: usize_to_int i ≠ usize_to_int k % len (vec_to_list slots) *)
   Cases_on ‘usize_to_int i ≤ usize_to_int k % len (vec_to_list slots)’ >> fs []
   >- (
@@ -1217,15 +1193,15 @@ Proof
       first_x_assum irule  >>
       last_x_assum (qspec_assume ‘usize_to_int i’) >> gvs [] >>
       (* Prove by contradiction - TODO: turn this into a lemma? *)
-      gvs [slot_t_inv_def, slot_s_inv_def, slot_s_inv_hash_def, hash_mod_key_def, hash_key_fwd_def] >>
+      gvs [slot_t_inv_def] >>
       Cases_on ‘slot_t_lookup k (vec_index slots i)’ >> fs [vec_index_def] >> exfalso >>
-      fs [slot_t_lookup_def] >>
+      fs [] >>
       imp_res_tac lookup_distinct_keys_MEM >>
       sg ‘usize_to_int k % len (vec_to_list slots) = usize_to_int i’ >- metis_tac [] >> fs []
       ) >>
     fs [] >>
     case_tac >>
-    fs [vec_to_list_vec_update] >>
+    fs [] >>
     sg_dep_rewrite_goal_tac index_update_same >> fs []
   ) >>
   (* Here: usize_to_int i > usize_to_int k % ... *)
@@ -1237,10 +1213,10 @@ Proof
     last_x_assum (qspec_assume ‘usize_to_int i’) >>
     pop_assum sg_premise_tac >> fs [] >>
     pop_assum sg_premise_tac >> fs [] >>
-    gvs [slot_t_inv_def, slot_s_inv_def, slot_s_inv_hash_def, hash_mod_key_def, hash_key_fwd_def] >>
+    gvs [slot_t_inv_def] >>
     (* Prove by contradiction *)
     Cases_on ‘slot_t_lookup k (vec_index slots i)’ >> fs [vec_index_def] >> exfalso >>
-    fs [slot_t_lookup_def] >>
+    fs [] >>
     imp_res_tac lookup_distinct_keys_MEM >>
     sg ‘usize_to_int k % len (vec_to_list slots) = usize_to_int i’ >- metis_tac [] >> fs []
   ) >>
@@ -1402,7 +1378,7 @@ Proof
      (* TODO: automate *)
      qspec_assume ‘hm.hash_map_num_entries’ usize_to_int_bounds >> fs [] >>
      int_tac)
-  >-(fs [hash_map_t_base_inv_def, slots_t_inv_def, slots_s_inv_def]) >>
+  >-(fs [hash_map_t_base_inv_def, slots_t_inv_def]) >>
   pure_rewrite_tac [hash_map_t_inv_def] >>
   fs [len_s_def, hash_map_t_base_inv_def, hash_map_t_al_v_def, hash_map_t_v_def, drop_eq] >>
   gvs [] >>
@@ -1431,7 +1407,7 @@ Proof
     int_tac) >>
   rw [] >>
   first_x_assum (qspec_assume ‘k’) >>
-  gvs [hash_mod_key_def, hash_key_fwd_def, slots_t_lookup_def, slot_t_lookup_def, lookup_s_def] >>
+  gvs [slots_t_lookup_def, lookup_s_def] >>
   massage >>
   sg ‘0 ≤ usize_to_int k % len (vec_to_list hm.hash_map_slots)’
   >- (irule pos_mod_pos_is_pos >> fs [] >> int_tac) >> fs [] >>
@@ -1498,359 +1474,479 @@ Proof
 QED
 val _ = save_spec_thm "hash_map_insert_fwd_back_spec"
 
-(*
-Theorem nth_mut_fwd_spec:
-  !(ls : 't list_t) (i : u32).
-    u32_to_int i < len (list_t_v ls) ==>
-    case nth_mut_fwd ls i of
-    | Return x => x = index (u32_to_int i) (list_t_v ls)
-    | Fail _ => F
-    | Diverge => F
+Theorem hash_map_contains_key_in_list_fwd_spec:
+  ∀ key ls.
+  hash_map_contains_key_in_list_fwd key ls = Return (slot_t_lookup key ls ≠ NONE)
 Proof
-  Induct_on ‘ls’ >> rw [list_t_v_def, len_def] >~ [‘ListNil’]
-  >-(massage >> int_tac) >>
-  pure_once_rewrite_tac [nth_mut_fwd_def] >> rw [] >>
-  fs [index_eq] >>
-  progress >> progress
+  fs [hash_map_contains_key_in_list_fwd_def] >>
+  Induct_on ‘ls’ >>
+  pure_once_rewrite_tac [hash_map_contains_key_in_list_loop_fwd_def] >>
+  fs [] >>
+  (* There remains only the recursive case *)
+  rw []
 QED
+val _ = save_spec_thm "hash_map_contains_key_in_list_fwd_spec"
 
-val _ = save_spec_thm "nth_mut_fwd_spec"
-
-val _ = new_constant ("insert", “: u32 -> 't -> (u32 # 't) list_t -> (u32 # 't) list_t result”)
-val insert_def = new_axiom ("insert_def", “
- insert (key : u32) (value : 't) (ls : (u32 # 't) list_t) : (u32 # 't) list_t result =
-  case ls of
-  | ListCons (ckey, cvalue) tl =>
-    if ckey = key
-    then Return (ListCons (ckey, value) tl)
-    else
-      do
-      tl0 <- insert key value tl;
-      Return (ListCons (ckey, cvalue) tl0)
-      od
-  | ListNil => Return (ListCons (key, value) ListNil)
- ”)
-
-(* Property that keys are pairwise distinct *)
-val distinct_keys_def = Define ‘
-  distinct_keys (ls : (u32 # 't) list) =
-    !i j.
-      0 ≤ i ⇒ i < j ⇒ j < len ls ⇒
-      FST (index i ls) ≠ FST (index j ls)
-’
-
-(* Lemma about ‘insert’, without the invariant *)
-Theorem insert_lem_aux:
-  !ls key value.
-    (* The keys are pairwise distinct *)
-    case insert key value ls of
-    | Return ls1 =>
-      (* We updated the binding *)
-      lookup key ls1 = SOME value /\
-      (* The other bindings are left unchanged *)
-      (!k. k <> key ==> lookup k ls = lookup k ls1)
-    | Fail _ => F
-    | Diverge => F
+Theorem hash_map_contains_key_fwd_spec:
+  ∀ hm key.
+  hash_map_t_inv hm ⇒
+  hash_map_contains_key_fwd hm key = Return (lookup_s hm key ≠ NONE)
 Proof
-  Induct_on ‘ls’ >> rw [list_t_v_def] >~ [‘ListNil’] >>
-  pure_once_rewrite_tac [insert_def] >> rw []
-  >- (rw [lookup_def, lookup_raw_def, list_t_v_def])
-  >- (rw [lookup_def, lookup_raw_def, list_t_v_def]) >>
-  case_tac >> rw []
-  >- (rw [lookup_def, lookup_raw_def, list_t_v_def])
-  >- (rw [lookup_def, lookup_raw_def, list_t_v_def]) >>
+  fs [hash_map_contains_key_fwd_def] >>
+  fs [hash_key_fwd_def] >>
+  rw [] >>
+  (* TODO: automate *)
+  qspec_assume ‘hm.hash_map_slots’ vec_len_spec >> fs [] >>
+  progress >> gvs []
+  >- (
+    fs [hash_map_t_inv_def, hash_map_t_base_inv_def, vec_len_def] >>
+    massage >> int_tac) >>
+  progress >> massage >> gvs [int_rem_def]
+  >- (irule pos_mod_pos_lt >> fs [hash_map_t_inv_def, hash_map_t_base_inv_def] >> int_tac) >>
   progress >>
-  fs [lookup_def, lookup_raw_def, list_t_v_def]
+  fs [lookup_s_def, vec_index_def, slots_t_lookup_def]
 QED
-
-(*
- * Invariant proof 1
- *)
-
-Theorem distinct_keys_cons:
-  ∀ k v ls.
-  (∀ i. 0 ≤ i ⇒ i < len ls ⇒ FST (index i ls) ≠ k) ⇒
-  distinct_keys ls ⇒
-  distinct_keys ((k,v) :: ls)
+val _ = save_spec_thm "hash_map_contains_key_fwd_spec"
+
+Theorem hash_map_get_in_list_fwd_spec:
+  ∀ ls key.
+  case hash_map_get_in_list_fwd key ls of
+  | Diverge => F
+  | Fail _ => slot_t_lookup key ls = NONE
+  | Return x => slot_t_lookup key ls = SOME x
+Proof
+  fs [hash_map_get_in_list_fwd_def] >>
+  Induct_on ‘ls’ >> pure_once_rewrite_tac [hash_map_get_in_list_loop_fwd_def] >>
+  fs [] >>
+  rw []
+QED
+val _ = save_spec_thm "hash_map_get_in_list_fwd_spec"
+
+Theorem hash_map_get_fwd_spec:
+  ∀ hm key.
+  hash_map_t_inv hm ⇒
+  case hash_map_get_fwd hm key of
+  | Diverge => F
+  | Fail _ => lookup_s hm key = NONE
+  | Return x => lookup_s hm key = SOME x
+Proof
+  rw [hash_map_get_fwd_def] >>
+  fs [hash_key_fwd_def] >>
+  (* TODO: automate *)
+  qspec_assume ‘hm.hash_map_slots’ vec_len_spec >> fs [] >>
+  progress >> gvs []
+  >- (
+    fs [hash_map_t_inv_def, hash_map_t_base_inv_def, vec_len_def] >>
+    massage >> int_tac) >>
+  progress >> massage >> gvs [int_rem_def]
+  >- (irule pos_mod_pos_lt >> fs [hash_map_t_inv_def, hash_map_t_base_inv_def] >> int_tac) >>
+  progress >>
+  gvs [lookup_s_def, vec_index_def, slots_t_lookup_def]
+QED
+val _ = save_spec_thm "hash_map_get_fwd_spec"
+
+Theorem hash_map_get_mut_in_list_fwd_spec:
+  ∀ ls key.
+  case hash_map_get_mut_in_list_fwd ls key of
+  | Diverge => F
+  | Fail _ => slot_t_lookup key ls = NONE
+  | Return x => slot_t_lookup key ls = SOME x
+Proof
+  fs [hash_map_get_mut_in_list_fwd_def] >>
+  Induct_on ‘ls’ >> pure_once_rewrite_tac [hash_map_get_mut_in_list_loop_fwd_def] >>
+  fs [] >>
+  rw []
+QED
+val _ = save_spec_thm "hash_map_get_mut_in_list_fwd_spec"
+
+Theorem hash_map_get_mut_fwd_spec:
+  ∀ hm key.
+  hash_map_t_inv hm ⇒
+  case hash_map_get_mut_fwd hm key of
+  | Diverge => F
+  | Fail _ => lookup_s hm key = NONE
+  | Return x => lookup_s hm key = SOME x
 Proof
+  rw [hash_map_get_mut_fwd_def] >>
+  fs [hash_key_fwd_def] >>
+  (* TODO: automate *)
+  qspec_assume ‘hm.hash_map_slots’ vec_len_spec >> fs [] >>
+  progress >> gvs []
+  >- (
+    fs [hash_map_t_inv_def, hash_map_t_base_inv_def, vec_len_def] >>
+    massage >> int_tac) >>
+  progress >> massage >> gvs [int_rem_def]
+  >- (irule pos_mod_pos_lt >> fs [hash_map_t_inv_def, hash_map_t_base_inv_def] >> int_tac) >>
+  progress >>
+  gvs [lookup_s_def, vec_index_def, slots_t_lookup_def]
+QED
+val _ = save_spec_thm "hash_map_get_mut_fwd_spec"
+
+Theorem hash_map_get_mut_in_list_back_spec:
+  ∀ ls key nx.
+  slot_t_lookup key ls ≠ NONE ⇒
+  ∃ nls. hash_map_get_mut_in_list_back ls key nx = Return nls ∧
+  slot_t_lookup key nls = SOME nx ∧
+  (∀ k. k ≠ key ⇒ slot_t_lookup k nls = slot_t_lookup k ls)
+Proof
+  fs [hash_map_get_mut_in_list_back_def] >>
+  Induct_on ‘ls’ >> pure_once_rewrite_tac [hash_map_get_mut_in_list_loop_back_def] >>
+  fs [] >>
   rw [] >>
-  rw [distinct_keys_def] >>
-  Cases_on ‘i = 0’ >> fs []
+  fs [] >>
+  progress >>
+  fs []  
+QED
+val _ = save_spec_thm "hash_map_get_mut_in_list_back_spec"
+
+Theorem hash_map_get_mut_back_spec:
+  ∀ hm key nx.
+  lookup_s hm key ≠ NONE ⇒
+  hash_map_t_inv hm ⇒
+  ∃ hm1. hash_map_get_mut_back hm key nx = Return hm1 ∧
+  lookup_s hm1 key = SOME nx ∧
+  (∀ k. k ≠ key ⇒ lookup_s hm1 k = lookup_s hm k)
+Proof
+  rw [hash_map_get_mut_back_def] >>
+  fs [hash_key_fwd_def] >>
+  (* TODO: automate *)
+  qspec_assume ‘hm.hash_map_slots’ vec_len_spec >> fs [] >>
+  progress >> gvs []
+  >- (
+    fs [hash_map_t_inv_def, hash_map_t_base_inv_def, vec_len_def] >>
+    massage >> int_tac) >>
+  progress >> massage >> gvs [int_rem_def]
+  (* TODO: we did this proof many times *)
+  >- (irule pos_mod_pos_lt >> fs [hash_map_t_inv_def, hash_map_t_base_inv_def] >> int_tac) >>
+  progress >>
+  gvs [lookup_s_def, vec_index_def, slots_t_lookup_def] >>
+  progress
+  (* TODO: again the same proof *)
+  >- (massage >> irule pos_mod_pos_lt >> fs [hash_map_t_inv_def, hash_map_t_base_inv_def] >> int_tac) >>
+  gvs [] >>
+  conj_tac
   >-(
-    (* Use the first hypothesis *)
-    fs [index_eq] >>
-    last_x_assum (qspecl_assume [‘j - 1’]) >>
-    sg ‘0 ≤ j - 1’ >- int_tac >>
-    fs [len_def] >>
-    sg ‘j - 1 < len ls’ >- int_tac >>
+    sg_dep_rewrite_goal_tac index_update_diff
+    >- (fs [] >> irule pos_mod_pos_lt >> fs [hash_map_t_inv_def, hash_map_t_base_inv_def] >> int_tac) >>
     fs []
   ) >>
-  (* Use the second hypothesis *)
-  sg ‘0 < i’ >- int_tac >>
-  sg ‘0 < j’ >- int_tac >>
-  fs [distinct_keys_def, index_eq, len_def] >>
-  first_x_assum (qspecl_assume [‘i - 1’, ‘j - 1’]) >>
-  sg ‘0 ≤ i - 1 ∧ i - 1 < j - 1 ∧ j - 1 < len ls’ >- int_tac >>
+  rw [] >>
+  Cases_on ‘usize_to_int k % len (vec_to_list hm.hash_map_slots) = usize_to_int key % len (vec_to_list hm.hash_map_slots)’ >>
+  fs []
+  >- (
+    sg_dep_rewrite_goal_tac index_update_diff
+    >- (fs [] >> irule pos_mod_pos_lt >> fs [hash_map_t_inv_def, hash_map_t_base_inv_def] >> int_tac) >>
+    fs []
+  ) >>
+  sg_dep_rewrite_goal_tac index_update_same
+  >- (
+    rw []
+    >- (irule pos_mod_pos_lt >> massage >> fs [hash_map_t_inv_def, hash_map_t_base_inv_def] >> int_tac)
+    >- (irule pos_mod_pos_lt >> massage >> fs [hash_map_t_inv_def, hash_map_t_base_inv_def] >> int_tac)
+  ) >>
   fs []
 QED
+val _ = save_spec_thm "hash_map_get_mut_back_spec"
 
-Theorem distinct_keys_tail:
-  ∀ k v ls.
-  distinct_keys ((k,v) :: ls) ⇒
-  distinct_keys ls
+Theorem hash_map_remove_from_list_fwd_spec:
+  ∀ key l i ls.
+  hash_map_remove_from_list_fwd key ls = Return (slot_t_lookup key ls)
 Proof
-  rw [distinct_keys_def] >>
-  last_x_assum (qspecl_assume [‘i + 1’, ‘j + 1’]) >>
-  fs [len_def] >>
-  sg ‘0 ≤ i + 1 ∧ i + 1 < j + 1 ∧ j + 1 < 1 + len ls’ >- int_tac >> fs [] >>
-  sg ‘0 < i + 1 ∧ 0 < j + 1’ >- int_tac >> fs [index_eq] >>
-  sg ‘i + 1 - 1 = i ∧ j + 1 - 1 = j’ >- int_tac >> fs []
+  fs [hash_map_remove_from_list_fwd_def] >>
+  Induct_on ‘ls’ >> pure_once_rewrite_tac [hash_map_remove_from_list_loop_fwd_def] >>
+  rw [] >>
+  metis_tac []
 QED
+val _ = save_spec_thm "hash_map_remove_from_list_fwd_spec"
 
-Theorem insert_index_neq:
-  ∀ q k v ls0 ls1 i.
-  (∀ j. 0 ≤ j ∧ j < len (list_t_v ls0) ⇒ q ≠ FST (index j (list_t_v ls0))) ⇒
-  q ≠ k ⇒
-  insert k v ls0 = Return ls1 ⇒
-  0 ≤ i ⇒
-  i < len (list_t_v ls1) ⇒
-  FST (index i (list_t_v ls1)) ≠ q
+Theorem lookup_SOME_not_empty:
+  ∀ ls k. lookup k ls ≠ NONE ⇒ 0 < len ls
 Proof
-  ntac 3 strip_tac >>
-  Induct_on ‘ls0’ >> rw [] >~ [‘ListNil’]
-  >-(
-    fs [insert_def] >>
-    sg ‘ls1 = ListCons (k,v) ListNil’ >- fs [] >> fs [list_t_v_def, len_def] >>
-    sg ‘i = 0’ >- int_tac >> fs [index_eq]) >>
-  Cases_on ‘t’ >>
-  Cases_on ‘i = 0’ >> fs []
-  >-(
-    qpat_x_assum ‘insert _ _ _ = _’ mp_tac >>
-    simp [MK_BOUNDED insert_def 1, bind_def] >>
-    Cases_on ‘q' = k’ >> rw []
-    >- (fs [list_t_v_def, index_eq]) >>
-    Cases_on ‘insert k v ls0’ >> fs [] >>
-    gvs [list_t_v_def, index_eq] >>
-    first_x_assum (qspec_assume ‘0’) >>
-    fs [len_def] >>
-    strip_tac >>
-    qspec_assume ‘list_t_v ls0’ len_pos >>
-    sg ‘0 < 1 + len (list_t_v ls0)’ >- int_tac >>
-    fs []) >>
-  qpat_x_assum ‘insert _ _ _ = _’ mp_tac >>
-  simp [MK_BOUNDED insert_def 1, bind_def] >>
-  Cases_on ‘q' = k’ >> rw []
-  >-(
-    fs [list_t_v_def, index_eq, len_def] >>
-    first_x_assum (qspec_assume ‘i’) >> rfs []) >>
-  Cases_on ‘insert k v ls0’ >> fs [] >>
-  gvs [list_t_v_def, index_eq] >>
-  last_x_assum (qspec_assume ‘i - 1’) >>
-  fs [len_def] >>
-  sg ‘0 ≤ i - 1 ∧ i - 1 < len (list_t_v a)’ >- int_tac >> fs [] >>
-  first_x_assum irule >>
-  rw [] >>
-  last_x_assum (qspec_assume ‘j + 1’) >>
-  rfs [] >>
-  sg ‘j + 1 < 1 + len (list_t_v ls0) ∧ j + 1 − 1 = j ∧ j + 1 ≠ 0’ >- int_tac >> fs []
+  Induct_on ‘ls’ >> fs [] >> rw [] >>
+  qspec_assume ‘ls’ len_pos >>
+  int_tac
 QED
 
-Theorem distinct_keys_insert_index_neq:
-  ∀ k v q r ls0 ls1 i.
-  distinct_keys ((q,r)::list_t_v ls0) ⇒
-  q ≠ k ⇒
-  insert k v ls0 = Return ls1 ⇒
+Theorem slot_t_lookup_SOME_not_empty:
+  ∀ ls i k.
   0 ≤ i ⇒
-  i < len (list_t_v ls1) ⇒
-  FST (index i (list_t_v ls1)) ≠ q
+  i < len ls ⇒
+  slot_t_lookup k (index i ls) ≠ NONE ⇒
+  0 < len (FLAT (MAP list_t_v ls))
 Proof
-  rw [] >>
-  (* Use the first assumption to prove the following assertion *)
-  sg ‘∀ j. 0 ≤ j ∧ j < len (list_t_v ls0) ⇒ q ≠ FST (index j (list_t_v ls0))’
+  Induct_on ‘ls’ >> rw [] >> try_tac int_tac >>
+  gvs [index_eq] >>
+  Cases_on ‘i = 0’ >> fs []
   >-(
-    strip_tac >>
-    fs [distinct_keys_def] >>
-    last_x_assum (qspecl_assume [‘0’, ‘j + 1’]) >>
-    fs [index_eq] >>
-    sg ‘j + 1 - 1 = j’ >- int_tac >> fs [len_def] >>
-    rw []>>
-    first_x_assum irule >> int_tac) >>
-  qspecl_assume [‘q’, ‘k’, ‘v’, ‘ls0’, ‘ls1’, ‘i’] insert_index_neq >>
-  fs []
+    qspec_assume ‘FLAT (MAP list_t_v ls)’ len_pos >>
+    imp_res_tac lookup_SOME_not_empty >> int_tac
+  ) >>
+  qspec_assume ‘list_t_v h’ len_pos >>
+  last_x_assum (qspecl_assume [‘i - 1’, ‘k’]) >>
+  sg ‘0 ≤ i - 1 ∧ i - 1 < len ls ∧ i ≠ 0’ >- int_tac >>
+  gvs [] >>
+  int_tac
 QED
 
-Theorem distinct_keys_insert:
-  ∀ k v ls0 ls1.
-  distinct_keys (list_t_v ls0) ⇒
-  insert k v ls0 = Return ls1 ⇒
-  distinct_keys (list_t_v ls1)
+Theorem lookup_s_SOME_not_empty:
+  ∀ hm key.
+    hash_map_t_inv hm ⇒
+    lookup_s hm key ≠ NONE ⇒ 0 < len_s hm
 Proof
-  Induct_on ‘ls0’ >~ [‘ListNil’]
-  >-(
-    rw [distinct_keys_def, list_t_v_def, insert_def] >>
-    fs [list_t_v_def, len_def] >>
-    int_tac) >>  
-  Cases >>
-  pure_once_rewrite_tac [insert_def] >> fs[] >>
-  rw [] >> fs []
-  >-(
-    (* k = q *)
-    last_x_assum ignore_tac >>
-    fs [distinct_keys_def] >>
-    rw [] >>
-    last_x_assum (qspecl_assume [‘i’, ‘j’]) >>
-    rfs [list_t_v_def, len_def] >>
-    sg ‘0 < j’ >- int_tac >>
-    Cases_on ‘i = 0’ >> gvs [index_eq]) >>
-  (* k ≠ q: recursion *)
-  Cases_on ‘insert k v ls0’ >> fs [bind_def] >>
-  last_x_assum (qspecl_assume [‘k’, ‘v’, ‘a’]) >>
-  gvs [list_t_v_def] >>
-  imp_res_tac distinct_keys_tail >> fs [] >>
-  irule distinct_keys_cons >> rw [] >>
-  metis_tac [distinct_keys_insert_index_neq]
-QED  
+  rw [lookup_s_def, slots_t_lookup_def, len_s_def, hash_map_t_al_v_def, hash_map_t_v_def] >>
+  sg ‘0 < len (vec_to_list hm.hash_map_slots)’
+  >- (fs [hash_map_t_inv_def, hash_map_t_base_inv_def] >> int_tac) >>
+  irule slot_t_lookup_SOME_not_empty >>
+  qexists ‘usize_to_int key % len (vec_to_list hm.hash_map_slots)’ >>
+  qexists ‘key’ >>
+  rw []
+  >-(irule pos_mod_pos_is_pos >> massage >> int_tac) >>
+  irule pos_mod_pos_lt >> massage >> int_tac
+QED
 
-Theorem insert_lem:
-  !ls key value.
-    (* The keys are pairwise distinct *)
-    distinct_keys (list_t_v ls) ==>
-    case insert key value ls of
-    | Return ls1 =>
-      (* We updated the binding *)
-      lookup key ls1 = SOME value /\
-      (* The other bindings are left unchanged *)
-      (!k. k <> key ==> lookup k ls = lookup k ls1) ∧
-      (* The keys are still pairwise disjoint *)
-      distinct_keys (list_t_v ls1)
-    | Fail _ => F
-    | Diverge => F
+Theorem hash_map_remove_fwd_spec:
+  ∀ hm key.
+  hash_map_t_inv hm ⇒
+  hash_map_remove_fwd hm key = Return (lookup_s hm key)
 Proof
-  rw [] >>
-  qspecl_assume [‘ls’, ‘key’, ‘value’] insert_lem_aux >>
+  rw [hash_map_remove_fwd_def] >>
+  (* TODO: automate *)
+  qspec_assume ‘hm.hash_map_slots’ vec_len_spec >> fs [] >>
+  sg ‘0 < usize_to_int (vec_len hm.hash_map_slots)’
+  >- (fs [hash_map_t_inv_def, hash_map_t_base_inv_def, vec_len_def] >> massage >> int_tac) >>
+  fs [vec_len_def] >> massage >>
+  (* TODO: rewriting for: usize_to_int (int_to_usize (len (vec_to_list v))) = len (vec_to_list v) *)
+  progress >>
+  progress >> fs [int_rem_def, vec_len_def] >> massage
+  >- (irule pos_mod_pos_lt >> fs []) >>
+  progress >>
+  gvs [lookup_s_def, slots_t_lookup_def, vec_index_def] >>
   case_tac >> fs [] >>
-  metis_tac [distinct_keys_insert]
+  progress >>
+  (* Prove that we can decrement the number of entries *)
+  qspecl_assume [‘hm’, ‘key’] lookup_s_SOME_not_empty >>
+  gvs [lookup_s_def, slots_t_lookup_def, len_s_def, hash_map_t_inv_def, hash_map_t_base_inv_def] >>
+  int_tac
 QED
+val _ = save_spec_thm "hash_map_remove_fwd_spec"
 
-(*
- * Invariant proof 2: functional version of the invariant
- *)
-
-Theorem distinct_keys_f_insert_for_all:
-  ∀k v k1 ls0 ls1.
-  k1 ≠ k ⇒
-  for_all (λy. k1 ≠ FST y) (list_t_v ls0) ⇒
-  pairwise_rel (λx y. FST x ≠ FST y) (list_t_v ls0) ⇒
-  insert k v ls0 = Return ls1 ⇒
-  for_all (λy. k1 ≠ FST y) (list_t_v ls1)
+Theorem every_distinct_remove_every_distinct:
+  ∀ k0 k1 ls0.
+  EVERY (λy. k1 ≠ FST y) ls0 ⇒
+  EVERY (λy. k1 ≠ FST y) (remove k0 ls0)
 Proof
-  Induct_on ‘ls0’ >> rw [pairwise_rel_def] >~ [‘ListNil’] >>
-  gvs [list_t_v_def, pairwise_rel_def, for_all_def]
-  >-(gvs [MK_BOUNDED insert_def 1, bind_def, list_t_v_def, for_all_def]) >>
-  pat_undisch_tac ‘insert _ _ _ = _’ >>
-  simp [MK_BOUNDED insert_def 1, bind_def] >>
-  Cases_on ‘t’ >> rw [] >> gvs [list_t_v_def, pairwise_rel_def, for_all_def] >>
-  Cases_on ‘insert k v ls0’ >>
-  gvs [distinct_keys_f_def, list_t_v_def, pairwise_rel_def, for_all_def] >>
-  metis_tac []
+  Induct_on ‘ls0’ >> fs [] >> rw [] >>
+  Cases_on ‘h’ >> fs [] >>
+  case_tac >> fs []
 QED
 
-Theorem distinct_keys_f_insert:
-  ∀ k v ls0 ls1.
-  distinct_keys_f (list_t_v ls0) ⇒
-  insert k v ls0 = Return ls1 ⇒
-  distinct_keys_f (list_t_v ls1)
+Theorem hash_map_remove_from_list_back_spec:
+  ∀ key ls.
+  ∃ ls1. hash_map_remove_from_list_back key ls = Return ls1 ∧
+  (∀ l i. slot_t_inv l i ls ⇒
+    slot_t_inv l i ls1 ∧
+    list_t_v ls1 = remove key (list_t_v ls) ∧
+    slot_t_lookup key ls1 = NONE ∧
+    (∀ k. k ≠ key ⇒ slot_t_lookup k ls1 = slot_t_lookup k ls) ∧
+    (case slot_t_lookup key ls of
+     | NONE => len (list_t_v ls1) = len (list_t_v ls)
+     | SOME _ => len (list_t_v ls1) = len (list_t_v ls) - 1))
 Proof
-  Induct_on ‘ls0’ >> rw [distinct_keys_f_def] >~ [‘ListNil’]
-  >-(
-    fs [list_t_v_def, insert_def] >>
-    gvs [list_t_v_def, pairwise_rel_def, for_all_def]) >>
-  last_x_assum (qspecl_assume [‘k’, ‘v’]) >>
-  pat_undisch_tac ‘insert _ _ _ = _’ >>
-  simp [MK_BOUNDED insert_def 1, bind_def] >>
-  (* TODO: improve case_tac *)
-  Cases_on ‘t’ >> rw [] >> gvs [list_t_v_def, pairwise_rel_def, for_all_def] >>
-  Cases_on ‘insert k v ls0’ >>
-  gvs [distinct_keys_f_def, list_t_v_def, pairwise_rel_def, for_all_def] >>
-  metis_tac [distinct_keys_f_insert_for_all]
+  fs [hash_map_remove_from_list_back_def] >>
+  Induct_on ‘ls’ >> pure_once_rewrite_tac [hash_map_remove_from_list_loop_back_def] >>
+  fs [slot_t_inv_def] >>
+  fs [distinct_keys_def, pairwise_rel_def] >>
+  rw []
+  >- (metis_tac [])
+  >- (
+    last_x_assum ignore_tac >>
+    pop_assum ignore_tac >>
+    pop_assum ignore_tac >>
+    Induct_on ‘ls’ >> fs []
+  ) >>
+  progress >>
+  rw [] >> gvs [pairwise_rel_def]
+  >- metis_tac [every_distinct_remove_every_distinct]
+  >- metis_tac []
+  >- metis_tac []
+  >- metis_tac []
+  >- metis_tac [] >>
+  case_tac >> fs [] >- metis_tac [] >>
+  first_x_assum (qspecl_assume [‘l’, ‘i’]) >> gvs [] >>
+  pop_assum sg_premise_tac >- metis_tac [] >> fs []
 QED
+val _ = save_spec_thm "hash_map_remove_from_list_back_spec"
 
-(*
- * Proving equivalence between the two version - exercise.
- *)
-Theorem for_all_quant:
-  ∀p ls. for_all p ls ⇔ ∀i. 0 ≤ i ⇒ i < len ls ⇒ p (index i ls)
+(* TODO: automate this *)
+Theorem hash_map_remove_back_branch_eq:
+  ∀ key hm a.
+  (case lookup key (list_t_v (vec_index hm.hash_map_slots a)) of
+    NONE =>
+      do
+        l0 <-
+          hash_map_remove_from_list_back key
+            (vec_index hm.hash_map_slots a);
+        v <- vec_index_mut_back hm.hash_map_slots a l0;
+        Return (hm with hash_map_slots := v)
+      od
+  | SOME x0 =>
+    do
+      i0 <- usize_sub hm.hash_map_num_entries (int_to_usize 1);
+      l0 <-
+        hash_map_remove_from_list_back key
+          (vec_index hm.hash_map_slots a);
+      v <- vec_index_mut_back hm.hash_map_slots a l0;
+      Return
+        (hm with <|hash_map_num_entries := i0; hash_map_slots := v|>)
+    od) =
+  (do
+   i0 <- (case lookup key (list_t_v (vec_index hm.hash_map_slots a)) of
+         | NONE => Return hm.hash_map_num_entries
+         | SOME _ => usize_sub hm.hash_map_num_entries (int_to_usize 1));
+   l0 <-
+     hash_map_remove_from_list_back key
+       (vec_index hm.hash_map_slots a);
+   v <- vec_index_mut_back hm.hash_map_slots a l0;
+   Return
+     (hm with <|hash_map_num_entries := i0; hash_map_slots := v|>)
+   od)
 Proof
-  strip_tac >> Induct_on ‘ls’
-  >-(rw [for_all_def, len_def] >> int_tac) >>
-  rw [for_all_def, len_def, index_eq] >>
-  equiv_tac
-  >-(
-    rw [] >>
-    Cases_on ‘i = 0’ >> fs [] >>
-    first_x_assum irule >>
-    int_tac) >>
-  rw []
-  >-(
-    first_x_assum (qspec_assume ‘0’) >> fs [] >>
-    first_x_assum irule >>
-    qspec_assume ‘ls’ len_pos >>
-    int_tac) >>
-  first_x_assum (qspec_assume ‘i + 1’) >>
-  fs [] >>
-  sg ‘i + 1 ≠ 0 ∧ i + 1 - 1 = i’ >- int_tac >> fs [] >>
-  first_x_assum irule >> int_tac
+  rw [bind_def] >>
+  rpt (case_tac >> fs []) >>
+  Cases_on ‘hm’ >> fs [] >>
+  fs [hashmap_TypesTheory.recordtype_hash_map_t_seldef_hash_map_slots_fupd_def,
+      hashmap_TypesTheory.recordtype_hash_map_t_seldef_hash_map_num_entries_fupd_def]
 QED
 
-Theorem pairwise_rel_quant:
-  ∀p ls. pairwise_rel p ls ⇔
-    (∀i j. 0 ≤ i ⇒ i < j ⇒ j < len ls ⇒ p (index i ls) (index j ls))
+(* TODO: this doesn't work very well *)
+Theorem lookup_cond_decr_entries_eq:
+  ∀ hm key i.
+  hash_map_t_inv hm ⇒
+  usize_to_int i < len (vec_to_list hm.hash_map_slots) ⇒
+  ∃ j.
+  (case lookup key (list_t_v (vec_index hm.hash_map_slots i)) of
+    NONE => Return hm.hash_map_num_entries
+  | SOME v => usize_sub hm.hash_map_num_entries (int_to_usize 1)) = Return j ∧
+  (lookup key (list_t_v (vec_index hm.hash_map_slots i)) = NONE ⇒ j = hm.hash_map_num_entries) ∧
+  (lookup key (list_t_v (vec_index hm.hash_map_slots i)) ≠ NONE ⇒
+   usize_to_int j + 1 = usize_to_int hm.hash_map_num_entries)
 Proof
-  strip_tac >> Induct_on ‘ls’
-  >-(rw [pairwise_rel_def, len_def] >> int_tac) >>
-  rw [pairwise_rel_def, len_def] >>
-  equiv_tac
-  >-(
-    (* ==> *)
-    rw [] >>
-    sg ‘0 < j’ >- int_tac >>
-    Cases_on ‘i = 0’
-    >-(
-      simp [index_eq] >>
-      qspecl_assume [‘p h’, ‘ls’] (iffLR for_all_quant) >>
-      first_x_assum irule >> fs [] >> int_tac
-    ) >>
-    rw [index_eq] >>
-    first_x_assum irule >> int_tac
-  ) >>
-  (* <== *)
-  rw []
-  >-(
-    rw [for_all_quant] >>
-    first_x_assum (qspecl_assume [‘0’, ‘i + 1’]) >>
-    sg ‘0 < i + 1 ∧ i + 1 - 1 = i’ >- int_tac >>
-    fs [index_eq] >>
-    first_x_assum irule >> int_tac
-  ) >>
-  sg ‘pairwise_rel p ls’
-  >-(
-    rw [pairwise_rel_def] >>
-    first_x_assum (qspecl_assume [‘i' + 1’, ‘j' + 1’]) >>
-    sg ‘0 < i' + 1 ∧ 0 < j' + 1’ >- int_tac >>
-    fs [index_eq, int_add_minus_same_eq] >>
-    first_x_assum irule >> int_tac
-  ) >>
-  fs []
+  rw [] >>
+  case_tac >>
+  progress >>
+  qspecl_assume [‘vec_to_list hm.hash_map_slots’, ‘usize_to_int i’, ‘key’] slot_t_lookup_SOME_not_empty >>
+  gvs [vec_index_def] >>
+  fs [hash_map_t_inv_def, hash_map_t_base_inv_def, hash_map_t_al_v_def, hash_map_t_v_def] >>
+  int_tac
 QED
 
-Theorem distinct_keys_f_eq_distinct_keys:
-  ∀ ls.
-  distinct_keys_f ls ⇔ distinct_keys ls
+(* TODO: when saving a spec theorem, check that all the variables which appear
+   in the pre/postconditions also appear in the application *)
+Theorem hash_map_remove_back_spec:
+  ∀ hm key.
+  hash_map_t_inv hm ⇒
+  ∃ hm1. hash_map_remove_back hm key = Return hm1 ∧
+  hash_map_t_inv hm1 ∧
+  lookup_s hm1 key = NONE ∧
+  (∀ k. k ≠ key ⇒ lookup_s hm1 k = lookup_s hm k) ∧
+  (case lookup_s hm key of
+   | NONE => len_s hm1 = len_s hm
+   | SOME _ => len_s hm1 = len_s hm - 1)
 Proof
-  rw [distinct_keys_def, distinct_keys_f_def] >>
-  qspecl_assume [‘(λx y. FST x ≠ FST y)’, ‘ls’] pairwise_rel_quant >>
-  fs []
+  rw [hash_map_remove_back_def] >>
+  (* TODO: automate *)
+  qspec_assume ‘hm.hash_map_slots’ vec_len_spec >>
+  fs [vec_len_def] >>
+  sg ‘0 < usize_to_int (vec_len hm.hash_map_slots)’
+  >-(fs [hash_map_t_inv_def, hash_map_t_base_inv_def, gt_eq_lt, vec_len_def] >> massage >> fs []) >>
+  (* TODO: we have to prove this several times - it would be good to remember the preconditions
+     we proved, sometimes *)
+  sg ‘usize_to_int key % len (vec_to_list hm.hash_map_slots) < usize_to_int (vec_len hm.hash_map_slots)’
+  >- (fs [vec_len_def] >> massage >> irule pos_mod_pos_lt >> int_tac) >>
+  (* TODO: add a rewriting rule *)
+  sg ‘usize_to_int (vec_len hm.hash_map_slots) = len (vec_to_list hm.hash_map_slots)’
+  >- (fs [vec_len_def] >> massage >> fs []) >>
+  fs [vec_len_def] >>
+  massage >>
+  progress >>
+  progress >> fs [int_rem_def, vec_len_def] >>
+  progress >>
+  gvs [] >>
+  fs [hash_map_remove_back_branch_eq] >>
+  qspecl_assume [‘hm’, ‘key’, ‘a’] lookup_cond_decr_entries_eq >>
+  gvs [] >>
+  progress >>
+  progress >> fs [vec_len_def] >>
+  (* Prove the post-condition *)
+  sg ‘let i = usize_to_int key % len (vec_to_list hm.hash_map_slots) in
+      slot_t_inv (len (vec_to_list hm.hash_map_slots)) i (index i (vec_to_list hm.hash_map_slots))’
+  >- (fs [hash_map_t_inv_def, hash_map_t_base_inv_def, slots_t_inv_def]) >> fs [] >>
+  gvs [vec_index_def] >>
+  qpat_assum ‘∀l. _’ imp_res_tac >>
+  rw []
+  >- (
+    fs [hash_map_t_inv_def, hash_map_t_base_inv_def, hash_map_t_al_v_def, hash_map_t_v_def] >>
+    rw []
+    >- (
+      (* The num_entries has been correctly updated *)
+      sg_dep_rewrite_goal_tac len_FLAT_MAP_update >- int_tac >> fs [] >> pop_assum ignore_tac >> gvs [] >>
+      (* Case analysis on: ‘lookup key (index (key % len slots) slots)’ *)
+      pop_assum mp_tac >> case_tac >> fs [] >>
+      int_tac
+    )
+    >- (
+      fs [slots_t_inv_def] >> rw [] >>
+      (* TODO: this is annoying: we proved this many times *)
+      last_x_assum (qspec_assume ‘i’) >>
+      gvs [vec_index_def] >>
+      qpat_x_assum ‘∀l. _’ imp_res_tac >>
+      Cases_on ‘i = usize_to_int key % len (vec_to_list hm.hash_map_slots)’ >> fs []
+      >- (
+        sg_dep_rewrite_goal_tac index_update_diff
+        >- (fs [] >> int_tac) >> fs []
+      ) >>
+      sg_dep_rewrite_goal_tac index_update_same
+      >- (fs [] >> int_tac) >> fs []
+    ) >>
+    (* num_entries ≤ max_load *)
+    Cases_on ‘lookup key (list_t_v (index
+                (usize_to_int key % len (vec_to_list hm.hash_map_slots))
+                (vec_to_list hm.hash_map_slots)))’ >> gvs [] >>
+    (* Remains the case where we decrment num_entries - TODO: this is too much work, should be easier *)
+    gvs [usize_sub_def, mk_usize_def] >>
+    massage >>
+    sg ‘0 ≤ len_s hm - 1 ∧ len_s hm - 1 ≤ usize_max’
+    >- (fs [len_s_def, hash_map_t_al_v_def, hash_map_t_v_def] >> int_tac) >>
+    fs [len_s_def, hash_map_t_al_v_def, hash_map_t_v_def] >>
+    gvs [] >>
+    massage >>
+    Cases_on ‘hm.hash_map_max_load_factor’ >> gvs [] >>
+    disj1_tac >> int_tac)
+  >- (
+    fs [lookup_s_def, slots_t_lookup_def] >>
+    (* TODO: we did this too many times *)
+    sg_dep_rewrite_goal_tac index_update_diff
+    >- (fs [] >> int_tac) >> fs [] >>
+    metis_tac []
+  )
+  >- (
+    (* Lookup of k ≠ key *)
+    fs [lookup_s_def, slots_t_lookup_def] >>
+    Cases_on ‘usize_to_int k % len (vec_to_list hm.hash_map_slots) = usize_to_int key % len (vec_to_list hm.hash_map_slots)’ >> fs []
+    >- (
+      sg_dep_rewrite_goal_tac index_update_diff
+      >- (fs [] >> int_tac) >> fs [] >>
+      metis_tac []) >>
+    sg_dep_rewrite_goal_tac index_update_same
+    >- (rw [] >> try_tac int_tac >> irule pos_mod_pos_lt >> fs [] >> massage >> fs []) >> fs [] >>
+    case_tac >> fs [] >>
+    metis_tac []
+  ) >>
+  (* The length is correctly updated *)
+  fs [lookup_s_def, len_s_def, slots_t_lookup_def, hash_map_t_al_v_def, hash_map_t_v_def] >>
+  case_tac >> gvs [] >>  
+  sg_dep_rewrite_goal_tac len_FLAT_MAP_update >> fs [] >> int_tac
 QED
+val _ = save_spec_thm "hash_map_remove_back_spec"
 
 val _ = export_theory ()
-*)
diff --git a/tests/hol4/hashmap/hashmap_PropertiesTheory.sig b/tests/hol4/hashmap/hashmap_PropertiesTheory.sig
new file mode 100644
index 00000000..39b6f048
--- /dev/null
+++ b/tests/hol4/hashmap/hashmap_PropertiesTheory.sig
@@ -0,0 +1,902 @@
+signature hashmap_PropertiesTheory =
+sig
+  type thm = Thm.thm
+  
+  (*  Axioms  *)
+    val usize_u32_bounds : thm
+  
+  (*  Definitions  *)
+    val distinct_keys_def : thm
+    val hash_map_just_before_resize_pred_def : thm
+    val hash_map_same_params_def : thm
+    val hash_map_t_al_v_def : thm
+    val hash_map_t_base_inv_def : thm
+    val hash_map_t_inv_def : thm
+    val hash_map_t_v_def : thm
+    val hash_mod_key_def : thm
+    val insert_in_slot_t_rel_def : thm
+    val len_s_def : thm
+    val list_t_v_def : thm
+    val lookup_s_def : thm
+    val pairwise_rel_def : thm
+    val slot_s_inv_def : thm
+    val slot_s_inv_hash_def : thm
+    val slot_t_inv_def : thm
+    val slot_t_lookup_def : thm
+    val slot_t_remove_def : thm
+    val slots_s_inv_def : thm
+    val slots_t_inv_def : thm
+    val slots_t_lookup_def : thm
+  
+  (*  Theorems  *)
+    val EVERY_quant_equiv : thm
+    val FLAT_ListNil_is_nil : thm
+    val MEM_EVERY_not : thm
+    val MEM_distinct_keys_lookup : thm
+    val distinct_keys_MEM_not_eq : thm
+    val distinct_keys_lookup_NONE : thm
+    val every_distinct_remove_every_distinct : thm
+    val hash_map_allocate_slots_fwd_spec : thm
+    val hash_map_allocate_slots_loop_fwd_spec : thm
+    val hash_map_clear_fwd_back_spec : thm
+    val hash_map_clear_loop_fwd_back_spec : thm
+    val hash_map_clear_loop_fwd_back_spec_aux : thm
+    val hash_map_cond_incr_thm : thm
+    val hash_map_contains_key_fwd_spec : thm
+    val hash_map_contains_key_in_list_fwd_spec : thm
+    val hash_map_get_fwd_spec : thm
+    val hash_map_get_in_list_fwd_spec : thm
+    val hash_map_get_mut_back_spec : thm
+    val hash_map_get_mut_fwd_spec : thm
+    val hash_map_get_mut_in_list_back_spec : thm
+    val hash_map_get_mut_in_list_fwd_spec : thm
+    val hash_map_insert_fwd_back_spec : thm
+    val hash_map_insert_in_list_fwd_spec : thm
+    val hash_map_insert_in_list_loop_back_EVERY_distinct_keys : thm
+    val hash_map_insert_in_list_loop_back_distinct_keys : thm
+    val hash_map_insert_in_list_loop_back_spec : thm
+    val hash_map_insert_in_list_loop_back_spec_aux : thm
+    val hash_map_insert_in_list_loop_fwd_spec : thm
+    val hash_map_insert_no_resize_fwd_back_branches_eq : thm
+    val hash_map_insert_no_resize_fwd_back_spec : thm
+    val hash_map_insert_no_resize_fwd_back_spec_aux : thm
+    val hash_map_len_spec : thm
+    val hash_map_move_elements_from_list_fwd_back_spec : thm
+    val hash_map_move_elements_fwd_back_spec : thm
+    val hash_map_move_elements_loop_fwd_back_spec_aux : thm
+    val hash_map_new_fwd_spec : thm
+    val hash_map_new_with_capacity_fwd_spec : thm
+    val hash_map_remove_back_branch_eq : thm
+    val hash_map_remove_back_spec : thm
+    val hash_map_remove_from_list_back_spec : thm
+    val hash_map_remove_from_list_fwd_spec : thm
+    val hash_map_remove_fwd_spec : thm
+    val hash_map_same_params_refl : thm
+    val hash_map_t_base_inv_len_slots : thm
+    val hash_map_try_resize_fwd_back_spec : thm
+    val key_MEM_j_lookup_i_is_NONE : thm
+    val len_FLAT_MAP_update : thm
+    val len_index_FLAT_MAP_list_t_v : thm
+    val len_vec_FLAT_drop_update : thm
+    val lookup_SOME_not_empty : thm
+    val lookup_cond_decr_entries_eq : thm
+    val lookup_def : thm
+    val lookup_distinct_keys_MEM : thm
+    val lookup_ind : thm
+    val lookup_s_SOME_not_empty : thm
+    val pairwise_rel_quant_equiv : thm
+    val remove_def : thm
+    val remove_ind : thm
+    val slot_t_lookup_SOME_not_empty : thm
+  
+  val hashmap_Properties_grammars : type_grammar.grammar * term_grammar.grammar
+(*
+   [hashmap_Funs] Parent theory of "hashmap_Properties"
+   
+   [usize_u32_bounds]  Axiom
+      
+      [oracles: ] [axioms: usize_u32_bounds] [] ⊢ usize_max = u32_max
+   
+   [distinct_keys_def]  Definition
+      
+      ⊢ ∀ls. distinct_keys ls ⇔ pairwise_rel (λx y. FST x ≠ FST y) ls
+   
+   [hash_map_just_before_resize_pred_def]  Definition
+      
+      ⊢ ∀hm.
+          hash_map_just_before_resize_pred hm ⇔
+          (let
+             (dividend,divisor) = hm.hash_map_max_load_factor
+           in
+             usize_to_int hm.hash_map_num_entries =
+             usize_to_int hm.hash_map_max_load + 1 ∧
+             len (vec_to_list hm.hash_map_slots) * 2 *
+             usize_to_int dividend ≤ usize_max ∨
+             len (vec_to_list hm.hash_map_slots) * 2 *
+             usize_to_int dividend > usize_max)
+   
+   [hash_map_same_params_def]  Definition
+      
+      ⊢ ∀hm hm1.
+          hash_map_same_params hm hm1 ⇔
+          hm1.hash_map_max_load_factor = hm.hash_map_max_load_factor ∧
+          hm1.hash_map_max_load = hm.hash_map_max_load ∧
+          len (vec_to_list hm1.hash_map_slots) =
+          len (vec_to_list hm.hash_map_slots)
+   
+   [hash_map_t_al_v_def]  Definition
+      
+      ⊢ ∀hm. hash_map_t_al_v hm = FLAT (hash_map_t_v hm)
+   
+   [hash_map_t_base_inv_def]  Definition
+      
+      ⊢ ∀hm.
+          hash_map_t_base_inv hm ⇔
+          (let
+             al = hash_map_t_al_v hm
+           in
+             usize_to_int hm.hash_map_num_entries = len al ∧
+             slots_t_inv hm.hash_map_slots ∧
+             len (vec_to_list hm.hash_map_slots) > 0 ∧
+             (let
+                capacity = len (vec_to_list hm.hash_map_slots);
+                (dividend,divisor) = hm.hash_map_max_load_factor;
+                dividend = usize_to_int dividend;
+                divisor = usize_to_int divisor
+              in
+                0 < dividend ∧ dividend < divisor ∧
+                capacity * dividend ≥ divisor ∧
+                usize_to_int hm.hash_map_max_load =
+                capacity * dividend / divisor))
+   
+   [hash_map_t_inv_def]  Definition
+      
+      ⊢ ∀hm.
+          hash_map_t_inv hm ⇔
+          hash_map_t_base_inv hm ∧
+          (let
+             (dividend,divisor) = hm.hash_map_max_load_factor
+           in
+             usize_to_int hm.hash_map_num_entries ≤
+             usize_to_int hm.hash_map_max_load ∨
+             len (vec_to_list hm.hash_map_slots) * 2 *
+             usize_to_int dividend > usize_max)
+   
+   [hash_map_t_v_def]  Definition
+      
+      ⊢ ∀hm. hash_map_t_v hm = MAP list_t_v (vec_to_list hm.hash_map_slots)
+   
+   [hash_mod_key_def]  Definition
+      
+      ⊢ ∀k l.
+          hash_mod_key k l =
+          case hash_key_fwd k of
+            Return k => usize_to_int k % l
+          | Fail v3 => ARB
+          | Diverge => ARB
+   
+   [insert_in_slot_t_rel_def]  Definition
+      
+      ⊢ ∀l key value slot slot1.
+          insert_in_slot_t_rel l key value slot slot1 ⇔
+          slot_t_inv l (hash_mod_key key l) slot1 ∧
+          slot_t_lookup key slot1 = SOME value ∧
+          (∀k. k ≠ key ⇒ slot_t_lookup k slot = slot_t_lookup k slot1) ∧
+          case slot_t_lookup key slot of
+            NONE => len (list_t_v slot1) = len (list_t_v slot) + 1
+          | SOME v => len (list_t_v slot1) = len (list_t_v slot)
+   
+   [len_s_def]  Definition
+      
+      ⊢ ∀hm. len_s hm = len (hash_map_t_al_v hm)
+   
+   [list_t_v_def]  Definition
+      
+      ⊢ list_t_v ListNil = [] ∧
+        ∀k v tl. list_t_v (ListCons k v tl) = (k,v)::list_t_v tl
+   
+   [lookup_s_def]  Definition
+      
+      ⊢ ∀hm k.
+          lookup_s hm k = slots_t_lookup (vec_to_list hm.hash_map_slots) k
+   
+   [pairwise_rel_def]  Definition
+      
+      ⊢ (∀p. pairwise_rel p [] ⇔ T) ∧
+        ∀p x ls.
+          pairwise_rel p (x::ls) ⇔ EVERY (p x) ls ∧ pairwise_rel p ls
+   
+   [slot_s_inv_def]  Definition
+      
+      ⊢ ∀l i ls.
+          slot_s_inv l i ls ⇔ distinct_keys ls ∧ slot_s_inv_hash l i ls
+   
+   [slot_s_inv_hash_def]  Definition
+      
+      ⊢ ∀l i ls.
+          slot_s_inv_hash l i ls ⇔
+          ∀k v. MEM (k,v) ls ⇒ hash_mod_key k l = i
+   
+   [slot_t_inv_def]  Definition
+      
+      ⊢ ∀l i s. slot_t_inv l i s ⇔ slot_s_inv l i (list_t_v s)
+   
+   [slot_t_lookup_def]  Definition
+      
+      ⊢ ∀key ls. slot_t_lookup key ls = lookup key (list_t_v ls)
+   
+   [slot_t_remove_def]  Definition
+      
+      ⊢ ∀key ls. slot_t_remove key ls = remove key (list_t_v ls)
+   
+   [slots_s_inv_def]  Definition
+      
+      ⊢ ∀s. slots_s_inv s ⇔
+            ∀i. 0 ≤ i ⇒ i < len s ⇒ slot_t_inv (len s) i (index i s)
+   
+   [slots_t_inv_def]  Definition
+      
+      ⊢ ∀s. slots_t_inv s ⇔ slots_s_inv (vec_to_list s)
+   
+   [slots_t_lookup_def]  Definition
+      
+      ⊢ ∀s k.
+          slots_t_lookup s k =
+          (let
+             i = hash_mod_key k (len s);
+             slot = index i s
+           in
+             slot_t_lookup k slot)
+   
+   [EVERY_quant_equiv]  Theorem
+      
+      ⊢ ∀p ls. EVERY p ls ⇔ ∀i. 0 ≤ i ⇒ i < len ls ⇒ p (index i ls)
+   
+   [FLAT_ListNil_is_nil]  Theorem
+      
+      ⊢ EVERY (λx. x = ListNil) ls ⇒ FLAT (MAP list_t_v ls) = []
+   
+   [MEM_EVERY_not]  Theorem
+      
+      ⊢ ∀k v ls. MEM (k,v) ls ⇒ EVERY (λx. k ≠ FST x) ls ⇒ F
+   
+   [MEM_distinct_keys_lookup]  Theorem
+      
+      ⊢ ∀k v ls. MEM (k,v) ls ⇒ distinct_keys ls ⇒ lookup k ls = SOME v
+   
+   [distinct_keys_MEM_not_eq]  Theorem
+      
+      ⊢ ∀ls k1 x1 k2 x2.
+          distinct_keys ((k1,x1)::ls) ⇒ MEM (k2,x2) ls ⇒ k2 ≠ k1
+   
+   [distinct_keys_lookup_NONE]  Theorem
+      
+      ⊢ ∀ls k x. distinct_keys ((k,x)::ls) ⇒ lookup k ls = NONE
+   
+   [every_distinct_remove_every_distinct]  Theorem
+      
+      ⊢ ∀k0 k1 ls0.
+          EVERY (λy. k1 ≠ FST y) ls0 ⇒
+          EVERY (λy. k1 ≠ FST y) (remove k0 ls0)
+   
+   [hash_map_allocate_slots_fwd_spec]  Theorem
+      
+      ⊢ ∀n. usize_to_int n ≤ usize_max ⇒
+            ∃slots.
+              hash_map_allocate_slots_fwd vec_new n = Return slots ∧
+              slots_t_inv slots ∧
+              len (vec_to_list slots) = usize_to_int n ∧
+              EVERY (λx. x = ListNil) (vec_to_list slots)
+   
+   [hash_map_allocate_slots_loop_fwd_spec]  Theorem
+      
+      ⊢ ∀slots n.
+          EVERY (λx. x = ListNil) (vec_to_list slots) ⇒
+          len (vec_to_list slots) + usize_to_int n ≤ usize_max ⇒
+          ∃nslots.
+            hash_map_allocate_slots_loop_fwd slots n = Return nslots ∧
+            len (vec_to_list nslots) =
+            len (vec_to_list slots) + usize_to_int n ∧
+            EVERY (λx. x = ListNil) (vec_to_list nslots)
+   
+   [hash_map_clear_fwd_back_spec]  Theorem
+      
+      ⊢ ∀hm.
+          hash_map_t_inv hm ⇒
+          ∃hm1.
+            hash_map_clear_fwd_back hm = Return hm1 ∧ hash_map_t_inv hm1 ∧
+            len_s hm1 = 0 ∧ ∀k. lookup_s hm1 k = NONE
+   
+   [hash_map_clear_loop_fwd_back_spec]  Theorem
+      
+      ⊢ ∀slots. ∃slots1.
+          hash_map_clear_loop_fwd_back slots (int_to_usize 0) =
+          Return slots1 ∧
+          len (vec_to_list slots1) = len (vec_to_list slots) ∧
+          (∀j. 0 ≤ j ⇒
+               j < len (vec_to_list slots) ⇒
+               index j (vec_to_list slots1) = ListNil) ∧
+          FLAT (MAP list_t_v (vec_to_list slots1)) = []
+   
+   [hash_map_clear_loop_fwd_back_spec_aux]  Theorem
+      
+      ⊢ ∀n slots i.
+          n = len (vec_to_list slots) − usize_to_int i ⇒
+          ∃slots1.
+            hash_map_clear_loop_fwd_back slots i = Return slots1 ∧
+            len (vec_to_list slots1) = len (vec_to_list slots) ∧
+            (∀j. 0 ≤ j ⇒
+                 j < usize_to_int i ⇒
+                 j < len (vec_to_list slots) ⇒
+                 index j (vec_to_list slots1) = index j (vec_to_list slots)) ∧
+            ∀j. usize_to_int i ≤ j ⇒
+                j < len (vec_to_list slots) ⇒
+                index j (vec_to_list slots1) = ListNil
+   
+   [hash_map_cond_incr_thm]  Theorem
+      
+      ⊢ ∀hm key a.
+          hash_map_t_base_inv hm ⇒
+          (slot_t_lookup key (vec_index hm.hash_map_slots a) = NONE ⇒
+           len_s hm < usize_max) ⇒
+          ∃n. (if
+                 slot_t_lookup key (vec_index hm.hash_map_slots a) = NONE
+               then
+                 usize_add hm.hash_map_num_entries (int_to_usize 1)
+               else Return hm.hash_map_num_entries) =
+              Return n ∧
+              if
+                slot_t_lookup key (vec_index hm.hash_map_slots a) = NONE
+              then
+                usize_to_int n = usize_to_int hm.hash_map_num_entries + 1
+              else n = hm.hash_map_num_entries
+   
+   [hash_map_contains_key_fwd_spec]  Theorem
+      
+      ⊢ ∀hm key.
+          hash_map_t_inv hm ⇒
+          hash_map_contains_key_fwd hm key =
+          Return (lookup_s hm key ≠ NONE)
+   
+   [hash_map_contains_key_in_list_fwd_spec]  Theorem
+      
+      ⊢ ∀key ls.
+          hash_map_contains_key_in_list_fwd key ls =
+          Return (slot_t_lookup key ls ≠ NONE)
+   
+   [hash_map_get_fwd_spec]  Theorem
+      
+      ⊢ ∀hm key.
+          hash_map_t_inv hm ⇒
+          case hash_map_get_fwd hm key of
+            Return x => lookup_s hm key = SOME x
+          | Fail v1 => lookup_s hm key = NONE
+          | Diverge => F
+   
+   [hash_map_get_in_list_fwd_spec]  Theorem
+      
+      ⊢ ∀ls key.
+          case hash_map_get_in_list_fwd key ls of
+            Return x => slot_t_lookup key ls = SOME x
+          | Fail v1 => slot_t_lookup key ls = NONE
+          | Diverge => F
+   
+   [hash_map_get_mut_back_spec]  Theorem
+      
+      ⊢ ∀hm key nx.
+          lookup_s hm key ≠ NONE ⇒
+          hash_map_t_inv hm ⇒
+          ∃hm1.
+            hash_map_get_mut_back hm key nx = Return hm1 ∧
+            lookup_s hm1 key = SOME nx ∧
+            ∀k. k ≠ key ⇒ lookup_s hm1 k = lookup_s hm k
+   
+   [hash_map_get_mut_fwd_spec]  Theorem
+      
+      ⊢ ∀hm key.
+          hash_map_t_inv hm ⇒
+          case hash_map_get_mut_fwd hm key of
+            Return x => lookup_s hm key = SOME x
+          | Fail v1 => lookup_s hm key = NONE
+          | Diverge => F
+   
+   [hash_map_get_mut_in_list_back_spec]  Theorem
+      
+      ⊢ ∀ls key nx.
+          slot_t_lookup key ls ≠ NONE ⇒
+          ∃nls.
+            hash_map_get_mut_in_list_back ls key nx = Return nls ∧
+            slot_t_lookup key nls = SOME nx ∧
+            ∀k. k ≠ key ⇒ slot_t_lookup k nls = slot_t_lookup k ls
+   
+   [hash_map_get_mut_in_list_fwd_spec]  Theorem
+      
+      ⊢ ∀ls key.
+          case hash_map_get_mut_in_list_fwd ls key of
+            Return x => slot_t_lookup key ls = SOME x
+          | Fail v1 => slot_t_lookup key ls = NONE
+          | Diverge => F
+   
+   [hash_map_insert_fwd_back_spec]  Theorem
+      
+      [oracles: DISK_THM] [axioms: usize_u32_bounds] []
+      ⊢ ∀hm key value.
+          hash_map_t_inv hm ⇒
+          (lookup_s hm key = NONE ⇒ len_s hm < usize_max) ⇒
+          ∃hm1.
+            hash_map_insert_fwd_back hm key value = Return hm1 ∧
+            hash_map_t_inv hm1 ∧ lookup_s hm1 key = SOME value ∧
+            (∀k. k ≠ key ⇒ lookup_s hm k = lookup_s hm1 k) ∧
+            case lookup_s hm key of
+              NONE => len_s hm1 = len_s hm + 1
+            | SOME v => len_s hm1 = len_s hm
+   
+   [hash_map_insert_in_list_fwd_spec]  Theorem
+      
+      ⊢ ∀ls key value. ∃b.
+          hash_map_insert_in_list_fwd key value ls = Return b ∧
+          (b ⇔ slot_t_lookup key ls = NONE)
+   
+   [hash_map_insert_in_list_loop_back_EVERY_distinct_keys]  Theorem
+      
+      ⊢ ∀k v k1 ls0 ls1.
+          k1 ≠ k ⇒
+          EVERY (λy. k1 ≠ FST y) (list_t_v ls0) ⇒
+          pairwise_rel (λx y. FST x ≠ FST y) (list_t_v ls0) ⇒
+          hash_map_insert_in_list_loop_back k v ls0 = Return ls1 ⇒
+          EVERY (λy. k1 ≠ FST y) (list_t_v ls1)
+   
+   [hash_map_insert_in_list_loop_back_distinct_keys]  Theorem
+      
+      ⊢ ∀k v ls0 ls1.
+          distinct_keys (list_t_v ls0) ⇒
+          hash_map_insert_in_list_loop_back k v ls0 = Return ls1 ⇒
+          distinct_keys (list_t_v ls1)
+   
+   [hash_map_insert_in_list_loop_back_spec]  Theorem
+      
+      ⊢ ∀i ls key value.
+          distinct_keys (list_t_v ls) ⇒
+          ∃ls1.
+            hash_map_insert_in_list_loop_back key value ls = Return ls1 ∧
+            ∀l. slot_s_inv_hash l (hash_mod_key key l) (list_t_v ls) ⇒
+                insert_in_slot_t_rel l key value ls ls1
+   
+   [hash_map_insert_in_list_loop_back_spec_aux]  Theorem
+      
+      ⊢ ∀ls key value. ∃ls1.
+          hash_map_insert_in_list_loop_back key value ls = Return ls1 ∧
+          slot_t_lookup key ls1 = SOME value ∧
+          (∀k. k ≠ key ⇒ slot_t_lookup k ls = slot_t_lookup k ls1) ∧
+          (∀l. slot_s_inv_hash l (hash_mod_key key l) (list_t_v ls) ⇒
+               slot_s_inv_hash l (hash_mod_key key l) (list_t_v ls1)) ∧
+          case slot_t_lookup key ls of
+            NONE => len (list_t_v ls1) = len (list_t_v ls) + 1
+          | SOME v => len (list_t_v ls1) = len (list_t_v ls)
+   
+   [hash_map_insert_in_list_loop_fwd_spec]  Theorem
+      
+      ⊢ ∀ls key value. ∃b.
+          hash_map_insert_in_list_loop_fwd key value ls = Return b ∧
+          (b ⇔ slot_t_lookup key ls = NONE)
+   
+   [hash_map_insert_no_resize_fwd_back_branches_eq]  Theorem
+      
+      ⊢ (if slot_t_lookup key (vec_index hm.hash_map_slots a) = NONE then
+           do
+             i0 <- usize_add hm.hash_map_num_entries (int_to_usize 1);
+             l0 <-
+               hash_map_insert_in_list_back key value
+                 (vec_index hm.hash_map_slots a);
+             v <- vec_index_mut_back hm.hash_map_slots a l0;
+             Return
+               (hm with <|hash_map_num_entries := i0; hash_map_slots := v|>)
+           od
+         else
+           do
+             l0 <-
+               hash_map_insert_in_list_back key value
+                 (vec_index hm.hash_map_slots a);
+             v <- vec_index_mut_back hm.hash_map_slots a l0;
+             Return (hm with hash_map_slots := v)
+           od) =
+        do
+          i0 <-
+            if
+              slot_t_lookup key (vec_index hm.hash_map_slots a) = NONE
+            then
+              usize_add hm.hash_map_num_entries (int_to_usize 1)
+            else Return hm.hash_map_num_entries;
+          l0 <-
+            hash_map_insert_in_list_back key value
+              (vec_index hm.hash_map_slots a);
+          v <- vec_index_mut_back hm.hash_map_slots a l0;
+          Return
+            (hm with <|hash_map_num_entries := i0; hash_map_slots := v|>)
+        od
+   
+   [hash_map_insert_no_resize_fwd_back_spec]  Theorem
+      
+      ⊢ ∀hm key value.
+          hash_map_t_base_inv hm ⇒
+          (lookup_s hm key = NONE ⇒ len_s hm < usize_max) ⇒
+          ∃hm1.
+            hash_map_insert_no_resize_fwd_back hm key value = Return hm1 ∧
+            hash_map_t_base_inv hm1 ∧ lookup_s hm1 key = SOME value ∧
+            (∀k. k ≠ key ⇒ lookup_s hm k = lookup_s hm1 k) ∧
+            (case lookup_s hm key of
+               NONE => len_s hm1 = len_s hm + 1
+             | SOME v => len_s hm1 = len_s hm) ∧
+            hash_map_same_params hm hm1
+   
+   [hash_map_insert_no_resize_fwd_back_spec_aux]  Theorem
+      
+      ⊢ ∀hm key value.
+          hash_map_t_base_inv hm ⇒
+          (lookup_s hm key = NONE ⇒ len_s hm < usize_max) ⇒
+          ∃hm1 slot1.
+            hash_map_insert_no_resize_fwd_back hm key value = Return hm1 ∧
+            len (vec_to_list hm1.hash_map_slots) =
+            len (vec_to_list hm.hash_map_slots) ∧
+            (let
+               l = len (vec_to_list hm.hash_map_slots);
+               i = hash_mod_key key (len (vec_to_list hm.hash_map_slots));
+               slot = index i (vec_to_list hm.hash_map_slots)
+             in
+               insert_in_slot_t_rel l key value slot slot1 ∧
+               vec_to_list hm1.hash_map_slots =
+               update (vec_to_list hm.hash_map_slots) i slot1 ∧
+               hm1.hash_map_max_load_factor = hm.hash_map_max_load_factor ∧
+               hm1.hash_map_max_load = hm.hash_map_max_load ∧
+               (case lookup_s hm key of
+                  NONE =>
+                    usize_to_int hm1.hash_map_num_entries =
+                    usize_to_int hm.hash_map_num_entries + 1
+                | SOME v =>
+                  hm1.hash_map_num_entries = hm.hash_map_num_entries) ∧
+               hash_map_same_params hm hm1)
+   
+   [hash_map_len_spec]  Theorem
+      
+      ⊢ ∀hm.
+          hash_map_t_base_inv hm ⇒
+          ∃x. hash_map_len_fwd hm = Return x ∧ usize_to_int x = len_s hm
+   
+   [hash_map_move_elements_from_list_fwd_back_spec]  Theorem
+      
+      ⊢ ∀hm ls.
+          (let
+             l = len (list_t_v ls)
+           in
+             hash_map_t_base_inv hm ⇒
+             len_s hm + l ≤ usize_max ⇒
+             ∃hm1.
+               hash_map_move_elements_from_list_fwd_back hm ls = Return hm1 ∧
+               hash_map_t_base_inv hm1 ∧
+               ((∀k v. MEM (k,v) (list_t_v ls) ⇒ lookup_s hm k = NONE) ⇒
+                distinct_keys (list_t_v ls) ⇒
+                (∀k. slot_t_lookup k ls = NONE ⇒
+                     lookup_s hm1 k = lookup_s hm k) ∧
+                (∀k. slot_t_lookup k ls ≠ NONE ⇒
+                     lookup_s hm1 k = slot_t_lookup k ls) ∧
+                len_s hm1 = len_s hm + l) ∧ hash_map_same_params hm hm1)
+   
+   [hash_map_move_elements_fwd_back_spec]  Theorem
+      
+      ⊢ ∀hm slots i.
+          (let
+             slots_l =
+               len
+                 (FLAT
+                    (MAP list_t_v
+                       (drop (usize_to_int i) (vec_to_list slots))))
+           in
+             hash_map_t_base_inv hm ⇒
+             len_s hm + slots_l ≤ usize_max ⇒
+             (∀j. (let
+                     l = len (vec_to_list slots)
+                   in
+                     usize_to_int i ≤ j ⇒
+                     j < l ⇒
+                     (let
+                        slot = index j (vec_to_list slots)
+                      in
+                        slot_t_inv l j slot ∧
+                        ∀k v.
+                          MEM (k,v) (list_t_v slot) ⇒ lookup_s hm k = NONE))) ⇒
+             ∃hm1 slots1.
+               hash_map_move_elements_fwd_back hm slots i =
+               Return (hm1,slots1) ∧ hash_map_t_base_inv hm1 ∧
+               len_s hm1 = len_s hm + slots_l ∧
+               (∀k. lookup_s hm1 k =
+                    case lookup_s hm k of
+                      NONE =>
+                        (let
+                           j = hash_mod_key k (len (vec_to_list slots))
+                         in
+                           if
+                             usize_to_int i ≤ j ∧
+                             j < len (vec_to_list slots)
+                           then
+                             (let
+                                slot = index j (vec_to_list slots)
+                              in
+                                lookup k (list_t_v slot))
+                           else NONE)
+                    | SOME v => SOME v) ∧ hash_map_same_params hm hm1)
+   
+   [hash_map_move_elements_loop_fwd_back_spec_aux]  Theorem
+      
+      ⊢ ∀hm slots i n.
+          (let
+             slots_l =
+               len
+                 (FLAT
+                    (MAP list_t_v
+                       (drop (usize_to_int i) (vec_to_list slots))))
+           in
+             n = len (vec_to_list slots) − usize_to_int i ⇒
+             hash_map_t_base_inv hm ⇒
+             len_s hm + slots_l ≤ usize_max ⇒
+             (∀j. (let
+                     l = len (vec_to_list slots)
+                   in
+                     usize_to_int i ≤ j ⇒
+                     j < l ⇒
+                     (let
+                        slot = index j (vec_to_list slots)
+                      in
+                        slot_t_inv l j slot ∧
+                        ∀k v.
+                          MEM (k,v) (list_t_v slot) ⇒ lookup_s hm k = NONE))) ⇒
+             ∃hm1 slots1.
+               hash_map_move_elements_loop_fwd_back hm slots i =
+               Return (hm1,slots1) ∧ hash_map_t_base_inv hm1 ∧
+               len_s hm1 = len_s hm + slots_l ∧
+               (∀k. lookup_s hm1 k =
+                    case lookup_s hm k of
+                      NONE =>
+                        (let
+                           j = hash_mod_key k (len (vec_to_list slots))
+                         in
+                           if
+                             usize_to_int i ≤ j ∧
+                             j < len (vec_to_list slots)
+                           then
+                             (let
+                                slot = index j (vec_to_list slots)
+                              in
+                                lookup k (list_t_v slot))
+                           else NONE)
+                    | SOME v => SOME v) ∧ hash_map_same_params hm hm1)
+   
+   [hash_map_new_fwd_spec]  Theorem
+      
+      ⊢ ∃hm.
+          hash_map_new_fwd = Return hm ∧ hash_map_t_inv hm ∧
+          ∀k. lookup_s hm k = NONE ∧ len_s hm = 0
+   
+   [hash_map_new_with_capacity_fwd_spec]  Theorem
+      
+      ⊢ ∀capacity max_load_dividend max_load_divisor.
+          0 < usize_to_int max_load_dividend ⇒
+          usize_to_int max_load_dividend < usize_to_int max_load_divisor ⇒
+          0 < usize_to_int capacity ⇒
+          usize_to_int capacity * usize_to_int max_load_dividend ≥
+          usize_to_int max_load_divisor ⇒
+          usize_to_int capacity * usize_to_int max_load_dividend ≤
+          usize_max ⇒
+          ∃hm.
+            hash_map_new_with_capacity_fwd capacity max_load_dividend
+              max_load_divisor =
+            Return hm ∧ hash_map_t_inv hm ∧ len_s hm = 0 ∧
+            ∀k. lookup_s hm k = NONE ∧
+                len (vec_to_list hm.hash_map_slots) = usize_to_int capacity ∧
+                hm.hash_map_max_load_factor =
+                (max_load_dividend,max_load_divisor)
+   
+   [hash_map_remove_back_branch_eq]  Theorem
+      
+      ⊢ ∀key hm a.
+          (case lookup key (list_t_v (vec_index hm.hash_map_slots a)) of
+             NONE =>
+               do
+                 l0 <-
+                   hash_map_remove_from_list_back key
+                     (vec_index hm.hash_map_slots a);
+                 v <- vec_index_mut_back hm.hash_map_slots a l0;
+                 Return (hm with hash_map_slots := v)
+               od
+           | SOME x0 =>
+             do
+               i0 <- usize_sub hm.hash_map_num_entries (int_to_usize 1);
+               l0 <-
+                 hash_map_remove_from_list_back key
+                   (vec_index hm.hash_map_slots a);
+               v <- vec_index_mut_back hm.hash_map_slots a l0;
+               Return
+                 (hm with
+                  <|hash_map_num_entries := i0; hash_map_slots := v|>)
+             od) =
+          do
+            i0 <-
+              case lookup key (list_t_v (vec_index hm.hash_map_slots a)) of
+                NONE => Return hm.hash_map_num_entries
+              | SOME v =>
+                usize_sub hm.hash_map_num_entries (int_to_usize 1);
+            l0 <-
+              hash_map_remove_from_list_back key
+                (vec_index hm.hash_map_slots a);
+            v <- vec_index_mut_back hm.hash_map_slots a l0;
+            Return
+              (hm with <|hash_map_num_entries := i0; hash_map_slots := v|>)
+          od
+   
+   [hash_map_remove_back_spec]  Theorem
+      
+      ⊢ ∀hm key.
+          hash_map_t_inv hm ⇒
+          ∃hm1.
+            hash_map_remove_back hm key = Return hm1 ∧ hash_map_t_inv hm1 ∧
+            lookup_s hm1 key = NONE ∧
+            (∀k. k ≠ key ⇒ lookup_s hm1 k = lookup_s hm k) ∧
+            case lookup_s hm key of
+              NONE => len_s hm1 = len_s hm
+            | SOME v => len_s hm1 = len_s hm − 1
+   
+   [hash_map_remove_from_list_back_spec]  Theorem
+      
+      ⊢ ∀key ls. ∃ls1.
+          hash_map_remove_from_list_back key ls = Return ls1 ∧
+          ∀l i.
+            slot_t_inv l i ls ⇒
+            slot_t_inv l i ls1 ∧ list_t_v ls1 = remove key (list_t_v ls) ∧
+            slot_t_lookup key ls1 = NONE ∧
+            (∀k. k ≠ key ⇒ slot_t_lookup k ls1 = slot_t_lookup k ls) ∧
+            case slot_t_lookup key ls of
+              NONE => len (list_t_v ls1) = len (list_t_v ls)
+            | SOME v => len (list_t_v ls1) = len (list_t_v ls) − 1
+   
+   [hash_map_remove_from_list_fwd_spec]  Theorem
+      
+      ⊢ ∀key l i ls.
+          hash_map_remove_from_list_fwd key ls =
+          Return (slot_t_lookup key ls)
+   
+   [hash_map_remove_fwd_spec]  Theorem
+      
+      ⊢ ∀hm key.
+          hash_map_t_inv hm ⇒
+          hash_map_remove_fwd hm key = Return (lookup_s hm key)
+   
+   [hash_map_same_params_refl]  Theorem
+      
+      ⊢ ∀hm. hash_map_same_params hm hm
+   
+   [hash_map_t_base_inv_len_slots]  Theorem
+      
+      ⊢ ∀hm.
+          hash_map_t_base_inv hm ⇒ 0 < len (vec_to_list hm.hash_map_slots)
+   
+   [hash_map_try_resize_fwd_back_spec]  Theorem
+      
+      [oracles: DISK_THM] [axioms: usize_u32_bounds] []
+      ⊢ ∀hm.
+          hash_map_t_base_inv hm ⇒
+          hash_map_just_before_resize_pred hm ⇒
+          ∃hm1.
+            hash_map_try_resize_fwd_back hm = Return hm1 ∧
+            hash_map_t_inv hm1 ∧ len_s hm1 = len_s hm ∧
+            ∀k. lookup_s hm1 k = lookup_s hm k
+   
+   [key_MEM_j_lookup_i_is_NONE]  Theorem
+      
+      ⊢ ∀i j slots k v.
+          usize_to_int i < j ⇒
+          j < len (vec_to_list slots) ⇒
+          (∀j. usize_to_int i ≤ j ⇒
+               j < len (vec_to_list slots) ⇒
+               slot_t_inv (len (vec_to_list slots)) j
+                 (index j (vec_to_list slots))) ⇒
+          MEM (k,v) (list_t_v (index j (vec_to_list slots))) ⇒
+          slot_t_lookup k (index (usize_to_int i) (vec_to_list slots)) =
+          NONE
+   
+   [len_FLAT_MAP_update]  Theorem
+      
+      ⊢ ∀x ls i.
+          0 ≤ i ⇒
+          i < len ls ⇒
+          len (FLAT (MAP list_t_v (update ls i x))) =
+          len (FLAT (MAP list_t_v ls)) + len (list_t_v x) −
+          len (list_t_v (index i ls))
+   
+   [len_index_FLAT_MAP_list_t_v]  Theorem
+      
+      ⊢ ∀slots i.
+          0 ≤ i ⇒
+          i < len slots ⇒
+          len (list_t_v (index i slots)) ≤
+          len (FLAT (MAP list_t_v (drop i slots)))
+   
+   [len_vec_FLAT_drop_update]  Theorem
+      
+      ⊢ ∀slots i.
+          0 ≤ i ⇒
+          i < len slots ⇒
+          len (FLAT (MAP list_t_v (drop i slots))) =
+          len (list_t_v (index i slots)) +
+          len (FLAT (MAP list_t_v (drop (i + 1) (update slots i ListNil))))
+   
+   [lookup_SOME_not_empty]  Theorem
+      
+      ⊢ ∀ls k. lookup k ls ≠ NONE ⇒ 0 < len ls
+   
+   [lookup_cond_decr_entries_eq]  Theorem
+      
+      ⊢ ∀hm key i.
+          hash_map_t_inv hm ⇒
+          usize_to_int i < len (vec_to_list hm.hash_map_slots) ⇒
+          ∃j. (case
+                 lookup key (list_t_v (vec_index hm.hash_map_slots i))
+               of
+                 NONE => Return hm.hash_map_num_entries
+               | SOME v =>
+                 usize_sub hm.hash_map_num_entries (int_to_usize 1)) =
+              Return j ∧
+              (lookup key (list_t_v (vec_index hm.hash_map_slots i)) = NONE ⇒
+               j = hm.hash_map_num_entries) ∧
+              (lookup key (list_t_v (vec_index hm.hash_map_slots i)) ≠ NONE ⇒
+               usize_to_int j + 1 = usize_to_int hm.hash_map_num_entries)
+   
+   [lookup_def]  Theorem
+      
+      ⊢ (∀key. lookup key [] = NONE) ∧
+        ∀v ls key k.
+          lookup key ((k,v)::ls) =
+          if k = key then SOME v else lookup key ls
+   
+   [lookup_distinct_keys_MEM]  Theorem
+      
+      ⊢ ∀k v ls. lookup k ls = SOME v ⇒ distinct_keys ls ⇒ MEM (k,v) ls
+   
+   [lookup_ind]  Theorem
+      
+      ⊢ ∀P. (∀key. P key []) ∧
+            (∀key k v ls. (k ≠ key ⇒ P key ls) ⇒ P key ((k,v)::ls)) ⇒
+            ∀v v1. P v v1
+   
+   [lookup_s_SOME_not_empty]  Theorem
+      
+      ⊢ ∀hm key. hash_map_t_inv hm ⇒ lookup_s hm key ≠ NONE ⇒ 0 < len_s hm
+   
+   [pairwise_rel_quant_equiv]  Theorem
+      
+      ⊢ ∀p ls.
+          pairwise_rel p ls ⇔
+          ∀i j. 0 ≤ i ⇒ i < j ⇒ j < len ls ⇒ p (index i ls) (index j ls)
+   
+   [remove_def]  Theorem
+      
+      ⊢ (∀key. remove key [] = []) ∧
+        ∀v ls key k.
+          remove key ((k,v)::ls) =
+          if k = key then ls else (k,v)::remove key ls
+   
+   [remove_ind]  Theorem
+      
+      ⊢ ∀P. (∀key. P key []) ∧
+            (∀key k v ls. (k ≠ key ⇒ P key ls) ⇒ P key ((k,v)::ls)) ⇒
+            ∀v v1. P v v1
+   
+   [slot_t_lookup_SOME_not_empty]  Theorem
+      
+      ⊢ ∀ls i k.
+          0 ≤ i ⇒
+          i < len ls ⇒
+          slot_t_lookup k (index i ls) ≠ NONE ⇒
+          0 < len (FLAT (MAP list_t_v ls))
+   
+   
+*)
+end
-- 
cgit v1.2.3