Definition compat_pointer_chunks (chunk1 chunk2: memory_chunk) : Prop :=
  match chunk1, chunk2 with
  | (Mint32 | Many32), (Mint32 | Many32) => True
  | (Mint64 | Many64), (Mint64 | Many64) => True
  | _, _ => False
  end.

(* Specifications: *)
Lemma compat_pointer_chunks_true: forall chunk1 chunk2, (chunk1 = Mint32 \/ chunk1 = Many32 \/ chunk1 = Mint64 \/ chunk1 = Many64) -> (chunk2 = Mint32 \/ chunk2 = Many32 \/ chunk2 = Mint64 \/ chunk2 = Many64) -> quantity_chunk chunk1 = quantity_chunk chunk2 -> compat_pointer_chunks chunk1 chunk2.
Theorem load_pointer_store: forall chunk m1 b ofs v m2 chunk' b' ofs' v_b v_o, store chunk m1 b ofs v = Some m2 -> load chunk' m2 b' ofs' = Some(Vptr v_b v_o) -> (v = Vptr v_b v_o /\ compat_pointer_chunks chunk chunk' /\ b' = b /\ ofs' = ofs) \/ (b' <> b \/ ofs' + size_chunk chunk' <= ofs \/ ofs + size_chunk chunk <= ofs').
Theorem load_store_pointer_overlap: forall chunk m1 b ofs v_b v_o m2 chunk' ofs' v, store chunk m1 b ofs (Vptr v_b v_o) = Some m2 -> load chunk' m2 b ofs' = Some v -> ofs' <> ofs -> ofs' + size_chunk chunk' > ofs -> ofs + size_chunk chunk > ofs' -> v = Vundef.
Theorem load_store_pointer_mismatch: forall chunk m1 b ofs v_b v_o m2 chunk' v, store chunk m1 b ofs (Vptr v_b v_o) = Some m2 -> load chunk' m2 b ofs = Some v -> ~compat_pointer_chunks chunk chunk' -> v = Vundef.
Lemma store_similar_chunks: forall chunk1 chunk2 v1 v2 m b ofs, encode_val chunk1 v1 = encode_val chunk2 v2 -> align_chunk chunk1 = align_chunk chunk2 -> store chunk1 m b ofs v1 = store chunk2 m b ofs v2.
Theorem store_signed_unsigned_8: forall m b ofs v, store Mint8signed m b ofs v = store Mint8unsigned m b ofs v.
Theorem store_signed_unsigned_16: forall m b ofs v, store Mint16signed m b ofs v = store Mint16unsigned m b ofs v.
Theorem store_int8_zero_ext: forall m b ofs n, store Mint8unsigned m b ofs (Vint (Int.zero_ext 8 n)) = store Mint8unsigned m b ofs (Vint n).
Theorem store_int8_sign_ext: forall m b ofs n, store Mint8signed m b ofs (Vint (Int.sign_ext 8 n)) = store Mint8signed m b ofs (Vint n).
Theorem store_int16_zero_ext: forall m b ofs n, store Mint16unsigned m b ofs (Vint (Int.zero_ext 16 n)) = store Mint16unsigned m b ofs (Vint n).
Theorem store_int16_sign_ext: forall m b ofs n, store Mint16signed m b ofs (Vint (Int.sign_ext 16 n)) = store Mint16signed m b ofs (Vint n).
Theorem range_perm_storebytes: forall m1 b ofs bytes, range_perm m1 b ofs (ofs + Z.of_nat (length bytes)) Cur Writable -> { m2 : mem | storebytes m1 b ofs bytes = Some m2 }.
Theorem store_storebytes: forall m1 b ofs chunk v m2, store chunk m1 b ofs v = Some m2 -> storebytes m1 b ofs (encode_val chunk v) = Some m2.
Lemma storebytes_access: mem_access m2 = mem_access m1.
Theorem perm_storebytes_1: forall b' ofs' k p, perm m1 b' ofs' k p -> perm m2 b' ofs' k p.
Theorem perm_storebytes_2: forall b' ofs' k p, perm m2 b' ofs' k p -> perm m1 b' ofs' k p.
Theorem storebytes_valid_access_1: forall chunk' b' ofs' p, valid_access m1 chunk' b' ofs' p -> valid_access m2 chunk' b' ofs' p.
Theorem storebytes_valid_access_2: forall chunk' b' ofs' p, valid_access m2 chunk' b' ofs' p -> valid_access m1 chunk' b' ofs' p.
Theorem nextblock_storebytes: nextblock m2 = nextblock m1.
Theorem storebytes_valid_block_1: forall b', valid_block m1 b' -> valid_block m2 b'.
Theorem storebytes_valid_block_2: forall b', valid_block m2 b' -> valid_block m1 b'.
Theorem storebytes_range_perm: range_perm m1 b ofs (ofs + Z.of_nat (length bytes)) Cur Writable.
Theorem loadbytes_storebytes_same: loadbytes m2 b ofs (Z.of_nat (length bytes)) = Some bytes.
Theorem loadbytes_storebytes_disjoint: forall b' ofs' len, len >= 0 -> b' <> b \/ Intv.disjoint (ofs', ofs' + len) (ofs, ofs + Z.of_nat (length bytes)) -> loadbytes m2 b' ofs' len = loadbytes m1 b' ofs' len.
Theorem loadbytes_storebytes_other: forall b' ofs' len, len >= 0 -> b' <> b \/ ofs' + len <= ofs \/ ofs + Z.of_nat (length bytes) <= ofs' -> loadbytes m2 b' ofs' len = loadbytes m1 b' ofs' len.
Theorem load_storebytes_other: forall chunk b' ofs', b' <> b \/ ofs' + size_chunk chunk <= ofs \/ ofs + Z.of_nat (length bytes) <= ofs' -> load chunk m2 b' ofs' = load chunk m1 b' ofs'.
Lemma setN_concat: forall bytes1 bytes2 ofs c, setN (bytes1 ++ bytes2) ofs c = setN bytes2 (ofs + Z.of_nat (length bytes1)) (setN bytes1 ofs c).
Theorem storebytes_concat: forall m b ofs bytes1 m1 bytes2 m2, storebytes m b ofs bytes1 = Some m1 -> storebytes m1 b (ofs + Z.of_nat(length bytes1)) bytes2 = Some m2 -> storebytes m b ofs (bytes1 ++ bytes2) = Some m2.
Theorem storebytes_split: forall m b ofs bytes1 bytes2 m2, storebytes m b ofs (bytes1 ++ bytes2) = Some m2 -> exists m1, storebytes m b ofs bytes1 = Some m1 /\ storebytes m1 b (ofs + Z.of_nat(length bytes1)) bytes2 = Some m2.
Theorem store_int64_split: forall m b ofs v m', store Mint64 m b ofs v = Some m' -> Archi.ptr64 = false -> exists m1, store Mint32 m b ofs (if Archi.big_endian then Val.hiword v else Val.loword v) = Some m1 /\ store Mint32 m1 b (ofs + 4) (if Archi.big_endian then Val.loword v else Val.hiword v) = Some m'.
Theorem storev_int64_split: forall m a v m', storev Mint64 m a v = Some m' -> Archi.ptr64 = false -> exists m1, storev Mint32 m a (if Archi.big_endian then Val.hiword v else Val.loword v) = Some m1 /\ storev Mint32 m1 (Val.add a (Vint (Int.repr 4))) (if Archi.big_endian then Val.loword v else Val.hiword v) = Some m'.
Theorem nextblock_alloc: nextblock m2 = Pos.succ (nextblock m1).
Theorem alloc_result: b = nextblock m1.
Theorem valid_block_alloc: forall b', valid_block m1 b' -> valid_block m2 b'.
Theorem fresh_block_alloc: ~(valid_block m1 b).
Theorem valid_new_block: valid_block m2 b.
Theorem valid_block_alloc_inv: forall b', valid_block m2 b' -> b' = b \/ valid_block m1 b'.
Theorem perm_alloc_1: forall b' ofs k p, perm m1 b' ofs k p -> perm m2 b' ofs k p.
Theorem perm_alloc_2: forall ofs k, lo <= ofs < hi -> perm m2 b ofs k Freeable.
Theorem perm_alloc_inv: forall b' ofs k p, perm m2 b' ofs k p -> if eq_block b' b then lo <= ofs < hi else perm m1 b' ofs k p.
Theorem perm_alloc_3: forall ofs k p, perm m2 b ofs k p -> lo <= ofs < hi.
Theorem perm_alloc_4: forall b' ofs k p, perm m2 b' ofs k p -> b' <> b -> perm m1 b' ofs k p.
Theorem valid_access_alloc_other: forall chunk b' ofs p, valid_access m1 chunk b' ofs p -> valid_access m2 chunk b' ofs p.
Theorem load_alloc_unchanged: forall chunk b' ofs, valid_block m1 b' -> load chunk m2 b' ofs = load chunk m1 b' ofs.
Theorem load_alloc_other: forall chunk b' ofs v, load chunk m1 b' ofs = Some v -> load chunk m2 b' ofs = Some v.
Theorem load_alloc_same: forall chunk ofs v, load chunk m2 b ofs = Some v -> v = Vundef.
Theorem loadbytes_alloc_unchanged: forall b' ofs n, valid_block m1 b' -> loadbytes m2 b' ofs n = loadbytes m1 b' ofs n.
Theorem loadbytes_alloc_same: forall n ofs bytes byte, loadbytes m2 b ofs n = Some bytes -> In byte bytes -> byte = Undef.
Theorem range_perm_free: forall m1 b lo hi, range_perm m1 b lo hi Cur Freeable -> { m2: mem | free m1 b lo hi = Some m2 }.
Theorem free_range_perm: range_perm m1 bf lo hi Cur Freeable.
Lemma free_result: m2 = unchecked_free m1 bf lo hi.
Theorem nextblock_free: nextblock m2 = nextblock m1.
Theorem valid_block_free_1: forall b, valid_block m1 b -> valid_block m2 b.
Theorem valid_block_free_2: forall b, valid_block m2 b -> valid_block m1 b.
Theorem perm_free_1: forall b ofs k p, b <> bf \/ ofs < lo \/ hi <= ofs -> perm m1 b ofs k p -> perm m2 b ofs k p.
Theorem perm_free_2: forall ofs k p, lo <= ofs < hi -> ~ perm m2 bf ofs k p.
Theorem perm_free_3: forall b ofs k p, perm m2 b ofs k p -> perm m1 b ofs k p.
Theorem perm_free_inv: forall b ofs k p, perm m1 b ofs k p -> (b = bf /\ lo <= ofs < hi) \/ perm m2 b ofs k p.
Theorem valid_access_free_1: forall chunk b ofs p, valid_access m1 chunk b ofs p -> b <> bf \/ lo >= hi \/ ofs + size_chunk chunk <= lo \/ hi <= ofs -> valid_access m2 chunk b ofs p.
Theorem valid_access_free_2: forall chunk ofs p, lo < hi -> ofs + size_chunk chunk > lo -> ofs < hi -> ~(valid_access m2 chunk bf ofs p).
Theorem valid_access_free_inv_1: forall chunk b ofs p, valid_access m2 chunk b ofs p -> valid_access m1 chunk b ofs p.
Theorem valid_access_free_inv_2: forall chunk ofs p, valid_access m2 chunk bf ofs p -> lo >= hi \/ ofs + size_chunk chunk <= lo \/ hi <= ofs.
Theorem load_free: forall chunk b ofs, b <> bf \/ lo >= hi \/ ofs + size_chunk chunk <= lo \/ hi <= ofs -> load chunk m2 b ofs = load chunk m1 b ofs.
Theorem load_free_2: forall chunk b ofs v, load chunk m2 b ofs = Some v -> load chunk m1 b ofs = Some v.
Theorem loadbytes_free: forall b ofs n, b <> bf \/ lo >= hi \/ ofs + n <= lo \/ hi <= ofs -> loadbytes m2 b ofs n = loadbytes m1 b ofs n.
Theorem loadbytes_free_2: forall b ofs n bytes, loadbytes m2 b ofs n = Some bytes -> loadbytes m1 b ofs n = Some bytes.
Theorem range_perm_drop_1: forall m b lo hi p m', drop_perm m b lo hi p = Some m' -> range_perm m b lo hi Cur Freeable.
Theorem range_perm_drop_2: forall m b lo hi p, range_perm m b lo hi Cur Freeable -> {m' | drop_perm m b lo hi p = Some m' }.
Theorem nextblock_drop: nextblock m' = nextblock m.
Theorem drop_perm_valid_block_1: forall b', valid_block m b' -> valid_block m' b'.
Theorem drop_perm_valid_block_2: forall b', valid_block m' b' -> valid_block m b'.
Theorem perm_drop_1: forall ofs k, lo <= ofs < hi -> perm m' b ofs k p.
Theorem perm_drop_2: forall ofs k p', lo <= ofs < hi -> perm m' b ofs k p' -> perm_order p p'.
Theorem perm_drop_3: forall b' ofs k p', b' <> b \/ ofs < lo \/ hi <= ofs -> perm m b' ofs k p' -> perm m' b' ofs k p'.
Theorem perm_drop_4: forall b' ofs k p', perm m' b' ofs k p' -> perm m b' ofs k p'.
Lemma valid_access_drop_1: forall chunk b' ofs p', b' <> b \/ ofs + size_chunk chunk <= lo \/ hi <= ofs \/ perm_order p p' -> valid_access m chunk b' ofs p' -> valid_access m' chunk b' ofs p'.
Lemma valid_access_drop_2: forall chunk b' ofs p', valid_access m' chunk b' ofs p' -> valid_access m chunk b' ofs p'.
Theorem load_drop: forall chunk b' ofs, b' <> b \/ ofs + size_chunk chunk <= lo \/ hi <= ofs \/ perm_order p Readable -> load chunk m' b' ofs = load chunk m b' ofs.
Theorem loadbytes_drop: forall b' ofs n, b' <> b \/ ofs + n <= lo \/ hi <= ofs \/ perm_order p Readable -> loadbytes m' b' ofs n = loadbytes m b' ofs n.
Lemma perm_inj: forall f m1 m2 b1 ofs k p b2 delta, mem_inj f m1 m2 -> perm m1 b1 ofs k p -> f b1 = Some(b2, delta) -> perm m2 b2 (ofs + delta) k p.
Lemma range_perm_inj: forall f m1 m2 b1 lo hi k p b2 delta, mem_inj f m1 m2 -> range_perm m1 b1 lo hi k p -> f b1 = Some(b2, delta) -> range_perm m2 b2 (lo + delta) (hi + delta) k p.
Lemma valid_access_inj: forall f m1 m2 b1 b2 delta chunk ofs p, mem_inj f m1 m2 -> f b1 = Some(b2, delta) -> valid_access m1 chunk b1 ofs p -> valid_access m2 chunk b2 (ofs + delta) p.
Lemma load_inj: forall f m1 m2 chunk b1 ofs b2 delta v1, mem_inj f m1 m2 -> load chunk m1 b1 ofs = Some v1 -> f b1 = Some (b2, delta) -> exists v2, load chunk m2 b2 (ofs + delta) = Some v2 /\ Val.inject f v1 v2.
Lemma loadbytes_inj: forall f m1 m2 len b1 ofs b2 delta bytes1, mem_inj f m1 m2 -> loadbytes m1 b1 ofs len = Some bytes1 -> f b1 = Some (b2, delta) -> exists bytes2, loadbytes m2 b2 (ofs + delta) len = Some bytes2 /\ list_forall2 (memval_inject f) bytes1 bytes2.
Lemma setN_inj: forall (access: Z -> Prop) delta f vl1 vl2, list_forall2 (memval_inject f) vl1 vl2 -> forall p c1 c2, (forall q, access q -> memval_inject f (ZMap.get q c1) (ZMap.get (q + delta) c2)) -> (forall q, access q -> memval_inject f (ZMap.get q (setN vl1 p c1)) (ZMap.get (q + delta) (setN vl2 (p + delta) c2))).