Require Import VST.floyd.base2.
Require Import VST.floyd.client_lemmas.
Require Import VST.floyd.type_induction.
(*Require Import VST.floyd.fieldlist.*)
Require Import VST.floyd.compact_prod_sum.
(*Require Import VST.floyd.aggregate_type.*)
Require Import VST.floyd.mapsto_memory_block.
Require Import VST.floyd.nested_pred_lemmas.
Require Import VST.floyd.jmeq_lemmas.
Require Import VST.floyd.sublist.

Require Export VST.floyd.fieldlist.
Require Export VST.floyd.aggregate_type.

Open Scope Z.
Open Scope logic.

(******************************************

Definition and lemmas about rangespec

******************************************)

Fixpoint rangespec (lo: Z) (n: nat) (P: Z -> val -> mpred): val -> mpred :=
  match n with
  | O => fun _ => emp
  | S n' => P lo * rangespec (Zsucc lo) n' P
 end.

Fixpoint fold_range' {A: Type} (f: Z -> A -> A) (zero: A) (lo: Z) (n: nat) : A :=
 match n with
  | O => zero
  | S n' => f lo (fold_range' f  zero (Zsucc lo) n')
 end.

Definition fold_range {A: Type} (f: Z -> A -> A) (zero: A) (lo hi: Z) : A :=
  fold_range' f zero lo (Z.to_nat (hi-lo)).

Lemma rangespec_shift_derives: forall lo lo' len P P' p p',
  (forall i i', lo <= i < lo + Z_of_nat len -> i - lo = i' - lo' -> P i p |-- P' i' p') ->
  rangespec lo len P p |-- rangespec lo' len P' p'.
Proof.
  intros.
  revert lo lo' H;
  induction len; intros.
  + simpl. auto.
  + simpl.
    apply sepcon_derives.
    - apply H; [| omega].
      rewrite Nat2Z.inj_succ.
      rewrite <- Z.add_1_r.
      omega.
    - apply IHlen. intros.
      apply H; [| omega].
      rewrite Nat2Z.inj_succ.
      rewrite <- Z.add_1_r.
      pose proof Zle_0_nat (S len).
      omega.
Qed.

Lemma rangespec_ext_derives: forall lo len P P' p,
  (forall i, lo <= i < lo + Z_of_nat len -> P i p |-- P' i p) ->
  rangespec lo len P p |-- rangespec lo len P' p.
Proof.
  intros.
  apply rangespec_shift_derives.
  intros.
  assert (i = i') by omega.
  subst.
  apply H.
  auto.
Qed.

Lemma rangespec_shift: forall lo lo' len P P' p p',
  (forall i i', lo <= i < lo + Z_of_nat len -> i - lo = i' - lo' -> P i p = P' i' p') ->
  rangespec lo len P p = rangespec lo' len P' p'.
Proof.
  intros; apply pred_ext; apply rangespec_shift_derives;
  intros.
  + erewrite H; eauto.
  + erewrite H; eauto.
    omega.
Qed.

Lemma rangespec_ext: forall lo len P P' p,
  (forall i, lo <= i < lo + Z_of_nat len -> P i p = P' i p) ->
  rangespec lo len P p = rangespec lo len P' p.
Proof.
  intros; apply pred_ext; apply rangespec_ext_derives;
  intros; rewrite H; auto.
Qed.

Lemma rangespec_sepcon: forall lo len P Q p,
  rangespec lo len P p * rangespec lo len Q p = rangespec lo len (P * Q) p.
Proof.
  intros.
  revert lo; induction len; intros.
  + simpl.
    rewrite sepcon_emp; auto.
  + simpl.
    rewrite !sepcon_assoc.
    f_equal.
    rewrite <- sepcon_assoc, (sepcon_comm _ (Q lo p)), sepcon_assoc.
    f_equal.
    rewrite IHlen.
    reflexivity.
Qed.

Lemma rangespec_elim: forall lo len P i,
  lo <= i < lo + Z_of_nat len -> rangespec lo len P |-- P i * TT.
Proof.
  intros. revert lo i H; induction len; intros.
  + simpl in H. omega.
  + simpl. intros; destruct (Z.eq_dec i lo).
    - subst. cancel.
    - replace (P i x * !!True) with (TT * (P i x * TT)) by (apply pred_ext; cancel).
      apply sepcon_derives; [cancel |].
      apply IHlen.
      rewrite Nat2Z.inj_succ in H.
      rewrite <- Z.add_1_l in *.
      omega.
Qed.

Inductive Forallz {A} (P: Z -> A->Prop) : Z -> list A -> Prop :=
 | Forallz_nil : forall i, Forallz P i nil
 | Forallz_cons : forall i x l, P i x -> Forallz P (Z.succ i) l -> Forallz P i (x::l).

(******************************************

Definition of aggregate predicates.

******************************************)

Definition array_pred {A: Type} (default: A) (lo hi: Z) (P: Z -> A -> val -> mpred) (v: list A) (p: val) : mpred :=
  !! (Zlength v = hi - lo) &&
  rangespec lo (Z.to_nat (hi-lo)) (fun i => P i (Znth (i-lo) v default)) p.

Definition struct_pred (m: members) {A: ident * type -> Type} (P: forall it, A it -> val -> mpred) (v: compact_prod (map A m)) (p: val): mpred.
Proof.
  destruct m as [| (i0, t0) m]; [exact emp |].
  revert i0 t0 v; induction m as [| (i0, t0) m]; intros ? ? v.
  + simpl in v.
    exact (P _ v p).
  + simpl in v.
    exact ((P _ (fst v) p) * IHm i0 t0 (snd v)).
Defined.

(* when unfold, do cbv [struct_pred list_rect]. *)

Definition union_pred (m: members) {A: ident * type -> Type} (P: forall it, A it -> val -> mpred) (v: compact_sum (map A m)) (p: val): mpred.
Proof.
  destruct m as [| (i0, t0) m]; [exact emp |].
  revert i0 t0 v; induction m as [| (i0, t0) m]; intros ? ? v.
  + simpl in v.
    exact (P _ v p).
  + simpl in v.
    destruct v as [v | v].
    - exact (P _ v p).
    - exact (IHm i0 t0 v).
Defined.

Definition array_Prop {A: Type} (d:A) (lo hi: Z) (P: Z -> A -> Prop) (v: list A) : Prop :=
   Zlength v = hi-lo /\ Forallz P 0 v.

Definition struct_Prop (m: members) {A: ident * type -> Type}
                             (P: forall it, A it -> Prop) (v: compact_prod (map A m)) : Prop.
Proof.
  destruct m as [| (i0, t0) m]; [exact True |].
  revert i0 t0 v; induction m as [| (i0, t0) m]; intros ? ? v.
  + simpl in v.
    exact (P _ v).
  + simpl in v.
    exact ((P _ (fst v)) /\ IHm i0 t0 (snd v)).
Defined.

Definition union_Prop (m: members) {A: ident * type -> Type}
               (P: forall it, A it -> Prop) (v: compact_sum (map A m)): Prop.
Proof.
  destruct m as [| (i0, t0) m]; [exact True |].
  revert i0 t0 v; induction m as [| (i0, t0) m]; intros ? ? v.
  + simpl in v.
    exact (P _ v).
  + simpl in v.
    destruct v as [v | v].
    - exact (P _ v).
    - exact (IHm i0 t0 v).
Defined.

(******************************************

Properties

******************************************)

Lemma array_pred_len_0: forall {A} (d: A) lo hi P p,
  hi = lo ->
  array_pred d lo hi P nil p = emp.
Proof.
  intros.
  unfold array_pred.
  replace (Z.to_nat (hi - lo)) with 0%nat by (symmetry; apply nat_of_Z_neg; omega).
  simpl.
  rewrite prop_true_andp by (unfold Zlength; simpl; omega).
  reflexivity.
Qed.

Lemma array_pred_len_1: forall {A} (d: A) i P v p,
  array_pred d i (i + 1) P (v :: nil) p = P i v p.
Proof.
  intros.
  unfold array_pred.
  replace (i + 1 - i) with 1 by omega.
  simpl. rewrite sepcon_emp.
  rewrite prop_true_andp by (unfold Zlength; simpl; omega).
  unfold Znth. rewrite Z.sub_diag. rewrite if_false by omega. change (Z.to_nat 0) with 0%nat. auto.
Qed.

Lemma split_array_pred: forall {A}  (d: A) lo mid hi P v p,
  lo <= mid <= hi ->
  Zlength v = hi - lo ->
  array_pred d lo hi P v p =
  array_pred d lo mid P (sublist 0 (mid-lo) v) p *
  array_pred d mid hi P (sublist (mid-lo) (hi-lo) v) p.
Proof.
  intros.
  unfold array_pred.
  normalize.
  rewrite prop_true_andp by (rewrite !Zlength_sublist by omega; omega).
  clear H0.
  remember (Z.to_nat (mid-lo)) as n.
  replace (Z.to_nat (hi-lo)) with (n + Z.to_nat (hi-mid))%nat in *
    by (subst n; rewrite <- Z2Nat.inj_add by omega; f_equal; omega).
  assert (lo = mid - Z.of_nat n)
    by (rewrite Heqn; rewrite Z2Nat.id by omega; omega).
  clear Heqn.
  revert lo v H H0; induction n; intros.
  + subst lo.
    change (Z.of_nat 0) with 0 in *.
    simpl rangespec at 2. rewrite emp_sepcon.
    rewrite Z.sub_0_r, Z.sub_diag, plus_0_l.
    apply rangespec_ext; intros.
    rewrite Z2Nat.id in H0 by omega.
    f_equal.
    rewrite Znth_sublist, Z.add_0_r by omega.
    reflexivity.
  + simpl plus at 1.
    unfold rangespec; fold rangespec.
    repeat match goal with |- context [(?A * ?B) p] => change ((A*B)p) with (A p * B p) end.
    rewrite !sepcon_assoc.
    f_equal.
    - f_equal.
      rewrite Z.sub_diag.
      subst lo.
      rewrite Znth_sublist by (try rewrite Nat2Z.inj_succ; omega).
      reflexivity.
    - replace (rangespec (Z.succ lo) (n + Z.to_nat (hi - mid))
              (fun i : Z => P i (Znth (i - lo) v d)) p)
      with (rangespec (Z.succ lo) (n + Z.to_nat (hi - mid))
              (fun i : Z => P i (Znth (i - Z.succ lo) (skipn 1 v) d)) p).
      Focus 2. {
        apply rangespec_ext; intros.
        f_equal.
        rewrite <- Znth_succ by omega; auto.
      } Unfocus.
      rewrite Nat2Z.inj_succ in H0.
      rewrite IHn by omega.
      f_equal.
      * apply rangespec_ext; intros.
        f_equal.
        rewrite Znth_sublist, Z.add_0_r by omega.
        rewrite <- Znth_succ by omega; auto.
        rewrite Znth_sublist, Z.add_0_r by omega.
        reflexivity.
      * apply rangespec_ext; intros.
        f_equal.
        rewrite Z2Nat.id in H1 by omega.
        rewrite Znth_sublist by omega.
        rewrite Znth_sublist by omega.
        replace (i - mid + (mid - Z.succ lo)) with (i - Z.succ lo) by omega.
        rewrite <- Znth_succ by omega; auto.
         f_equal; omega.
Qed.

Lemma array_pred_shift: forall {A} (d: A) (lo hi lo' hi' mv : Z) P' P v p,
  lo - lo' = mv ->
  hi - hi' = mv ->
 (forall i i', lo <= i < hi -> i - i' = mv -> P' i' (Znth (i-lo) v d) p = P i (Znth (i-lo) v d) p) ->
  array_pred d lo' hi' P' v p = array_pred d lo hi P v p.
Proof.
  intros.
  unfold array_pred.
  apply andp_prop_ext; [omega | intros].
  replace (hi' - lo') with (hi - lo) by omega.
  destruct (zlt hi lo). rewrite Z2Nat_neg by omega. reflexivity.
  apply pred_ext; apply rangespec_shift_derives; intros.
  rewrite H4; rewrite Z2Nat.id in H3 by omega.
  rewrite H1; auto; omega.
  rewrite <- H4; rewrite Z2Nat.id in H3 by omega.
  rewrite H1; auto; omega.
Qed.

Lemma array_pred_ext_derives: forall {A B} (dA: A) (dB: B) lo hi P0 P1 v0 v1 p,
  (Zlength v0 = hi - lo -> Zlength v1 = hi - lo) ->
  (forall i, lo <= i < hi ->
    P0 i (Znth (i-lo) v0 dA) p |-- P1 i (Znth (i-lo) v1 dB) p) ->
  array_pred dA lo hi P0 v0 p |-- array_pred dB lo hi P1 v1 p.
Proof.
  intros.
  unfold array_pred.
  normalize.
  rewrite prop_true_andp by omega.
  apply rangespec_ext_derives.
  intros.
  destruct (zlt hi lo).
  + rewrite Z2Nat_neg  in H2 by omega.
    change (Z.of_nat 0) with 0 in H2. omega.
  + rewrite Z2Nat.id in H2 by omega.
    apply H0. omega.
Qed.

Lemma array_pred_ext: forall {A B} (dA: A) (dB: B) lo hi P0 P1 v0 v1 p,
  Zlength v0 = Zlength v1 ->
  (forall i, lo <= i < hi ->
    P0 i (Znth (i-lo) v0 dA) p = P1 i (Znth (i-lo) v1 dB) p) ->
  array_pred dA lo hi P0 v0 p = array_pred dB lo hi P1 v1 p.
Proof.
  intros; apply pred_ext; apply array_pred_ext_derives; intros; try omega;
  rewrite H0; auto.
Qed.

Lemma at_offset_array_pred: forall  {A} (d:A) lo hi P v ofs p,
  at_offset (array_pred d lo hi P v) ofs p = array_pred d lo hi (fun i v => at_offset (P i v) ofs) v p.
Proof.
  intros.
  rewrite at_offset_eq.
  unfold array_pred.
  f_equal.
  apply rangespec_shift.
  intros.
  assert (i = i') by omega; subst i'; clear H0.
  rewrite at_offset_eq.
  auto.
Qed.

Lemma array_pred_sepcon: forall  {A} (d:A) lo hi P Q v p,
  array_pred d lo hi P v p * array_pred d lo hi Q v p = array_pred d lo hi (P * Q) v p.
Proof.
  intros.
  unfold array_pred.
  normalize.
  apply andp_prop_ext; [omega | intros].
  rewrite rangespec_sepcon.
  auto.
Qed.

Opaque member_dec.

Lemma struct_pred_ext_derives: forall m {A0 A1} (P0: forall it, A0 it -> val -> mpred) (P1: forall it, A1 it -> val -> mpred) v0 v1 p,
  members_no_replicate m = true ->
  (forall i d0 d1, in_members i m ->
     P0 _ (proj_struct i m v0 d0) p |-- P1 _ (proj_struct i m v1 d1) p) ->
  struct_pred m P0 v0 p |-- struct_pred m P1 v1 p.
Proof.
  unfold proj_struct, field_type.
  intros.
  destruct m as [| (i0, t0) m]; [simpl; auto |].
  revert i0 t0 v0 v1 H H0; induction m as [| (i1, t1) m]; intros.
  + specialize (H0 i0).
    simpl in H0.
    if_tac in H0; [| congruence].
    specialize (H0 v0 v1).
    spec H0; [left; reflexivity |].
    destruct (member_dec (i0, t0) (i0, t0)); [| congruence].
    unfold eq_rect_r in H0; rewrite <- !eq_rect_eq in H0.
    simpl.
    exact H0.
  + change (struct_pred ((i0, t0) :: (i1, t1) :: m) P0 v0 p) with
      (P0 (i0, t0) (fst v0) p * struct_pred ((i1, t1) :: m) P0 (snd v0) p).
    change (struct_pred ((i0, t0) :: (i1, t1) :: m) P1 v1 p) with
      (P1 (i0, t0) (fst v1) p * struct_pred ((i1, t1) :: m) P1 (snd v1) p).
    apply sepcon_derives.
    - specialize (H0 i0).
      simpl in H0.
      if_tac in H0; [| congruence].
      specialize (H0 (fst v0) (fst v1)).
      spec H0; [left; reflexivity |].
      destruct (member_dec (i0, t0) (i0, t0)); [| congruence].
      unfold eq_rect_r in H0; rewrite <- !eq_rect_eq in H0.
      simpl.
      exact H0.
    - rewrite members_no_replicate_ind in H.
      apply IHm; [tauto |].
      intros.
      specialize (H0 i).
      simpl in H0.
      if_tac in H0.
      * clear - H H1 H2.
        subst.
        tauto.
      * specialize (H0 d0 d1).
        spec H0; [right; auto |].
        change (if ident_eq i i1
                then Errors.OK t1
                else Ctypes.field_type i m) with (Ctypes.field_type i ((i1, t1) :: m)) in H0.
        destruct (member_dec
             (i,
             match Ctypes.field_type i ((i1, t1) :: m) with
             | Errors.OK t => t
             | Errors.Error _ => Tvoid
             end) (i0, t0)); [congruence |].
        exact H0.
Qed.

Lemma struct_pred_ext: forall m {A0 A1} (P0: forall it, A0 it -> val -> mpred) (P1: forall it, A1 it -> val -> mpred) v0 v1 p,
  members_no_replicate m = true ->
  (forall i d0 d1, in_members i m ->
     P0 _ (proj_struct i m v0 d0) p = P1 _ (proj_struct i m v1 d1) p) ->
  struct_pred m P0 v0 p = struct_pred m P1 v1 p.
Proof.
  intros.
  apply pred_ext; eapply struct_pred_ext_derives; eauto;
  intros; erewrite H0 by eauto; auto.
Qed.

Lemma struct_pred_not_member: forall m {A} (P: forall it, A it -> val -> mpred) (i: ident) v p,
  let P' it := if ident_eq i (fst it) then fun _ _ => emp else P it in
  ~ in_members i m ->
  struct_pred m P v p = struct_pred m P' v p.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [auto |].
  unfold proj_struct, field_type in *.
  revert i0 t0 H v; induction m as [| (i1, t1) m]; intros i0 t0 H.
  + intros; subst P'; simpl.
    rewrite if_false; auto.
    intro; apply H; subst; left; auto.
  + set (M := (i1, t1) :: m).
    simpl compact_prod; simpl Ctypes.field_type.
    intros v.
    subst M.
    change (struct_pred ((i0, t0) :: (i1, t1) :: m) P v p)
      with (P _ (fst v) p * struct_pred ((i1, t1) :: m) P (snd v) p).
    change (struct_pred ((i0, t0) :: (i1, t1) :: m) P' v p)
      with (P' _ (fst v) p * struct_pred ((i1, t1) :: m) P' (snd v) p).
    destruct (ident_eq i i0).
    - subst P'; intros; subst.
      exfalso; apply H.
      left; auto.
    - intros.
      f_equal.
      * unfold P'.
        rewrite if_false by auto.
        auto.
      * apply IHm.
        intro; apply H; right; auto.
Qed.

Lemma struct_pred_proj: forall m {A} (P: forall it, A it -> val -> mpred) (i: ident) v p d,
  let P' it := if ident_eq i (fst it) then fun _ _ => emp else P it in
  members_no_replicate m = true ->
  in_members i m ->
  struct_pred m P v p = P _ (proj_struct i m v d) p * struct_pred m P' v p.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [inv H0 |].
  unfold proj_struct, field_type in *.
  revert i0 t0 H v d H0; induction m as [| (i1, t1) m]; intros.
  + subst P'; simpl in *.
    destruct H0; [simpl in H0; subst i | tauto].
    destruct (ident_eq i0 i0); [| congruence].
    destruct (member_dec (i0, t0) (i0, t0)); [| congruence].
    unfold eq_rect_r; rewrite <- eq_rect_eq.
    rewrite sepcon_emp; auto.
  + pose proof H.
    apply members_no_replicate_ind in H1; destruct H1.
    set (M := (i1, t1) :: m).
    simpl compact_prod in v |- *; simpl Ctypes.field_type in d |- *.
    subst M.
    change (struct_pred ((i0, t0) :: (i1, t1) :: m) P v p)
      with (P _ (fst v) p * struct_pred ((i1, t1) :: m) P (snd v) p).
    change (struct_pred ((i0, t0) :: (i1, t1) :: m) P' v p)
      with (P' _ (fst v) p * struct_pred ((i1, t1) :: m) P' (snd v) p).
    destruct (ident_eq i i0).
    - subst i0.
      f_equal.
      * simpl.
        destruct (member_dec (i, t0) (i, t0)); [| congruence].
        unfold eq_rect_r; rewrite <- eq_rect_eq.
        auto.
      * erewrite struct_pred_not_member by eauto.
        unfold P' at 1.
        rewrite if_true by auto.
        rewrite emp_sepcon; auto.
    - intros.
      destruct H0; [simpl in H0; congruence |].
      rewrite <- sepcon_assoc, (sepcon_comm _ (P' _ _ _)), sepcon_assoc.
      f_equal.
      * unfold P'.
        rewrite if_false by (simpl; congruence).
        auto.
      * erewrite IHm by eauto.
        f_equal.
        simpl.
        match goal with
        | |- context [member_dec (i, ?t') (i0, t0)] =>
                destruct (member_dec (i, t') (i0, t0));
                  [inversion e; congruence |]
        end.
        auto.
Qed.

Lemma struct_pred_upd: forall m {A} (P: forall it, A it -> val -> mpred) (i: ident) v p v0,
  let P' it := if ident_eq i (fst it) then fun _ _ => emp else P it in
  members_no_replicate m = true ->
  in_members i m ->
  struct_pred m P (upd_struct i m v v0) p = P _ v0 p * struct_pred m P' v p.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [inv H0 |].
  unfold proj_struct, field_type in *.
  revert i0 t0 H v v0 H0; induction m as [| (i1, t1) m]; intros.
  + subst P'; simpl in *.
    destruct H0; [simpl in H0; subst i | tauto].
    destruct (ident_eq i0 i0); [| congruence].
    destruct (member_dec (i0, t0) (i0, t0)); [| congruence].
    unfold eq_rect_r; rewrite <- eq_rect_eq.
    rewrite sepcon_emp; auto.
  + pose proof H.
    apply members_no_replicate_ind in H1; destruct H1.
    simpl compact_prod in v |- *; simpl Ctypes.field_type in v0 |- *.
    set (v' := (upd_struct i ((i0, t0) :: (i1, t1) :: m) v v0)).
    change (struct_pred ((i0, t0) :: (i1, t1) :: m) P v' p)
      with (P _ (fst v') p * struct_pred ((i1, t1) :: m) P (snd v') p).
    change (struct_pred ((i0, t0) :: (i1, t1) :: m) P' v p)
      with (P' _ (fst v) p * struct_pred ((i1, t1) :: m) P' (snd v) p).
    subst v'.
    simpl upd_struct.
    destruct (ident_eq i i0).
    - subst i0.
      destruct (member_dec (i, t0) (i, t0)); [| congruence].
      f_equal.
      * simpl.
        unfold eq_rect_r; rewrite <- eq_rect_eq.
        auto.
      * unfold eq_rect_r; rewrite <- eq_rect_eq.
        change (snd (v0, snd v)) with (snd v).
        erewrite struct_pred_not_member by eauto.
        unfold P' at 1.
        rewrite if_true by auto.
        rewrite emp_sepcon; auto.
    - destruct H0; [simpl in H0; congruence |].
      rewrite <- sepcon_assoc, (sepcon_comm _ (P' _ _ _)), sepcon_assoc.
      match goal with
      | |- context [member_dec (i, ?t') (i0, t0)] =>
              destruct (member_dec (i, t') (i0, t0));
                [inversion e; congruence |]
      end.
      f_equal.
      * unfold P'; simpl.
        rewrite if_false by (simpl; congruence).
        auto.
      * simpl snd.
        simpl in IHm |- *; erewrite IHm by auto.
        reflexivity.
Qed.

Lemma struct_pred_ramif: forall m {A} (P: forall it, A it -> val -> mpred) (i: ident) v p d,
  in_members i m ->
  members_no_replicate m = true ->
  struct_pred m P v p |--
    P _ (proj_struct i m v d) p *
     (ALL v0: _, P _ v0 p -* struct_pred m P (upd_struct i m v v0) p).
Proof.
  intros.
  set (P' it := if ident_eq i (fst it) then fun _ _ => emp else P it).
  apply RAMIF_Q.solve with (struct_pred m P' v p).
  + apply derives_refl'.
    apply struct_pred_proj; auto.
  + intro v0.
    apply derives_refl'.
    symmetry; rewrite sepcon_comm.
    apply struct_pred_upd; auto.
Qed.

Lemma at_offset_struct_pred: forall m {A} (P: forall it, A it -> val -> mpred) v p ofs,
  at_offset (struct_pred m P v) ofs p = struct_pred m (fun it v => at_offset (P it v) ofs) v p.
Proof.
  intros.
  rewrite at_offset_eq.
  destruct m as [| (i0, t0) m]; [auto |].
  revert i0 t0 v; induction m as [| (i1, t1) m]; intros.
  + simpl.
    rewrite at_offset_eq.
    auto.
  + simpl.
    rewrite at_offset_eq.
    f_equal.
Qed.

Lemma corable_andp_struct_pred: forall m {A} (P: forall it, A it -> val -> mpred) v p Q,
  corable Q ->
  Q && struct_pred m P v p =
  match m with
  | nil => Q && emp
  | _ => struct_pred m (fun it v p => Q && P it v p) v p
  end.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [auto |].
  revert i0 t0 v; induction m as [| (i1, t1) m]; intros.
  + simpl.
    auto.
  + change (struct_pred ((i0, t0) :: (i1, t1) :: m) P v p)
      with (P (i0, t0) (fst v) p * struct_pred ((i1, t1) :: m) P (snd v) p).
    pattern Q at 1; rewrite <- (andp_dup Q).
    rewrite andp_assoc.
    rewrite <- corable_sepcon_andp1 by auto.
    rewrite IHm.
    rewrite <- corable_andp_sepcon1 by auto.
    reflexivity.
Qed.

Lemma struct_pred_sepcon: forall m {A} (P Q: forall it, A it -> val -> mpred) v p,
  struct_pred m P v p * struct_pred m Q v p = struct_pred m (fun it => P it * Q it) v p.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [| revert i0 t0 v; induction m as [| (i1, t1) m]; intros].
  + simpl.
    rewrite emp_sepcon; auto.
  + simpl.
    auto.
  + change (struct_pred ((i0, t0) :: (i1, t1) :: m) P v p)
      with (P (i0, t0) (fst v) p * struct_pred ((i1, t1) :: m) P (snd v) p).
    change (struct_pred ((i0, t0) :: (i1, t1) :: m) Q v p)
      with (Q (i0, t0) (fst v) p * struct_pred ((i1, t1) :: m) Q (snd v) p).
    change (struct_pred ((i0, t0) :: (i1, t1) :: m) (fun it => P it * Q it) v p)
      with (P (i0, t0) (fst v) p * Q (i0, t0) (fst v) p * struct_pred ((i1, t1) :: m) (fun it => P it * Q it) (snd v) p).
    rewrite !sepcon_assoc; f_equal.
    rewrite <- sepcon_assoc, (sepcon_comm _ (Q _ _ _)), sepcon_assoc; f_equal.
    apply IHm.
Qed.

Lemma compact_sum_inj_eq_spec: forall {A} a0 a1 (l: list A) F0 F1 (v0: compact_sum (map F0 (a0 :: a1 :: l))) (v1: compact_sum (map F1 (a0 :: a1 :: l))) H,
  ~ In a0 (a1 :: l) ->
 ((forall a, compact_sum_inj v0 a H <-> compact_sum_inj v1 a H) <->
  match v0, v1 with
  | inl _, inl _ => True
  | inr v0, inr v1 => forall a, compact_sum_inj (v0: compact_sum (map F0 (a1 :: l))) a H <-> compact_sum_inj (v1: compact_sum (map F1 (a1 :: l))) a H
  | _, _ => False
  end).
Proof.
  intros.
  rename H0 into H_not_in.
  destruct v0, v1.
  + simpl.
    firstorder.
  + assert (~ (forall a : A,
      iff
        (@compact_sum_inj A F0 (@cons A a0 (@cons A a1 l))
           (@inl (F0 a0) (compact_sum (@map A Type F0 (@cons A a1 l))) f) a H)
        (@compact_sum_inj A F1 (@cons A a0 (@cons A a1 l))
           (@inr (F1 a0) (compact_sum (@map A Type F1 (@cons A a1 l))) c) a H))); [| tauto].
    intro.
    specialize (H0 a0).
    simpl in H0.
    destruct (H a0 a0); [| congruence].
    tauto.
  + assert (~ (forall a : A,
      iff
        (@compact_sum_inj A F0 (@cons A a0 (@cons A a1 l))
           (@inr (F0 a0) (compact_sum (@map A Type F0 (@cons A a1 l))) c) a H)
        (@compact_sum_inj A F1 (@cons A a0 (@cons A a1 l))
           (@inl (F1 a0) (compact_sum (@map A Type F1 (@cons A a1 l))) f) a H))); [| tauto].
    intro.
    specialize (H0 a0).
    simpl in H0.
    destruct (H a0 a0); [| congruence].
    tauto.
  + split; intros HH a; specialize (HH a).
    - pose proof compact_sum_inj_in c a H.
      pose proof compact_sum_inj_in c0 a H.
Opaque In.
      simpl in HH, H0, H1 |- *.
Transparent In.
      destruct (H a a0).
      * subst.
        tauto.
      * tauto.
    - simpl in HH |- *.
      destruct (H a a0).
      * tauto.
      * tauto.
Qed.

Lemma union_pred_ext_derives: forall m {A0 A1} (P0: forall it, A0 it -> val -> mpred) (P1: forall it, A1 it -> val -> mpred) v0 v1 p,
  members_no_replicate m = true ->
  (forall it, members_union_inj v0 it <-> members_union_inj v1 it) ->
  (forall i d0 d1, members_union_inj v0 (i, field_type i m) -> members_union_inj v1 (i, field_type i m) ->
     P0 _ (proj_union i m v0 d0) p |-- P1 _ (proj_union i m v1 d1) p) ->
  union_pred m P0 v0 p |-- union_pred m P1 v1 p.
Proof.
  unfold members_union_inj, proj_union, field_type.
  intros.
  destruct m as [| (i0, t0) m]; [simpl; auto |].
  revert i0 t0 v0 v1 H H0 H1; induction m as [| (i1, t1) m]; intros.
  + specialize (H1 i0).
    simpl in H1.
    if_tac in H1; [| congruence].
    specialize (H1 v0 v1).
    spec H1; [if_tac; [auto | congruence] |].
    spec H1; [if_tac; [auto | congruence] |].
    destruct (member_dec (i0, t0) (i0, t0)); [| congruence].
    unfold eq_rect_r in H1; rewrite <- !eq_rect_eq in H1.
    simpl.
    exact H1.
  + rewrite compact_sum_inj_eq_spec in H0.
    Focus 2. {
      clear - H.
      pose proof in_members_tail_no_replicate i0 _ _ _ H.
      intro HH; apply in_map with (f := fst) in HH.
      apply H0 in HH.
      tauto.
    } Unfocus.
    destruct v0 as [v0 | v0], v1 as [v1 | v1]; try solve [inversion H0].
    - specialize (H1 i0).
      simpl in H1.
      if_tac in H1; [| congruence].
      specialize (H1 v0 v1).
      spec H1; [if_tac; [auto | congruence] |].
      spec H1; [if_tac; [auto | congruence] |].
      destruct (member_dec (i0, t0) (i0, t0)); [| congruence].
      unfold eq_rect_r in H1; rewrite <- !eq_rect_eq in H1.
      simpl.
      exact H1.
    - rewrite members_no_replicate_ind in H.
      apply IHm; [tauto | tauto |].
      intros.
      specialize (H1 i).
      simpl in H1.
      if_tac in H1. (* i = i0 vs i <> i0 *)
      * clear - H H2 H4 t0.
        pose proof compact_sum_inj_in v0 (i, field_type i ((i1, t1) :: m)) member_dec.
        spec H0; [exact H2 |].
        subst.
        apply in_map with (f := fst) in H0.
        unfold fst at 1 in H0.
        tauto.
      * specialize (H1 d0 d1).
        change (if ident_eq i i1
                then Errors.OK t1
                else Ctypes.field_type i m) with (Ctypes.field_type i ((i1, t1) :: m)) in H1.
        destruct (member_dec
             (i,
             match Ctypes.field_type i ((i1, t1) :: m) with
             | Errors.OK t => t
             | Errors.Error _ => Tvoid
             end) (i0, t0)); [congruence |].
        spec H1; [auto |].
        spec H1; [auto |].
        exact H1.
Qed.

Lemma union_pred_ext: forall m {A0 A1} (P0: forall it, A0 it -> val -> mpred) (P1: forall it, A1 it -> val -> mpred) v0 v1 p,
  members_no_replicate m = true ->
  (forall it, members_union_inj v0 it <-> members_union_inj v1 it) ->
  (forall i d0 d1, members_union_inj v0 (i, field_type i m) -> members_union_inj v1 (i, field_type i m) ->
     P0 _ (proj_union i m v0 d0) p = P1 _ (proj_union i m v1 d1) p) ->
  union_pred m P0 v0 p = union_pred m P1 v1 p.
Proof.
  intros.
  assert (forall it, members_union_inj v1 it <-> members_union_inj v0 it)
    by (intro it; specialize (H0 it); tauto).
  apply pred_ext; eapply union_pred_ext_derives; auto;
  intros; erewrite H1 by eauto; auto.
Qed.

Lemma union_pred_derives_const: forall m {A} (P: forall it, A it -> val -> mpred) p v R,
  members_no_replicate m = true ->
  m <> nil ->
  (forall i (v: A (i, field_type i m)), in_members i m -> P _ v p |-- R) ->
  union_pred m P v p |-- R.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [congruence |].
  clear H0.
  revert i0 t0 v H H1; induction m as [| (i1, t1) m]; intros.
  + simpl.
    specialize (H1 i0); simpl in H1.
    destruct (ident_eq i0 i0); [| congruence].
    apply H1; left; auto.
  + destruct v; simpl.
    - specialize (H1 i0); simpl in H1.
      destruct (ident_eq i0 i0); [| congruence].
      apply H1; left; auto.
    - pose proof H.
      rewrite members_no_replicate_ind in H; destruct H.
      apply (IHm i1 t1); auto.
      intros.
      specialize (H1 i).
      pose proof in_members_tail_no_replicate _ _ _ _ H0 H3.
      simpl in H1; destruct (ident_eq i i0); [congruence |].
      apply H1.
      right; auto.
Qed.

Lemma union_pred_proj: forall m {A} (P: forall it, A it -> val -> mpred) (i: ident) v p d,
  members_no_replicate m = true ->
  in_members i m ->
  (forall i' (v': A (i', field_type i' m)), in_members i' m -> P _ v' p |-- P _ d p) ->
  union_pred m P v p |-- P _ (proj_union i m v d) p.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [inv H0 |].
  revert i0 t0 v d H H0 H1; induction m as [| (i1, t1) m]; intros.
  + destruct H0; [simpl in H0; subst i | tauto].
    simpl in *.
    destruct (ident_eq i0 i0); [| congruence].
    destruct (member_dec (i0, t0) (i0, t0)); [| congruence].
    unfold eq_rect_r; rewrite <- eq_rect_eq.
    auto.
  + pose proof H.
    rewrite members_no_replicate_ind in H; destruct H.
    simpl in *; destruct (ident_eq i i0), v.
    - subst i.
      destruct (member_dec (i0, t0) (i0, t0)); [| congruence].
      unfold eq_rect_r; rewrite <- eq_rect_eq.
      auto.
    - subst i.
      destruct (member_dec (i0, t0) (i0, t0)); [| congruence].
      unfold eq_rect_r; rewrite <- eq_rect_eq.
      apply (union_pred_derives_const ((i1, t1) :: m) P p y); [auto | congruence |].
      intros.
      specialize (H1 i).
      pose proof in_members_tail_no_replicate _ _ _ _ H2 H4.
      simpl in H1; destruct (ident_eq i i0); [congruence |].
      apply H1.
      right; auto.
    - match goal with
      | |- context [member_dec (i, ?t') (i0, t0)] =>
              destruct (member_dec (i, t') (i0, t0));
                [inversion e; congruence |]
      end.
      specialize (H1 i0).
      destruct (ident_eq i0 i0); [| congruence].
      apply H1.
      left; auto.
    - match goal with
      | |- context [member_dec (i, ?t') (i0, t0)] =>
              destruct (member_dec (i, t') (i0, t0));
                [inversion e; congruence |]
      end.
      apply (IHm i1 t1); auto.
      * destruct H0; [| auto].
        simpl in H0; congruence.
      * intros.
        specialize (H1 i').
        pose proof in_members_tail_no_replicate _ _ _ _ H2 H4.
        simpl in H1; destruct (ident_eq i' i0); [congruence |].
        apply H1.
        right; auto.
Qed.

Lemma union_pred_upd: forall m {A} (P: forall it, A it -> val -> mpred) (i: ident) v p v0,
  members_no_replicate m = true ->
  in_members i m ->
  union_pred m P (upd_union i m v v0) p = P _ v0 p.
Proof.
  intros.
  intros.
  unfold upd_union, upd_compact_sum.
  destruct (in_dec member_dec (i, field_type i m) m) as [?H | ?H];
    [| apply in_members_field_type in H0; tauto].
  clear v.
  destruct m as [| (i0, t0) m]; [inv H0 |].
  revert i0 t0 v0 H H0 H1; induction m as [| (i1, t1) m]; intros.
  + simpl in *.
    destruct H0; [simpl in H0; subst i | tauto].
    destruct (ident_eq i0 i0); [| congruence].
    destruct (member_dec (i0, t0) (i0, t0)); [| congruence].
    unfold eq_rect_r; rewrite <- eq_rect_eq.
    auto.
  + destruct H0; [simpl in H0; subst i |].
    - simpl in *.
      destruct (ident_eq i0 i0); [| congruence].
      destruct (member_dec (i0, t0) (i0, t0)); [| congruence].
      unfold eq_rect_r; rewrite <- eq_rect_eq.
      auto.
    - simpl in *.
      pose proof in_members_tail_no_replicate _ _ _ _ H H0.
      destruct (ident_eq i i0); [congruence |].
      match goal with
      | |- context [member_dec (i, ?t') (i0, t0)] =>
              destruct (member_dec (i, t') (i0, t0));
                [inversion e; congruence |]
      end.
      rewrite members_no_replicate_ind in H; destruct H.
      apply (IHm i1 t1); auto.
Qed.

Lemma union_pred_ramif: forall m {A} (P: forall it, A it -> val -> mpred) (i: ident) v p d,
  (forall i' (v': A (i', field_type i' m)), in_members i' m -> P _ v' p |-- P _ d p) ->
  in_members i m ->
  members_no_replicate m = true ->
  union_pred m P v p |--
    P _ (proj_union i m v d) p *
     (ALL v0: _, P _ v0 p -* union_pred m P (upd_union i m v v0) p).
Proof.
  intros.
  apply RAMIF_Q.solve with emp.
  + rewrite sepcon_emp.
    apply union_pred_proj; auto.
  + intro v0.
    rewrite emp_sepcon.
    apply derives_refl'.
    symmetry.
    apply union_pred_upd; auto.
Qed.

Lemma at_offset_union_pred: forall m {A} (P: forall it, A it -> val -> mpred) v p ofs,
  at_offset (union_pred m P v) ofs p = union_pred m (fun it v => at_offset (P it v) ofs) v p.
Proof.
  intros.
  rewrite at_offset_eq.
  destruct m as [| (i0, t0) m]; [simpl; auto |].
  revert i0 t0 v; induction m as [| (i1, t1) m]; intros.
  + simpl.
    rewrite at_offset_eq.
    auto.
  + simpl.
    destruct v.
    - rewrite at_offset_eq.
      auto.
    - apply IHm.
Qed.

Lemma andp_union_pred: forall m {A} (P: forall it, A it -> val -> mpred) v p Q,
  Q && union_pred m P v p =
  match m with
  | nil => Q && emp
  | _ => union_pred m (fun it v p => Q && P it v p) v p
  end.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [auto |].
  revert i0 t0 v; induction m as [| (i1, t1) m]; intros.
  + simpl.
    auto.
  + destruct v.
    - simpl.
      auto.
    - simpl.
      apply IHm.
Qed.

Lemma union_pred_sepcon: forall m {A} (P Q: forall it, A it -> val -> mpred) v p,
  union_pred m P v p * union_pred m Q v p = union_pred m (fun it => P it * Q it) v p.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [| revert i0 t0 v; induction m as [| (i1, t1) m]; intros].
  + simpl.
    rewrite sepcon_emp; auto.
  + simpl.
    auto.
  + destruct v.
    - simpl; auto.
    - apply IHm.
Qed.

Lemma struct_Prop_compact_prod_gen: forall m (F: ident * type -> Type) (P: forall it, F it -> Prop) (f: forall it, F it),
  members_no_replicate m = true ->
  (forall i, in_members i m -> P (i, field_type i m) (f (i, field_type i m))) ->
  struct_Prop m P (compact_prod_gen f m).
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [simpl; auto |].
  revert i0 t0 H H0; induction m as [| (i1, t1) m]; intros.
  + simpl.
    specialize (H0 i0).
    simpl in H0.
    rewrite if_true in H0 by auto.
    apply H0; left; auto.
  + change (struct_Prop ((i0, t0) :: (i1, t1) :: m) P
             (compact_prod_gen f ((i0, t0) :: (i1, t1) :: m)))
    with (P (i0, t0) (f (i0, t0)) /\
            struct_Prop ((i1, t1) :: m) P (compact_prod_gen f ((i1, t1) :: m))).
    split.
    - specialize (H0 i0).
      simpl in H0.
      rewrite if_true in H0 by auto.
      apply H0; left; auto.
    - rewrite members_no_replicate_ind in H; destruct H.
      apply (IHm i1 t1); auto.
      intros.
      specialize (H0 i).
      simpl in H0.
      destruct (ident_eq i i0); [subst; tauto |].
      apply H0; right; auto.
Qed.

Lemma struct_Prop_proj: forall m (F: ident * type -> Type) (P: forall it, F it -> Prop) v i d,
  in_members i m ->
  struct_Prop m P v ->
  P (i, field_type i m) (proj_struct i m v d).
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [inversion H |].
  revert i0 t0 v d H H0; induction m as [| (i1, t1) m]; intros.
  + inversion H; [simpl in H0; subst| tauto].
    simpl in *.
    destruct (ident_eq i0 i0); [| tauto].
    destruct (member_dec (i0, t0) (i0, t0)); [| tauto].
    unfold eq_rect_r; rewrite <- eq_rect_eq.
    auto.
  + destruct (ident_eq i i0).
    - subst.
      simpl in *.
      destruct (ident_eq i0 i0); [| tauto].
      destruct (member_dec (i0, t0) (i0, t0)); [| tauto].
      unfold eq_rect_r; rewrite <- eq_rect_eq.
      exact (proj1 H0).
    - assert (in_members i ((i1, t1) :: m)) by (inversion H; [subst; tauto | auto]).
      simpl in *.
      destruct (ident_eq i i0); [tauto |].
      destruct (member_dec
         (i,
         match
           (if ident_eq i i1 then Errors.OK t1 else Ctypes.field_type i m)
         with
         | Errors.OK t => t
         | Errors.Error _ => Tvoid
         end) (i0, t0)); [inversion e; subst; tauto |].
      apply IHm; auto.
      exact (proj2 H0).
Qed.

Lemma union_Prop_compact_sum_gen: forall m (F: ident * type -> Type) (P: forall it, F it -> Prop) (f: forall it, F it),
  members_no_replicate m = true ->
  (forall i, in_members i m -> P (i, field_type i m) (f (i, field_type i m))) ->
  union_Prop m P (compact_sum_gen (fun _ => true) f m).
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [simpl; auto |].
  destruct m as [| (i1, t1) m].
  + simpl.
    specialize (H0 i0).
    simpl in H0.
    rewrite if_true in H0 by auto.
    apply H0; left; auto.
  + simpl.
    specialize (H0 i0).
    simpl in H0.
    rewrite if_true in H0 by auto.
    apply H0; left; auto.
Qed.

Lemma union_Prop_proj: forall m (F: ident * type -> Type) (P: forall it, F it -> Prop) v i d,
  members_union_inj v (i, field_type i m) ->
  union_Prop m P v ->
  P (i, field_type i m) (proj_union i m v d).
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [inversion H |].
  revert i0 t0 v d H H0; induction m as [| (i1, t1) m]; intros.
  + simpl in H. if_tac in H; [| tauto].
    inversion H1; subst i.
    clear H1 H4.
    simpl in *.
    destruct (ident_eq i0 i0); [| tauto].
    destruct (member_dec (i0, t0) (i0, t0)); [| tauto].
    unfold eq_rect_r; rewrite <- eq_rect_eq.
    auto.
  + destruct (ident_eq i i0).
    - subst.
      simpl in *.
      destruct (ident_eq i0 i0); [| tauto].
      destruct (member_dec (i0, t0) (i0, t0)); [| tauto].
      unfold eq_rect_r; rewrite <- eq_rect_eq.
      destruct v; [| inversion H].
      auto.
    - assert (members_union_inj v (i, field_type i ((i1, t1) :: m))) by (simpl in H; destruct (ident_eq i i0); [tauto | auto]).
      simpl in *.
      destruct (ident_eq i i0); [tauto |].
      destruct (member_dec
         (i,
         match
           (if ident_eq i i1 then Errors.OK t1 else Ctypes.field_type i m)
         with
         | Errors.OK t => t
         | Errors.Error _ => Tvoid
         end) (i0, t0)); [inversion e; subst; tauto |].
      destruct v; [tauto |].
      apply IHm; auto.
Qed.

Lemma array_pred_local_facts: forall {A} (d: A) lo hi P v p Q,
  (forall i x, lo <= i < hi -> P i x p |-- !! Q x) ->
  array_pred d lo hi P v p |-- !! (Zlength v = hi - lo /\ Forall Q v).
Proof.
  intros.
  unfold array_pred.
  normalize.
  rewrite prop_and; apply andp_right; [normalize |].
  pose proof ZtoNat_Zlength v.
  rewrite H0 in H1; symmetry in H1; clear H0.
  revert hi lo H H1; induction v; intros.
  + apply prop_right; constructor.
  + replace (hi - lo) with (Z.succ (hi - Z.succ lo)) in * by omega.
    assert (hi - Z.succ lo >= 0).
    Focus 1. {
      destruct (zlt (hi - Z.succ lo) 0); auto.
      assert (Z.succ (hi - Z.succ lo) <= 0) by omega.
      simpl length in H1.
      destruct (zeq (Z.succ (hi - Z.succ lo)) 0);
       [rewrite e in H1 | rewrite Z2Nat_neg in H1 by omega]; inv H1.
    } Unfocus.
    rewrite Z2Nat.inj_succ in H1 |- * by omega.
    inv H1.
    simpl rangespec.
    replace (rangespec (Z.succ lo) (length v)
              (fun i : Z => P i (Znth (i - lo) (a :: v) d)) p)
    with (rangespec (Z.succ lo) (length v)
            (fun i : Z => P i (Znth (i - Z.succ lo) v d)) p).
    Focus 2. {
      apply rangespec_ext; intros.
      change v with (skipn 1 (a :: v)) at 1.
      rewrite <- Znth_succ by omega.
      auto.
    } Unfocus.
    rewrite H3.
    eapply derives_trans; [apply sepcon_derives; [apply H | apply IHv; auto] |].
    - omega.
    - intros; apply H; omega.
    - rewrite sepcon_prop_prop.
      apply prop_derives; intros.
      rewrite Z.sub_diag in H1; cbv in H1.
      constructor; tauto.
Qed.

Lemma struct_pred_local_facts: forall m {A} (P: forall it, A it -> val -> mpred)v p (R: forall it, A it -> Prop),
  members_no_replicate m = true ->
  (forall i v0, in_members i m -> P (i, field_type i m) v0 p |-- !! R _ v0) ->
  struct_pred m P v p |-- !! struct_Prop m R v.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [simpl; apply prop_right; auto |].
  revert i0 t0 v H H0; induction m as [| (i1, t1) m]; intros.
  + simpl.
    specialize (H0 i0).
    simpl in H0.
    rewrite if_true in H0 by auto.
    apply H0; left; auto.
  + change (struct_Prop ((i0, t0) :: (i1, t1) :: m) R v)
      with (R (i0, t0) (fst v) /\ struct_Prop ((i1, t1) :: m) R (snd v)).
    change (struct_pred ((i0, t0) :: (i1, t1) :: m) P v p)
      with (P (i0, t0) (fst v) p * struct_pred ((i1, t1) :: m) P (snd v) p).
    rewrite members_no_replicate_ind in H.

    pose proof H0 i0.
    simpl in H1.
    if_tac in H1; [| congruence].
    specialize (H1 (fst v)).
    spec H1; [left; auto |].

    specialize (IHm i1 t1 (snd v)).
    spec IHm; [tauto |].
    eapply derives_trans; [apply sepcon_derives; [apply H1 | apply IHm] |].
    - intros.
      specialize (H0 i).
      simpl in H0.
      destruct (ident_eq i i0); [subst; tauto |].
      apply H0; right; auto.
    - rewrite sepcon_prop_prop; normalize.
Qed.

Lemma union_pred_local_facts: forall m {A} (P: forall it, A it -> val -> mpred)v p (R: forall it, A it -> Prop),
  members_no_replicate m = true ->
  (forall i v0, in_members i m -> P (i, field_type i m) v0 p |-- !! R _ v0) ->
  union_pred m P v p |-- !! union_Prop m R v.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [simpl; apply prop_right; auto |].
  revert i0 t0 v H H0; induction m as [| (i1, t1) m]; intros.
  + simpl.
    specialize (H0 i0).
    simpl in H0.
    rewrite if_true in H0 by auto.
    apply H0; left; auto.
  + rewrite members_no_replicate_ind in H.
    destruct v.
    - simpl.
      pose proof H0 i0.
      simpl in H1.
      if_tac in H1; [| congruence].
      specialize (H1 a).
      apply H1; left; auto.
    - specialize (IHm i1 t1 c).
      spec IHm; [tauto |].
      apply IHm.
      intros.
      specialize (H0 i).
      simpl in H0.
      destruct (ident_eq i i0); [subst; tauto |].
      apply H0; right; auto.
Qed.

Section MEMORY_BLOCK_AGGREGATE.

Context {cs: compspecs}.

Lemma memory_block_array_pred: forall  {A} (d:A) sh t lo hi v b ofs,
  0 <= ofs + sizeof t * lo /\ ofs + sizeof t * hi < Ptrofs.modulus ->
  0 <= lo <= hi ->
  Zlength v = hi - lo ->
  array_pred d lo hi
    (fun i _ p => memory_block sh (sizeof t) (offset_val (sizeof t * i) p)) v
    (Vptr b (Ptrofs.repr ofs)) =
   memory_block sh (sizeof t * (hi - lo)) (Vptr b (Ptrofs.repr (ofs + sizeof t * lo))).
Proof.
  intros.
  unfold array_pred.
  rewrite prop_true_andp by auto; clear H1.
  f_equal.
  remember (Z.to_nat (hi - lo)) as n eqn:HH.
  revert lo HH H H0 v; induction n; intros.
  + simpl.
    pose proof arith_aux00 _ _ (proj2 H0) HH.
    rewrite H1, Z.mul_0_r, memory_block_zero_Vptr.
    reflexivity.
  + simpl.
    pose proof arith_aux01 _ _ _ HH.
    solve_mod_modulus.
    pose_size_mult cenv_cs t (0 :: hi - Z.succ lo :: hi - lo :: nil).
    rewrite IHn; [| apply arith_aux02; auto | omega | omega | exact v].
    replace (ofs + sizeof  t * Z.succ lo) with (ofs + sizeof t * lo + sizeof t) by omega.
    rewrite <- memory_block_split by (auto; omega).
    f_equal.
    omega.
Qed.

Lemma memory_block_array_pred': forall {A} (d:A)  sh t z b ofs,
  0 <= z ->
  0 <= ofs /\ ofs + sizeof t * z < Ptrofs.modulus ->
  array_pred d 0 z
     (fun i _ p =>
      memory_block sh (sizeof t) (offset_val (sizeof t * i) p))
             (list_repeat (Z.to_nat z) d)
     (Vptr b (Ptrofs.repr ofs))  =
  memory_block sh (sizeof t * z) (Vptr b (Ptrofs.repr ofs)).
Proof.
  intros.
  rewrite memory_block_array_pred.
  f_equal. f_equal. omega. f_equal. f_equal. rewrite Z.mul_0_r. omega.
  rewrite Z.mul_0_r. split; omega. omega.
  rewrite Z.sub_0_r. auto. rewrite Zlength_list_repeat', Z2Nat.id by omega.
  omega.
Qed.

Lemma memory_block_struct_pred: forall sh m sz {A} (v: compact_prod (map A m)) b ofs,
  (m = nil -> sz = 0) ->
  members_no_replicate m = true ->
  sizeof_struct cenv_cs 0 m <= sz < Ptrofs.modulus ->
  0 <= ofs /\ ofs + sz < Ptrofs.modulus ->
  struct_pred m
   (fun it _ p =>
     (memory_block sh (field_offset_next cenv_cs (fst it) m sz - field_offset cenv_cs (fst it) m))
     (offset_val (field_offset cenv_cs (fst it) m) p)) v (Vptr b (Ptrofs.repr ofs)) =
  memory_block sh sz (Vptr b (Ptrofs.repr ofs)).
Proof.
  unfold field_offset, Ctypes.field_offset, field_offset_next.
  intros sh m sz A v b ofs NIL_CASE NO_REPLI; intros.
  destruct m as [| (i0, t0) m].
  1: rewrite (NIL_CASE eq_refl), memory_block_zero; simpl; normalize.
  assert (align 0 (alignof t0) = 0) by apply align_0, alignof_pos.
  revert H0; pattern ofs at 1 4; replace ofs with (ofs + align 0 (alignof t0)) by omega; intros.
  revert H; pattern sz at 2 4; replace sz with (sz - align 0 (alignof t0)) by omega; intros.
  pattern 0 at 1; rewrite <- H1.
  clear NIL_CASE H1.
  revert H H0; generalize 0 at 1 2 4 5 6 8 10 11; revert i0 t0 v NO_REPLI;
  induction m as [| (i1, t1) m]; intros.
  + simpl.
    if_tac; [| congruence].
    solve_mod_modulus.
    reflexivity.
  + match goal with
    | |- struct_pred ((i0, t0) :: (i1, t1) :: m) ?P v ?p = _ =>
           change (struct_pred ((i0, t0) :: (i1, t1) :: m) P v p) with
             (P (i0, t0) (fst v) p * struct_pred ((i1, t1) :: m) P (snd v) p);
           simpl (P (i0, t0) (fst v) p)
    end.
    if_tac; [| congruence].
    solve_mod_modulus.
    erewrite struct_pred_ext.
    - rewrite members_no_replicate_ind in NO_REPLI; destruct NO_REPLI as [NOT_IN NO_REPLI].
      rewrite IHm with (z := align z (alignof t0) + sizeof t0);
        [| now auto
         | simpl in H |- *; pose_align_le; pose_sizeof_pos; omega
         | pose_align_le; pose_sizeof_pos; omega].
      replace (ofs + align (align z (alignof t0) + sizeof t0) (alignof t1)) with
        (ofs + align z (alignof t0) +
         (align (align z (alignof t0) + sizeof t0) (alignof t1) -
          align z (alignof t0))) by omega.
      rewrite <- memory_block_split by
        (simpl in H; revert H; pose_align_le; pose_sizeof_pos; intros; omega).
      f_equal; omega.
    - rewrite members_no_replicate_ind in NO_REPLI; destruct NO_REPLI as [NOT_IN NO_REPLI].
      auto.
    - intros. instantiate (1 := (snd v)).
      solve_mod_modulus.
      unfold fst.
      pose proof in_members_tail_no_replicate _ _ _ _ NO_REPLI H2.
      rewrite (neq_field_offset_rec_cons cenv_cs i i0 t0) by auto.
      rewrite (neq_field_offset_next_rec_cons cenv_cs i i0 t0) by auto.
      reflexivity.
Qed.

Lemma memory_block_union_pred: forall sh m sz {A} (v: compact_sum (map A m)) b ofs,
  (m = nil -> sz = 0) ->
  union_pred m (fun it _ => memory_block sh sz) v (Vptr b (Ptrofs.repr ofs)) =
  memory_block sh sz (Vptr b (Ptrofs.repr ofs)).
Proof.
  intros sh m sz A v b ofs NIL_CASE; intros.
  destruct m as [| (i0, t0) m].
  1: rewrite (NIL_CASE eq_refl), memory_block_zero; simpl; normalize.
  clear NIL_CASE.
  revert i0 t0 v; induction m as [| (i1, t1) m]; intros.
  + simpl; auto.
  + destruct v.
    - simpl; auto.
    - apply IHm.
Qed.

End MEMORY_BLOCK_AGGREGATE.

Module aggregate_pred.

Open Scope Z.
Open Scope logic.

Definition array_pred: forall {A: Type} (d:A) (lo hi: Z) (P: Z -> A -> val -> mpred) (v: list A),
    val -> mpred := @array_pred.

Definition struct_pred: forall (m: members) {A: ident * type -> Type} (P: forall it, A it -> val -> mpred) (v: compact_prod (map A m)) (p: val), mpred := @struct_pred.

Definition union_pred: forall (m: members) {A: ident * type -> Type} (P: forall it, A it -> val -> mpred) (v: compact_sum (map A m)) (p: val), mpred := @union_pred.

Definition array_Prop: forall {A: Type} (d:A) (lo hi: Z) (P: Z -> A -> Prop) (v: list A), Prop := @array_Prop.

Definition struct_Prop: forall (m: members) {A: ident * type -> Type} (P: forall it, A it -> Prop) (v: compact_prod (map A m)), Prop := @struct_Prop.

Definition union_Prop: forall (m: members) {A: ident * type -> Type} (P: forall it, A it -> Prop) (v: compact_sum (map A m)), Prop := union_Prop.

Definition array_pred_len_0: forall {A} (d:A) lo hi P p,
  hi = lo ->
  array_pred d lo hi P nil p = emp
:= @array_pred_len_0.

Definition array_pred_len_1: forall {A} (d:A) i P v p,
  array_pred d i (i + 1) P (v :: nil) p =  P i v p
:= @array_pred_len_1.

Definition split_array_pred: forall  {A} (d:A) lo mid hi P v p,
  lo <= mid <= hi ->
  Zlength v = (hi-lo) ->
  array_pred d lo hi P v p =
  array_pred d lo mid P (sublist 0 (mid-lo) v) p *
  array_pred d mid hi P (sublist (mid-lo) (hi-lo) v) p
:= @split_array_pred.

Definition array_pred_shift: forall {A} (d:A) lo hi lo' hi' mv P' P v p,
  lo - lo' = mv ->
  hi - hi' = mv ->
  (forall i i', lo <= i < hi -> i - i' = mv -> P' i' (Znth (i - lo) v d) p = P i (Znth (i - lo) v d) p) ->
  array_pred d lo' hi' P' v p = array_pred d lo hi P v p
:= @array_pred_shift.

Definition array_pred_ext_derives:
  forall {A B} (dA:A) (dB:B) lo hi P0 P1 v0 v1 p,
  (Zlength v0 = hi - lo -> Zlength v1 = hi - lo) ->
  (forall i, lo <= i < hi ->
      P0 i (Znth (i-lo) v0 dA) p |-- P1 i (Znth (i-lo) v1 dB) p) ->
  array_pred dA lo hi P0 v0 p |-- array_pred dB lo hi P1 v1 p
:= @array_pred_ext_derives.

Definition array_pred_ext:
  forall {A B} (dA:A) (dB:B) lo hi P0 P1 v0 v1 p,
  Zlength v0 = Zlength v1 ->
  (forall i, lo <= i < hi ->
     P0 i (Znth (i - lo) v0 dA) p = P1 i (Znth (i - lo) v1 dB) p) ->
  array_pred dA lo hi P0 v0 p = array_pred dB lo hi P1 v1 p
:= @array_pred_ext.

Definition at_offset_array_pred: forall {A} (d:A) lo hi P v ofs p,
  at_offset (array_pred d lo hi P v) ofs p = array_pred d lo hi (fun i v => at_offset (P i v) ofs) v p
:= @at_offset_array_pred.

Definition array_pred_sepcon: forall  {A} (d:A) lo hi P Q v p,
  array_pred d lo hi P v p * array_pred d lo hi Q v p = array_pred d lo hi (P * Q) v p
:= @array_pred_sepcon.

Definition struct_pred_ramif: forall m {A} (P: forall it, A it -> val -> mpred) (i: ident) v p d,
  in_members i m ->
  members_no_replicate m = true ->
  struct_pred m P v p |--
    P _ (proj_struct i m v d) p *
     allp ((fun v0: _ => P _ v0 p) -* (fun v0: _ => struct_pred m P (upd_struct i m v v0) p))
:= @struct_pred_ramif.

Definition struct_pred_ext_derives:
  forall m {A0 A1} (P0: forall it, A0 it -> val -> mpred) (P1: forall it, A1 it -> val -> mpred) v0 v1 p,
  members_no_replicate m = true ->
  (forall i d0 d1, in_members i m ->
     P0 _ (proj_struct i m v0 d0) p |-- P1 _ (proj_struct i m v1 d1) p) ->
  struct_pred m P0 v0 p |-- struct_pred m P1 v1 p
:= @struct_pred_ext_derives.

Definition struct_pred_ext:
  forall m {A0 A1} (P0: forall it, A0 it -> val -> mpred) (P1: forall it, A1 it -> val -> mpred) v0 v1 p,
  members_no_replicate m = true ->
  (forall i d0 d1, in_members i m ->
     P0 _ (proj_struct i m v0 d0) p = P1 _ (proj_struct i m v1 d1) p) ->
  struct_pred m P0 v0 p = struct_pred m P1 v1 p
:= @struct_pred_ext.

Definition at_offset_struct_pred: forall m {A} (P: forall it, A it -> val -> mpred) v p ofs,
  at_offset (struct_pred m P v) ofs p = struct_pred m (fun it v => at_offset (P it v) ofs) v p
:= @at_offset_struct_pred.

Definition andp_struct_pred: forall m {A} (P: forall it, A it -> val -> mpred) v p Q,
  corable Q ->
  Q && struct_pred m P v p =
  match m with
  | nil => Q && emp
  | _ => struct_pred m (fun it v p => Q && P it v p) v p
  end
:= @corable_andp_struct_pred.

Definition struct_pred_sepcon: forall m {A} (P Q: forall it, A it -> val -> mpred) v p,
  struct_pred m P v p * struct_pred m Q v p = struct_pred m (fun it => P it * Q it) v p
:= @struct_pred_sepcon.

Definition union_pred_ramif: forall m {A} (P: forall it, A it -> val -> mpred) (i: ident) v p d,
  (forall i' (v': A (i', field_type i' m)), in_members i' m -> P _ v' p |-- P _ d p) ->
  in_members i m ->
  members_no_replicate m = true ->
  union_pred m P v p |--
    P _ (proj_union i m v d) p *
     allp ((fun v0: _ => P _ v0 p) -* (fun v0 =>union_pred m P (upd_union i m v v0) p))
:= @union_pred_ramif.

Definition union_pred_ext_derives:
  forall m {A0 A1} (P0: forall it, A0 it -> val -> mpred) (P1: forall it, A1 it -> val -> mpred) v0 v1 p,
  members_no_replicate m = true ->
  (forall it, members_union_inj v0 it <-> members_union_inj v1 it) ->
  (forall i d0 d1, members_union_inj v0 (i, field_type i m) -> members_union_inj v1 (i, field_type i m) ->
     P0 _ (proj_union i m v0 d0) p |-- P1 _ (proj_union i m v1 d1) p) ->
  union_pred m P0 v0 p |-- union_pred m P1 v1 p
:= @union_pred_ext_derives.

Definition union_pred_ext:
  forall m {A0 A1} (P0: forall it, A0 it -> val -> mpred) (P1: forall it, A1 it -> val -> mpred) v0 v1 p,
  members_no_replicate m = true ->
  (forall it, members_union_inj v0 it <-> members_union_inj v1 it) ->
  (forall i d0 d1, members_union_inj v0 (i, field_type i m) -> members_union_inj v1 (i, field_type i m) ->
     P0 _ (proj_union i m v0 d0) p = P1 _ (proj_union i m v1 d1) p) ->
  union_pred m P0 v0 p = union_pred m P1 v1 p
:= @union_pred_ext.

Definition at_offset_union_pred: forall m {A} (P: forall it, A it -> val -> mpred) v p ofs,
  at_offset (union_pred m P v) ofs p = union_pred m (fun it v => at_offset (P it v) ofs) v p
:= at_offset_union_pred.

Definition andp_union_pred: forall m {A} (P: forall it, A it -> val -> mpred) v p Q,
  Q && union_pred m P v p =
  match m with
  | nil => Q && emp
  | _ => union_pred m (fun it v p => Q && P it v p) v p
  end
:= @andp_union_pred.

Definition union_pred_sepcon: forall m {A} (P Q: forall it, A it -> val -> mpred) v p,
  union_pred m P v p * union_pred m Q v p = union_pred m (fun it => P it * Q it) v p
:= @union_pred_sepcon.

Definition struct_Prop_compact_prod_gen: forall m (F: ident * type -> Type) (P: forall it, F it -> Prop) (f: forall it, F it),
  members_no_replicate m = true ->
  (forall i, in_members i m -> P (i, field_type i m) (f (i, field_type i m))) ->
  struct_Prop m P (compact_prod_gen f m)
:= @struct_Prop_compact_prod_gen.

Definition struct_Prop_proj: forall m (F: ident * type -> Type) (P: forall it, F it -> Prop) v i d,
  in_members i m ->
  struct_Prop m P v ->
  P (i, field_type i m) (proj_struct i m v d)
:= @struct_Prop_proj.

Definition union_Prop_compact_sum_gen: forall m (F: ident * type -> Type) (P: forall it, F it -> Prop) (f: forall it, F it),
  members_no_replicate m = true ->
  (forall i, in_members i m -> P (i, field_type i m) (f (i, field_type i m))) ->
  union_Prop m P (compact_sum_gen (fun _ => true) f m)
:= @union_Prop_compact_sum_gen.

Definition union_Prop_proj: forall m (F: ident * type -> Type) (P: forall it, F it -> Prop) v i d,
  members_union_inj v (i, field_type i m) ->
  union_Prop m P v ->
  P (i, field_type i m) (proj_union i m v d)
:= @union_Prop_proj.

Definition array_pred_local_facts: forall {A} (d: A) lo hi P v p Q,
  (forall i x, lo <= i < hi -> P i x p |-- !! Q x) ->
  array_pred d lo hi P v p |-- !! (Zlength v = hi - lo /\ Forall Q v)
:= @array_pred_local_facts.

Definition struct_pred_local_facts: forall m {A} (P: forall it, A it -> val -> mpred)v p (R: forall it, A it -> Prop),
  members_no_replicate m = true ->
  (forall i v0, in_members i m -> P (i, field_type i m) v0 p |-- !! R _ v0) ->
  struct_pred m P v p |-- !! struct_Prop m R v
:= @struct_pred_local_facts.

Definition union_pred_local_facts: forall m {A} (P: forall it, A it -> val -> mpred)v p (R: forall it, A it -> Prop),
  members_no_replicate m = true ->
  (forall i v0, in_members i m -> P (i, field_type i m) v0 p |-- !! R _ v0) ->
  union_pred m P v p |-- !! union_Prop m R v
:= @union_pred_local_facts.

End aggregate_pred.

Require Import VST.floyd.reptype_lemmas.

(******************************************

Auxiliary predicates

******************************************)

Section AUXILIARY_PRED.

Context {cs: compspecs}.

Variable sh: share.

Definition struct_data_at_rec_aux (m m0: members) (sz: Z) (P: ListType (map (fun it => reptype (field_type (fst it) m0) -> (val -> mpred)) m)) (v: compact_prod (map (fun it => reptype (field_type (fst it) m0)) m)) : (val -> mpred).
Proof.
  destruct m as [| (i0, t0) m]; [exact (fun _ => emp) |].
  revert i0 t0 v P; induction m as [| (i0, t0) m]; intros ? ? v P.
  + simpl in v, P.
    inversion P; subst.
    exact (withspacer sh
            (field_offset cenv_cs i0 m0 + sizeof (field_type i0 m0))
            (field_offset_next cenv_cs i0 m0 sz)
            (at_offset (a v) (field_offset cenv_cs i0 m0))).
  + simpl in v, P.
    destruct (ident_eq i1 i1); [| congruence].
    inversion P; subst.
    exact (withspacer sh
            (field_offset cenv_cs i1 m0 + sizeof (field_type i1 m0))
            (field_offset_next cenv_cs i1 m0 sz)
            (at_offset (a (fst v)) (field_offset cenv_cs i1 m0)) * IHm i0 t0 (snd v) b)%logic.
Defined.

Definition union_data_at_rec_aux (m m0: members) (sz: Z) (P: ListType (map (fun it => reptype (field_type (fst it) m0) -> (val -> mpred)) m)) (v: compact_sum (map (fun it => reptype (field_type (fst it) m0)) m)) : (val -> mpred).
Proof.
  destruct m as [| (i0, t0) m]; [exact (fun _ => emp) |].
  revert i0 t0 v P; induction m as [| (i0, t0) m]; intros ? ? v P.
  + simpl in v, P.
    inversion P; subst.
    exact (withspacer sh (sizeof (field_type i0 m0)) sz (a v)).
  + simpl in v, P.
    inversion P; subst.
    destruct v as [v | v].
    - exact (withspacer sh (sizeof (field_type i1 m0)) sz (a v)).
    - exact (IHm i0 t0 v b).
Defined.

Lemma struct_data_at_rec_aux_spec: forall m m0 sz v P,
  struct_data_at_rec_aux m m0 sz
   (ListTypeGen
     (fun it => reptype (field_type (fst it) m0) -> val -> mpred)
     P m) v =
  struct_pred m
   (fun it v =>
      withspacer sh
       (field_offset cenv_cs (fst it) m0 + sizeof (field_type (fst it) m0))
       (field_offset_next cenv_cs (fst it) m0 sz)
       (at_offset (P it v) (field_offset cenv_cs (fst it) m0))) v.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [reflexivity |].
  revert i0 t0 v; induction m as [| (i0, t0) m]; intros.
  + simpl; reflexivity.
  + replace
     (struct_data_at_rec_aux ((i1, t1) :: (i0, t0) :: m) m0 sz
     (ListTypeGen (fun it : ident * type => reptype (field_type (fst it) m0) -> val -> mpred)
        P ((i1, t1) :: (i0, t0) :: m)) v) with
     (withspacer sh
       (field_offset cenv_cs i1 m0 + sizeof (field_type i1 m0))
         (field_offset_next cenv_cs i1 m0 sz)
           (at_offset (P (i1, t1) (fst v)) (field_offset cenv_cs i1 m0)) *
      struct_data_at_rec_aux ((i0, t0) :: m) m0 sz
     (ListTypeGen (fun it : ident * type => reptype (field_type (fst it) m0) -> val -> mpred)
        P ((i0, t0) :: m)) (snd v))%logic.
    - rewrite IHm.
      reflexivity.
    - simpl.
      destruct (ident_eq i1 i1); [| congruence].
      reflexivity.
Qed.

Lemma union_data_at_rec_aux_spec: forall m m0 sz v P,
  union_data_at_rec_aux m m0 sz
   (ListTypeGen
     (fun it => reptype (field_type (fst it) m0) -> val -> mpred)
     P m) v =
  union_pred m
   (fun it v =>
      withspacer sh
       (sizeof (field_type (fst it) m0))
       sz
       (P it v)) v.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [reflexivity |].
  revert i0 t0 v; induction m as [| (i0, t0) m]; intros.
  + simpl. unfold union_pred. simpl. reflexivity.
  + destruct v as [v | v].
    - reflexivity.
    - match goal with
      | _ => apply IHm
      | _ => simpl ; f_equal ; apply IHm
      end.
Qed.

Definition struct_value_fits_aux (m m0: members)
      (P: ListType (map (fun it => reptype (field_type (fst it) m0) -> Prop) m))
      (v: compact_prod (map (fun it => reptype (field_type (fst it) m0)) m)) : Prop.
Proof.
  destruct m as [| (i0, t0) m]; [exact True |].
  revert i0 t0 v P; induction m as [| (i0, t0) m]; intros ? ? v P.
  + simpl in v, P.
    inversion P; subst.
    apply (a v).
  + simpl in v, P.
    destruct (ident_eq i1 i1); [| congruence].
    inversion P; subst.
    apply (a (fst v) /\ IHm i0 t0 (snd v) b).
Defined.

Definition union_value_fits_aux (m m0: members)
      (P: ListType (map (fun it => reptype (field_type (fst it) m0) -> Prop) m))
      (v: compact_sum (map (fun it => reptype (field_type (fst it) m0)) m)) : Prop.
Proof.
  destruct m as [| (i0, t0) m]; [exact True |].
  revert i0 t0 v P; induction m as [| (i0, t0) m]; intros ? ? v P.
  + simpl in v, P.
    inversion P; subst.
    exact (a v).
  + simpl in v, P.
    inversion P; subst.
    destruct v as [v | v].
    - exact (a v).
    - exact (IHm i0 t0 v b).
Defined.

Lemma struct_value_fits_aux_spec: forall m m0 v P,
  struct_value_fits_aux m m0
   (ListTypeGen
     (fun it => reptype (field_type (fst it) m0) -> Prop)
     P m) v =
  struct_Prop m P v.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [reflexivity |].
  revert i0 t0 v; induction m as [| (i0, t0) m]; intros.
  + simpl; reflexivity.
  + replace
     (struct_value_fits_aux ((i1, t1) :: (i0, t0) :: m) m0
     (ListTypeGen (fun it : ident * type => reptype (field_type (fst it) m0) -> Prop)
        P ((i1, t1) :: (i0, t0) :: m)) v) with
     (P (i1, t1) (fst v) /\  struct_value_fits_aux ((i0, t0) :: m) m0
     (ListTypeGen (fun it : ident * type => reptype (field_type (fst it) m0) -> Prop)
        P ((i0, t0) :: m)) (snd v)).
    - rewrite IHm.
      reflexivity.
    - simpl.
      destruct (ident_eq i1 i1); [| congruence].
      reflexivity.
Qed.

Lemma union_value_fits_aux_spec: forall m m0 v P,
  union_value_fits_aux m m0
   (ListTypeGen
     (fun it => reptype (field_type (fst it) m0) -> Prop)
     P m) v =
  union_Prop m P v.
Proof.
  intros.
  destruct m as [| (i0, t0) m]; [reflexivity |].
  revert i0 t0 v; induction m as [| (i0, t0) m]; intros.
  + simpl. unfold union_Prop. simpl. reflexivity.
  + destruct v as [v | v].
    - reflexivity.
    - match goal with
      | _ => apply IHm
      | _ => simpl ; f_equal ; apply IHm
      end.
Qed.

End AUXILIARY_PRED.

Module auxiliary_pred.

Import aggregate_pred.

Definition struct_data_at_rec_aux:
   forall {cs: compspecs} (sh: share) (m m0: members) (sz: Z) (P: ListType (map (fun it => reptype (field_type (fst it) m0) -> (val -> mpred)) m)) (v: compact_prod (map (fun it => reptype (field_type (fst it) m0)) m)), (val -> mpred)
:= @struct_data_at_rec_aux.

Definition union_data_at_rec_aux:
  forall {cs: compspecs} (sh: share) (m m0: members) (sz: Z) (P: ListType (map (fun it => reptype (field_type (fst it) m0) -> (val -> mpred)) m)) (v: compact_sum (map (fun it => reptype (field_type (fst it) m0)) m)), (val -> mpred)
:= @union_data_at_rec_aux.

Definition struct_data_at_rec_aux_spec: forall {cs: compspecs} (sh: share) m m0 sz v P,
  struct_data_at_rec_aux sh m m0 sz
   (ListTypeGen
     (fun it => reptype (field_type (fst it) m0) -> val -> mpred)
     P m) v =
  struct_pred m
   (fun it v =>
      withspacer sh
       (field_offset cenv_cs (fst it) m0 + sizeof (field_type (fst it) m0))
       (field_offset_next cenv_cs (fst it) m0 sz)
       (at_offset (P it v) (field_offset cenv_cs (fst it) m0))) v
:= @struct_data_at_rec_aux_spec.

Definition union_data_at_rec_aux_spec: forall {cs: compspecs} sh m m0 sz v P,
  union_data_at_rec_aux sh m m0 sz
   (ListTypeGen
     (fun it => reptype (field_type (fst it) m0) -> val -> mpred)
     P m) v =
  union_pred m
   (fun it v =>
      withspacer sh
       (sizeof (field_type (fst it) m0))
       sz
       (P it v)) v
:= @union_data_at_rec_aux_spec.

Definition struct_value_fits_aux:
  forall {cs: compspecs} (m m0: members)
      (P: ListType (map (fun it => reptype (field_type (fst it) m0) -> Prop) m))
      (v: compact_prod (map (fun it => reptype (field_type (fst it) m0)) m)), Prop
:= @struct_value_fits_aux.

Definition union_value_fits_aux:
  forall {cs: compspecs} (m m0: members)
      (P: ListType (map (fun it => reptype (field_type (fst it) m0) -> Prop) m))
      (v: compact_sum (map (fun it => reptype (field_type (fst it) m0)) m)), Prop
:= @union_value_fits_aux.

Definition struct_value_fits_aux_spec: forall {cs: compspecs} m m0 v P,
  struct_value_fits_aux m m0
   (ListTypeGen
     (fun it => reptype (field_type (fst it) m0) -> Prop)
     P m) v =
  struct_Prop m P v
:= @struct_value_fits_aux_spec.

Definition union_value_fits_aux_spec: forall {cs: compspecs} m m0 v P,
  union_value_fits_aux m m0
   (ListTypeGen
     (fun it => reptype (field_type (fst it) m0) -> Prop)
     P m) v =
  union_Prop m P v
:= @union_value_fits_aux_spec.

Definition memory_block_array_pred:
  forall {cs: compspecs} (A : Type) (d : A) sh t z b ofs,
  0 <= z ->
  0 <= ofs /\ ofs + sizeof t * z < Ptrofs.modulus ->
  array_pred d 0 z
     (fun i _ p =>
      memory_block sh (sizeof t)
        (offset_val (sizeof t * i) p)) (list_repeat (Z.to_nat z) d)
     (Vptr b (Ptrofs.repr ofs))  =
  memory_block sh (sizeof t * z) (Vptr b (Ptrofs.repr ofs))
:= @memory_block_array_pred'.

Definition memory_block_struct_pred:
  forall {cs: compspecs} sh m sz {A} (v: compact_prod (map A m)) b ofs,
  (m = nil -> sz = 0) ->
  members_no_replicate m = true ->
  sizeof_struct cenv_cs 0 m <= sz < Ptrofs.modulus ->
  0 <= ofs /\ ofs + sz < Ptrofs.modulus ->
  struct_pred m
   (fun it _ p =>
     (memory_block sh (field_offset_next cenv_cs (fst it) m sz - field_offset cenv_cs (fst it) m))
     (offset_val (field_offset cenv_cs (fst it) m) p)) v (Vptr b (Ptrofs.repr ofs)) =
  memory_block sh sz (Vptr b (Ptrofs.repr ofs))
:= @memory_block_struct_pred.

Definition memory_block_union_pred:
  forall sh m sz {A} (v: compact_sum (map A m)) b ofs,
  (m = nil -> sz = 0) ->
  union_pred m (fun it _ => memory_block sh sz) v (Vptr b (Ptrofs.repr ofs)) =
  memory_block sh sz (Vptr b (Ptrofs.repr ofs))
:= @memory_block_union_pred.

End auxiliary_pred.


(* SPDX-License-Identifier: GPL-2.0 *)
Require Import Coqlib.
Require Import Maps.
Require Import Integers.
Require Import Values.
Require Import GenSem.
Require Import Maps.
Require Import Integers.
Require Import Values.
Require Import RealParams.
Require Import GenSem.
Require Import Clight.
Require Import CDataTypes.
Require Import Ctypes.
Require Import PrimSemantics.
Require Import CompatClightSem.
Require Import liblayers.compcertx.Stencil.
Require Import liblayers.compat.CompatLayers.
Require Import liblayers.compat.CompatGenSem.

Require Import AbstractMachine.Spec.
Require Import RData.
Require Import MmioSPTOps.Layer.
Require Import Constants.
Require Import HypsecCommLib.

Local Open Scope Z_scope.

Section MemRegionSpec.

  Fixpoint mem_region_search_loop (n: nat) (addr: Z) (i: Z) (res: Z) (adt: RData) :=
    match n with
    | O => Some (i, res)
    | S n' =>
      match mem_region_search_loop n' addr i res adt with
      | Some (i', res') =>
        rely is_memblock i'; rely is_int res';
        when' base == get_mem_region_base_spec i' adt;
        rely is_addr base;
        when' size == get_mem_region_size_spec i' adt;
        rely is_int64 size; rely is_addr (base + size);
        if (base <=? addr) && (addr <? base + size) then
          Some (i'+1, i')
        else
          Some (i'+1, res')
      | _ => None
      end
    end.

  Definition mem_region_search_spec (addr: Z64) (adt: RData) : option Z :=
    match addr with
    | VZ64 addr =>
      when total_regions == get_mem_region_cnt_spec adt;
      rely is_memblock total_regions; rely is_addr addr;
      match mem_region_search_loop (Z.to_nat total_regions) addr 0 INVALID adt with
      | Some (i', res') =>
        rely is_memblock i'; rely is_int res'; Some res'
      | _ => None
      end
    end.

End MemRegionSpec.

Section MemRegionSpecLow.

  Context `{real_params: RealParams}.

  Notation LDATA := RData.

  Notation LDATAOps := (cdata (cdata_ops := MmioSPTOps_ops) LDATA).

  Inductive mem_region_search_spec_low_step `{StencilOps} `{Mem.MemoryModelOps} `{UseMemWithData mem}:
    sextcall_sem (mem := mwd LDATAOps) :=
  | mem_region_search_spec_low_intro s (WB: _ -> Prop) m'0 labd addr res
      (Hinv: high_level_invariant labd)
      (Hspec: mem_region_search_spec (VZ64 (Int64.unsigned addr)) labd = Some (Int.unsigned res)):
      mem_region_search_spec_low_step s WB ((Vlong addr)::nil) (m'0, labd) (Vint res) (m'0, labd).

  Section WITHMEM.

    Context `{Hstencil: Stencil}.
    Context `{Hmem: Mem.MemoryModelX}.
    Context `{Hmwd: UseMemWithData mem}.

    Definition mem_region_search_spec_low: compatsem LDATAOps :=
      csem mem_region_search_spec_low_step (type_of_list_type (Tint64::nil)) Tint32.

  End WITHMEM.

End MemRegionSpecLow.


Require Import CSet Le Var.

Require Import Plus Util AllInRel Map CSet OptionR MoreList.
Require Import Val Var Envs IL Annotation Infra.Lattice RenamedApart.
Require Import DecSolve Analysis Filter Terminating.
Require Import Analysis AnalysisForwardSSA FiniteFixpointIteration.
Require Import Reachability Subterm AnnotationLattice DomainSSA.

Set Implicit Arguments.

Local Arguments proj1_sig {A} {P} e.
Local Arguments length {A} e.
Local Arguments forward {sT} {D} {H} {H0} exp_transf reach_transf ZL ZLIncl st ST d anr.

Opaque poLe.

Ltac simpl_forward_setTopAnn :=
  match goal with
  | [H : ann_R _ (snd (fst (@forward ?sT ?D ?PO ?JSL ?f ?fr ?ZL ?ZLIncl
                                     ?s ?ST ?d ?r))) ?r' |- _ ] =>
    let X := fresh "HEQ" in
    match goal with
    | [ H' : getAnn r = getAnn r' |- _ ] => fail 1
    | _ => first
            [ unify r r'; fail 1
            | exploit (@forward_getAnn sT D PO JSL f fr ZL ZLIncl s ST d r r' H) as X;
              subst]
    end
  end; subst; try eassumption;
  try rewrite getAnn_setTopAnn in *;
  repeat rewrite setTopAnn_eta' in *.

Smpl Add 130 simpl_forward_setTopAnn : inv_trivial.

Lemma domupdd_eq (D : Type) `{PartialOrder D} (U : ⦃var⦄) (d:VDom U D) x v pf
  : domenv (proj1_sig d) x === v
    -> d ≣ @domupdd _ _ d x v pf.
Proof.
  eapply poEq_domupd; eauto.
Qed.

Opaque poEq.
Opaque poLe.

Ltac PIR2_eq_simpl :=
  match goal with
  | [ H : PIR2 (ann_R eq) _ _ |- _ ] =>
    eapply PIR2_R_impl with (R':=eq) in H;
    [|intros ? ?; rewrite <- ann_R_eq; let A := fresh "A" in intros A; apply A]
  | [ H : (@poEq (list (ann bool)) _) _ _ |- _ ] =>
    eapply PIR2_R_impl with (R':=eq) in H;
    [|intros ? ?; rewrite <- ann_R_eq; let A := fresh "A" in intros A; apply A]
  | [ H : PIR2 eq _ _ |- _ ] =>
    eapply PIR2_eq in H
  | [ H : PIR2 (@poEq bool _) _ _ |- _ ] =>
    eapply PIR2_eq in H
  | [ H : ann_R eq _ _ |- _ ] => rewrite ann_R_eq in H
  | [ H : _ = ?x |- _ ] => is_var x; rewrite H in *
  end.

Transparent poEq.

Lemma agree_comp_inv A `{OrderedType A} B (R:B->B->Prop) `{Transitive _ R} (f g : (A -> B) -> (A -> B)) Gf Gg
      (AGRf: forall d, agree_on R Gf d (f d))
      (AGRg: forall d, agree_on R Gg d (g d))
  : forall d, agree_on R (Gf ∩ Gg) d (f (g d)).
Proof.
  intros d. hnf; intros x IN.
  cset_tac'.
  rewrite <- (AGRf (g d) x); eauto.
  eapply AGRg; eauto.
Qed.

Lemma agree_comp_inv' A `{OrderedType A} B (R:B->B->Prop) `{Transitive _ R} (f g : (A -> B) -> (A -> B)) d D Gf Gg (disj: disj Gf Gg)
      (AGR:forall x, R (f (g d) x) (d x))
      (AGRf: forall d, agree_on R (D \ Gf) d (f d))
      (AGRg: forall d, agree_on R (D \ Gg) (g d) d)
  : agree_on R D (g d) d.
Proof.
  intros x IN.
  decide (x ∈ Gg).
  - assert (x ∉ Gf). cset_tac.
    specialize (AGRf (g d) x ).
    etransitivity. eapply AGRf. cset_tac.
    eapply AGR.
  - eapply AGRg. cset_tac.
Qed.

Lemma agree_comp_inv'' A `{OrderedType A} B (R:B->B->Prop) `{Transitive _ R}
      X (r : X -> (A -> B))
      (f g : X -> X) (d:X) D Gf Gg (disj: disj Gf Gg)
      (AGR:forall x, R (r (f (g d)) x) (r d x))
      (AGRf: forall d, agree_on R (D \ Gf) (r d) (r (f d)))
      (AGRg: forall d, agree_on R (D \ Gg) (r (g d)) (r d))
  : agree_on R D (r (g d)) (r d).
Proof.
  intros x IN.
  decide (x ∈ Gg).
  - assert (x ∉ Gf). cset_tac.
    specialize (AGRf (g d) x ).
    etransitivity. eapply AGRf. cset_tac.
    eapply AGR.
  - eapply AGRg. cset_tac.
Qed.

Lemma poEq_VDom D `{PartialOrder D} U (d d':VDom U D)
  : (forall G, agree_on poEq G (domenv (proj1_sig d)) (domenv (proj1_sig d')))
         <-> d ≣ d'.
Proof.
  intros. split; intros.
  - hnf; intros.
    eapply (H0 (singleton x)). cset_tac.
  - hnf; intros. eapply H0.
Qed.

(*
  eapply poEq_VDom; intros.
  decide (disj G (list_union (of_list ⊝ ZL))).
  - symmetry in disj1.
    eapply agree_comp_inv'' with
    (r:=fun d => domenv (proj1_sig d))
      (f:=fun d => fst (fst (forward f fr ZL ZLIncl t STt d ta)))
      (g:=fun d => fst (fst (forward f fr ZL ZLIncl s STs d sa))); try eapply disj1; eauto.
    hnf; intros. rewrite H1. eauto.
    + intros.
      eapply agree_on_incl.
      eapply forward_agree; eauto.
      instantiate (1:=G). revert d0. clear; cset_tac.
    + intros. symmetry.
      intros.
      eapply agree_on_incl.
      eapply forward_agree; eauto.
      instantiate (1:=G). revert d0. clear; cset_tac.
  -
 *)

Lemma forward_if_inv (sT:stmt) D `{JoinSemiLattice D}
      f fr ZL s t (d:VDom (occurVars sT) D) STt STs ZLIncl sa ta
      (EQ: fst (fst (forward f fr ZL ZLIncl t STt (fst (fst (forward f fr ZL ZLIncl s STs d sa))) ta)) ≣ d)
      (ANs:annotation s sa) (ANt:annotation t ta)
      (disj1:disj (definedVars s) (definedVars t))
      (disj2:disj (definedVars s ∪ definedVars t) (list_union (of_list ⊝ ZL)))
  : (fst (fst (forward f fr ZL ZLIncl s STs d sa))) ≣ d.
Proof.
  hnf; intros x.
  exploit (@forward_agree sT _ _ _ f fr ZL d (singleton x) s STs ZLIncl); eauto; dcr.
  decide (x ∈ (definedVars s ∪ list_union (of_list ⊝ ZL))).
  - specialize (EQ x).
    rewrite <- EQ.
    exploit (@forward_agree sT _ _ _ f fr ZL (fst (fst (forward f fr ZL ZLIncl s STs d sa))) (singleton x) t STt ZLIncl); eauto; dcr.
    decide (x ∈ list_union (of_list ⊝ ZL)).
    + assert (x ∉ definedVars t) by cset_tac.
      eapply poLe_antisymmetric.
      * specialize (H5 x). rewrite H5. reflexivity.
        cset_tac.
      * rewrite EQ. eapply H3. cset_tac.
    + specialize (H4 x).
      rewrite H4. reflexivity. cset_tac.
  - symmetry.
    eapply H2. cset_tac.
Qed.

Lemma forward_agree_ren sT D `{JoinSemiLattice D} f fr
      ZL AE G s (ST:subTerm s sT) ra ZLIncl anr
      (RA:renamedApart s ra) (AN:annotation s anr)
  : agree_on poEq (G \ (snd (getAnn ra) ∪ list_union (of_list ⊝ ZL)))
             (domenv (proj1_sig AE))
             (domenv (proj1_sig
                        (fst (fst (forward f fr ZL ZLIncl
                                            s ST AE anr))))) /\
    agree_on poLe (G \ (snd (getAnn ra)))
             (domenv (proj1_sig AE))
             (domenv (proj1_sig
                        (fst (fst (forward f fr ZL ZLIncl
                                            s ST AE anr))))).
Proof.
  rewrite <- renamedApart_occurVars; eauto.
  eapply forward_agree; eauto.
Qed.

Lemma forward_agree_def_ren sT D `{JoinSemiLattice D} ZL AE G s f fr
      (ST:subTerm s sT) ra ZLIncl anr pf
  (RA:renamedApart s ra) (AN:annotation s anr)
  : agree_on poEq (G \ (snd (getAnn ra) ∪ list_union (of_list ⊝ ZL)))
             (domenv AE)
             (domenv (proj1_sig
                        (fst (fst (forward f fr
                                            ZL ZLIncl
                                            s ST (exist _ AE pf) anr))))).
Proof.
  edestruct forward_agree_ren with (AE:=exist _ AE pf); dcr; eauto.
Qed.

Lemma forward_agree_def sT D `{JoinSemiLattice D} ZL AE G s f fr
      (ST:subTerm s sT) ra ZLIncl anr pf
  (RA:renamedApart s ra) (AN:annotation s anr)
  : agree_on poEq (G \ (definedVars s ∪ list_union (of_list ⊝ ZL)))
             (domenv AE)
             (domenv (proj1_sig
                        (fst (fst (forward f fr
                                            ZL ZLIncl
                                            s ST (exist _ AE pf) anr))))).
Proof.
  edestruct forward_agree with (AE:=exist _ AE pf); dcr; eauto.
Qed.

Lemma list_union_definedVars_renamedApart (F:list (params * stmt)) ra
      (RA:forall n Zs a,
          get F n Zs -> get ra n a -> renamedApart (snd Zs) a)
      (Len:❬F❭ = ❬ra❭)
  : list_union (snd ⊝ getAnn ⊝ ra) [=] list_union (definedVars ⊝ snd ⊝ F).
Proof.
  general induction Len; simpl; eauto.
  norm_lunion. rewrite IHLen; eauto using get.
  rewrite renamedApart_occurVars; eauto using get.
  reflexivity.
Qed.

Lemma forwardF_agree_get (sT:stmt) D `{JoinSemiLattice D} f fr
      (F : 〔params * stmt〕)
      (t : stmt)
      (ZL : 〔params〕)
      (ra : 〔ann (⦃var⦄ * ⦃var⦄)〕)
      (AE AE': VDom (occurVars sT) D) BL
      STF
      (ZLIncl : list_union (of_list ⊝ ZL) [<=] occurVars sT)
      (LenZL:❬ZL❭ >= ❬F❭)
      (sa : 〔ann bool〕)
      (ta : ann bool)  tra
      (RAt:renamedApart t tra) (AN:annotation t ta)
      (EQM :   (fst (fst (@forwardF sT (sTDom D) BL
                                (forward f fr ZL ZLIncl) F
                                sa
                                AE'
                                STF))) ≣ AE)
      (STt:subTerm t sT)
      (EQ: (fst (fst (forward f fr ZL ZLIncl t STt AE ta))) ≣ AE')
      (Disj1:disj (snd (getAnn tra)) (list_union (of_list ⊝ ZL)))
      (Disj2:disj (snd (getAnn tra)) (list_union (snd ⊝ getAnn ⊝ ra)))
      (Disj3:disj (list_union (of_list ⊝ ZL)) (list_union (snd ⊝ getAnn ⊝ ra)))
      (Disj5:disj (list_union (of_list ⊝ fst ⊝ F)) (list_union (snd ⊝ getAnn ⊝ ra)))
      (Disj6:PairwiseDisjoint.pairwise_ne disj (defVars ⊜ F ra))
      (Len2 : ❬F❭ = ❬ra❭)
      (Len1 : ❬F❭ = ❬sa❭)
      (AnnF : forall (n : nat) (s' : params * stmt) (sa' : ann bool),
         get sa n sa' -> get F n s' -> annotation (snd s') sa')
      (RA : forall (n : nat) (Zs : params * stmt) (a : ann (⦃var⦄ * ⦃var⦄)),
          get F n Zs -> get ra n a -> renamedApart (snd Zs) a)
      (fExt:forall (U : ⦃var⦄) (e : exp) (a0 a' : VDom U D),
          a0 ≣ a' -> forall b b' : bool, b ≣ b' -> f U b a0 e ≣ f U b' a' e)
      (frExt:forall (U : ⦃var⦄) (e : op) (a0 a' : VDom U D),
          a0 ≣ a' -> forall b b' : bool, b ≣ b' -> fr U b a0 e ≣ fr U b' a' e)
  : (fst (fst (forward f fr ZL ZLIncl t STt AE ta)) ≣ AE)
    /\  forall n Zs r (ST : subTerm (snd Zs) sT),
      get F n Zs ->
      get sa n r ->
      fst (fst (forward f fr ZL ZLIncl (snd Zs) ST AE r)) ≣ AE.
Proof.
  Opaque poEq.
  general induction Len1; simpl in *; eauto.
  - split; isabsurd. etransitivity; eauto.
  - destruct ra; simpl in *; isabsurd.
    revert Disj2 Disj3 Disj5. norm_lunion. intros Disj2 Disj3 Disj5.
    edestruct (IHLen1 sT _ _ _ f fr t ZL); eauto using get.
    + Transparent poEq.
      hnf; intros z.
      exploit (@forward_agree_ren sT _ _ _ f fr ZL AE (singleton z) t STt tra ZLIncl); eauto; dcr.
      exploit (@forward_agree_ren sT _ _ _ f fr ZL AE' (singleton z) (snd x) (STF 0 x (getLB XL x)) a ZLIncl); eauto using get; dcr.
      exploit (@forwardF_agree sT _ _ _ BL (singleton z) XL f fr YL ZL ZLIncl);
      eauto using get; dcr.
      * eauto with len.
      * intros. inv_get. inv_get. exploit (RA (S n)); eauto using get.
        eapply renamedApart_occurVars in H9; eauto.
        rewrite H9.
        eapply forward_agree_ren; eauto using get.
      * specialize (EQ z). specialize (EQM z).
    instantiate (3:=(fst (fst (forward f fr ZL ZLIncl (snd x) (STF 0 x (getLB _ _)) AE' y)))) in H6.
    instantiate (1:=(fun (n : nat) (s : params * stmt) (H : get XL n s) =>
                       STF (S n) s (getLS x H))) in H6.
        {
          decide (z ∈ snd (getAnn tra) ∪ list_union (of_list ⊝ ZL)).
          + eapply disj_union_inv in i. destruct i; dcr.
            * rewrite EQ.
              eapply H4.
              revert H8 H9 Disj2. clear.
              intros. cset_tac.
            * eapply poLe_antisymmetric.
              -- rewrite EQ.
                 eapply H5.
                 revert H8 Disj3; clear_all. intros. cset_tac.
              -- etransitivity; [| eapply H3].
                 rewrite <- EQM. specialize (H7 z).
                 eapply H7.
                 revert H8 Disj3.
                 rewrite list_union_definedVars_renamedApart; eauto using get.
                 clear_all; intros. cset_tac.
                 intros.
                 revert H9; clear_all; cset_tac.
            * eapply disj_2_incl; try eapply Disj1; eauto.
          + decide (z ∈ (list_union (defVars ⊜ XL ra))).
            * clear H7 H6.
              rewrite EQ. eapply H4.
              eapply (@defVars_drop_disj (x::XL) (a::ra) 0) in Disj6;
                eauto using get. simpl in *.
              unfold defVars in Disj6 at 1.
              rewrite list_union_defVars_decomp in *; eauto.
              revert n i Disj5 Disj6. clear_all.
              intros. cset_tac.
            * exploit (H2 z). revert n; clear_all; cset_tac.
              rewrite <- H1.
              rewrite <- EQM. symmetry. eapply H6.
              revert n n0.
              rewrite list_union_definedVars'; eauto.
              rewrite list_union_defVars_decomp; eauto.
              rewrite list_union_definedVars_renamedApart; eauto using get.
              clear_all; cset_tac.
        }
    + eapply disj_2_incl; eauto.
    + eapply disj_2_incl. eapply Disj3. clear_all; cset_tac.
    + eapply disj_incl. eapply Disj5.
      clear_all; cset_tac.
      clear_all; cset_tac.
    + hnf; intros. eapply Disj6; [|eauto using get|eauto using get]. omega.
    + intros; split; eauto.
      intros. inv H3; inv H4; eauto using get.
      assert (AEQ:AE === AE').
      { hnf; intros.
        specialize (EQ x). specialize (H1 x).
        rewrite <- EQ. rewrite H1. reflexivity.
      }
      assert (EQF:@forwardF sT (sTDom D) BL (forward f fr ZL ZLIncl) XL YL
                            (fst (fst (forward f fr ZL ZLIncl _ (STF 0 Zs (@getLB (params * stmt) XL Zs)) AE' r)))
                            (fun (n : nat) (s : params * stmt) (H : get XL n s) =>
                               STF (S n) s (getLS Zs H)) ===
                            forwardF BL (forward f fr ZL ZLIncl) XL YL
                            (fst (fst (forward f fr ZL ZLIncl _ ST AE r)))
                            (fun (n : nat) (s : params * stmt) (H : get XL n s) =>
                               STF (S n) s (getLS Zs H))
             ). {
        eapply forwardF_ext'; eauto.
        assert ((STF 0 Zs (@getLB (params * stmt) XL Zs)) = ST) by eapply subTerm_PI.
        subst.
        eapply forward_ext; eauto.
      }
      hnf; intros z.
      exploit (@forward_agree sT _ _ _ f fr ZL AE (singleton z) (snd Zs) ST ZLIncl); eauto using get; dcr.
      exploit (@forwardF_agree sT _ _ _  BL (singleton z) XL f fr YL ZL ZLIncl);
        eauto using get; dcr.
      eauto with len.
      intros. inv_get.
      eapply forward_agree; eauto using get.
      unfold domenv in *.
      instantiate (2:=(fst (fst (forward f fr ZL ZLIncl (snd Zs) ST AE r)))) in H9.
      decide (z ∈ snd (getAnn tra) ∪ list_union (of_list ⊝ ZL)).
      * eapply disj_union_inv in i. destruct i; dcr.
        -- symmetry.
           eapply (H6 z).
           revert H10 H11 Disj2.
           rewrite list_union_definedVars_renamedApart; eauto using get.
           rewrite renamedApart_occurVars; eauto using get.
           clear. intros. cset_tac.
        -- eapply poLe_antisymmetric.
           ++ etransitivity. Transparent poLe.
             eapply (H9 z).
             revert H10 Disj3.
             rewrite list_union_definedVars_renamedApart; eauto using get.
             clear_all. intros; cset_tac.
             rewrite <- (EQM z).
             hnf in EQF. dcr.
             hnf in H5. dcr.
             specialize (H13 z).
             rewrite H13. reflexivity.
           ++ eapply H7.
             rewrite renamedApart_occurVars; eauto using get.
             revert H10 H11 Disj3; clear_all. intros. cset_tac.
        -- eauto.
      * decide (z ∈ (list_union (defVars ⊜ XL ra))).
        -- symmetry.
           eapply H6.
           eapply (@defVars_drop_disj (Zs::XL) (a::ra) 0) in Disj6;
             eauto using get. simpl in *.
           unfold defVars in Disj6 at 1.
           rewrite list_union_defVars_decomp in Disj6; eauto.
           rewrite list_union_defVars_decomp in i; eauto.
           revert n i Disj5 Disj6.
           rewrite renamedApart_occurVars; eauto using get.
           clear_all. intros. cset_tac.
        -- rewrite (H8 z).
           rewrite <- (EQM z).
           hnf in EQF. dcr.
           hnf in H5. dcr.
           specialize (H11 z).
           rewrite H11. reflexivity.
           revert n n0.
           rewrite list_union_defVars_decomp; eauto.
           rewrite list_union_definedVars_renamedApart; eauto using get.
           rewrite list_union_definedVars'; eauto.
           clear_all; cset_tac.
Qed.

Opaque poEq.

Lemma forward_domupdd_eq (sT:stmt) D `{JoinSemiLattice D} ZL ZLIncl s f fr sa v x IN
      (d:VDom (occurVars sT) D) STs (AN:annotation s sa)
      (NOTIN:x ∉ definedVars s ∪ list_union (of_list ⊝ ZL))
      (EQ : fst (fst (forward f fr ZL ZLIncl s STs (@domupdd _ _ d x v IN) sa)) ≣ d)

  : fst (fst (forward f fr ZL ZLIncl s STs (@domupdd _ _ d x v IN) sa))
        ≣ domupdd d v IN.
Proof.
  exploit (@forward_agree sT _ _ _ f fr ZL (domupdd d v IN) (singleton x) s STs ZLIncl); eauto; dcr.
  exploit (H2 x).
  - cset_tac.
  - rewrite EQ.
    eapply domupdd_eq.
    exploit (EQ x).
    rewrite H4 in H1.
    rewrite <- H1. symmetry.
    unfold domenv, domupdd; simpl.
    rewrite domupd_var_eq. reflexivity. reflexivity.
Qed.

Ltac ST_pat :=
  match goal with
  | [ H : context [ forward _ _ _ ?ZLIncl _ ?ST _ _ ] |- _  ] =>
    try (first [ is_var ZLIncl; fail 1
               | let X := fresh "ZLIncl" in set (X:=ZLIncl) in * ]);
    first [ is_var ST; fail 1
          | let X := fresh "ST" in set (X:=ST) in * ]
  | [ H : context [ forwardF _ _ _ _ _ ?STF ] |- _  ] =>
    first [ is_var STF; fail 1
          | let X := fresh "STF" in set (X:=STF) in * ]
  end.

Hint Resolve funConstr_disj_Dt' funConstr_disj_ZL_getAnn disj_Dt_getAnn : ren.

Lemma poEq_refl D `{PartialOrder D} x
  : poEq x x.
Proof.
  reflexivity.
Qed.

Lemma poEq_sym D `{PartialOrder D} x y
  : poEq x y -> poEq y x.
Proof.
  intros. symmetry. eauto.
Qed.

Hint Immediate poEq_refl poEq_sym : po.

Instance join_respects_le A  `{JoinSemiLattice A}
  : Proper (poEq ==> poEq ==> poLe) join.
Proof.
  unfold Proper, respectful; intros.
  rewrite H1, H2. reflexivity.
Qed.

Definition reachability_sound (sT:stmt) D `{JoinSemiLattice D}
           f fr pr ZL BL s (d:VDom (occurVars sT) D) r (ST:subTerm s sT) ZLIncl
           (EQ:(fst (forward f fr ZL ZLIncl s ST d r)) ≣ (d,r)) ra
    (Ann: annotation s r) (RA:renamedApart s ra)
    (DefZL: labelsDefined s (length ZL))
    (DefBL: labelsDefined s (length BL))
    (BL_le: poLe (snd (forward f fr ZL ZLIncl s ST d r)) BL)
    (Disj:disj (list_union (of_list ⊝ ZL)) (snd (getAnn ra)))
    (frExt:forall U e (a a':VDom U D), a ≣ a' ->
        forall b b', b ≣ b' -> fr _ b a e ≣ fr _ b' a' e)
    (fExt:forall (U : ⦃var⦄) (e : exp) (a a' : VDom U D),
        a ≣ a' -> forall b b' : bool, b ≣ b' -> f U b a e ≣ f U b' a' e)
    (frSound1: forall e d r,
        ~ pr d e ⊑ ⎣ wTA false ⎦ -> uceq Sound r (fst (fr (occurVars sT) r d e)))
    (frSound2: forall e d r,
        ~ pr d e ⊑ ⎣ wTA true ⎦ -> uceq Sound r (snd (fr (occurVars sT) r d e)))
  : reachability (pr d) Sound BL s r.
Proof.
  general induction Ann; invt renamedApart; simpl in *; inv DefZL; inv DefBL;
    repeat let_case_eq; repeat simpl_pair_eqs; subst; simpl in *;
      simpl in *; inv_cleanup.
  - clear_trivial_eqs.
    econstructor; eauto.
    exploit forward_domupdd_eq; eauto.
    rewrite renamedApart_occurVars; eauto. pe_rewrite.
    set_simpl. eapply renamedApart_disj in H6; eauto.
    pe_rewrite. revert Disj H6; clear_all; cset_tac.
    rewrite EQ in H1. symmetry in H1.
    eapply forward_ext in H1; try reflexivity; eauto.
    rewrite H1 in *.
    eapply IHAnn; eauto.
    + split; simpl; eauto.
    + pe_rewrite. eapply disj_2_incl; eauto. cset_tac.
  - clear_trivial_eqs.
    set_simpl.
    exploit (forward_if_inv _ _ _ _ _ _ EQ); eauto.
    repeat rewrite renamedApart_occurVars; eauto;
      pe_rewrite; eauto with cset.
    repeat rewrite renamedApart_occurVars; eauto;
      pe_rewrite; eauto with cset.
    rewrite forward_ext in EQ; try eapply H1; try reflexivity; eauto.
    econstructor; eauto.
    + rewrite <- HEQ0. eapply frSound1.
    + rewrite <- HEQ. eapply frSound2.
    + eapply IHAnn1;
        eauto using @PIR2_zip_join_inv_left with len.
      * split; simpl; eauto.
      * pe_rewrite. eapply disj_2_incl; eauto.
    + eapply IHAnn2;
        eauto using @PIR2_zip_join_inv_left, @PIR2_zip_join_inv_right with len.
      * split; simpl; eauto.
        rewrite forward_ext; eauto.
      * eapply PIR2_zip_join_inv_right.
        rewrite <- BL_le. eapply PIR2_ojoin_zip. reflexivity.
        eapply poLe_refl. eapply forward_ext; eauto.
        symmetry; eauto with len.
      * pe_rewrite. eapply disj_2_incl; eauto.
  - edestruct get_in_range; eauto.
    edestruct get_in_range; try eapply H7; eauto.
    Transparent poLe. hnf in BL_le.
    edestruct PIR2_nth; eauto using ListUpdateAt.list_update_at_get_3; dcr.
    econstructor; simpl; eauto.
  - econstructor.
  - clear_trivial_eqs.
    eapply PIR2_get in H23; try eassumption. clear H22.
    exploit (snd_forwardF_inv _ _ _ _ _ _ _ H23); eauto with len.
    exploit (snd_forwardF_inv' _ _ _ _ _ _ _ H23); eauto with len.
    Transparent poEq. simpl poEq in H23.
    repeat PIR2_eq_simpl. repeat ST_pat.
    Opaque poEq.
    set (FWt:=(forward f fr (fst ⊝ s ++ ZL) ZLIncl0 t ST0 d ta)) in *.
    set (FWF:=forwardF (snd FWt) (forward f fr (fst ⊝ s ++ ZL) ZLIncl0)
                       s sa (fst (fst FWt)) STF) in *.

    assert (fst (fst (FWt)) ≣ d /\
            forall (n : nat) (Zs : params * stmt) (r : ann bool) (ST0 : subTerm (snd Zs) sT),
              get s n Zs ->
              get sa n r ->
              fst
                (fst
                   (forward f fr (fst ⊝ s ++ ZL) (ZLIncl_ext ZL eq_refl ST ZLIncl) (snd Zs) ST0 d r))
                ≣ d). {
      pe_rewrite. set_simpl.
      eapply forwardF_agree_get; eauto. eauto with len.
      rewrite <- EQ. unfold FWF. reflexivity.
      unfold FWt. reflexivity.
      pe_rewrite. eauto with ren.
      pe_rewrite.
      eapply disj_Dt_getAnn; eauto.
      eapply funConstr_disj_ZL_getAnn; eauto.
      eapply disj_1_incl.
      eapply funConstr_disj_ZL_getAnn; eauto.
      rewrite List.map_app. rewrite list_union_app.
      clear_all. cset_tac.
    } dcr.

    assert (forall (n : nat) (r : ann bool) (Zs : params * stmt),
       get sa n r ->
       get s n Zs ->
       forall STZs : subTerm (snd Zs) sT,
         (snd
            (fst
               (forward f fr (fst ⊝ s ++ ZL) ZLIncl0 (snd Zs) STZs d r))) ≣ r). {
      eapply (@snd_forwardF_inv_get) with (BL:=(snd FWt)); eauto.
      subst FWt; eauto with len.
      subst FWt; eauto with len.
      rewrite <- H5 at 2. unfold FWF.
      rewrite forwardF_ext'; try reflexivity; eauto.
    }
    econstructor; eauto.
    + eapply IHAnn; eauto.
      * split; eauto.
      * erewrite (take_eta ❬s❭) at 1. eapply PIR2_app; eauto.
        --change (PIR2 poLe) with (@poLe (list bool) _).
          assert (poLe (Take.take ❬s❭ (snd FWt)) (getAnn ⊝ sa)). {
            rewrite H.
            eapply (@joinTopAnn_map_inv).
            rewrite <- H4 at 2. reflexivity.
          }
          rewrite <- H4. subst FWF.
          subst FWt.
          rewrite H4.
          rewrite <- H5.
          rewrite <- (@forwardF_mon' sT D _ _ f fr); eauto.
        -- rewrite <- BL_le.
           eapply PIR2_drop.
           etransitivity;[|
                          eapply forwardF_mon; eauto].
           reflexivity.
           subst FWt. eauto with len.
      * pe_rewrite.
        set_simpl.
        rewrite List.map_app. rewrite list_union_app.
        eapply disj_union_left.
        -- symmetry.
           eapply funConstr_disj_Dt; eauto.
        -- symmetry. eapply disj_incl; eauto.
    + intros. inv_get. exploit H7; eauto.
      eapply H1; eauto.
      -- split; simpl; eauto.
      -- rewrite (take_eta ❬sa❭) at 1.
         eapply PIR2_app; eauto.
         ++ Transparent poEq.
           unfold poEq in H23. simpl in H23.
           unfold poEq in H23. simpl in H23.
           repeat PIR2_eq_simpl.
           eapply setTopAnn_map_inv in H23.
           rewrite <- H23. eapply PIR2_take.
           change (PIR2 poLe) with (@poLe (list bool) _).
           unfold FWF.
           rewrite forwardF_ext'; eauto; try reflexivity.
           eapply forwardF_PIR2; eauto.
           subst FWt. clear_all. eauto with len.
         ++ etransitivity; eauto.
           rewrite H. eapply PIR2_drop.
           change (PIR2 poLe) with (@poLe (list bool) _).
           unfold FWF.
           rewrite forwardF_ext'; eauto; try reflexivity.
           unfold poEq in H23. simpl in H23.
           unfold poEq in H23. simpl in H23.
           repeat PIR2_eq_simpl.
           eapply forwardF_PIR2; eauto.
           subst FWt. clear_all. eauto with len.
      -- set_simpl.
         eapply disj_2_incl.
         eapply funConstr_disj_ZL_getAnn; eauto with ren.
         eapply incl_list_union; eauto using zip_get.
         Grab Existential Variables.
         eauto.
Qed.


(*

  Copyright 2014 Cornell University
  Copyright 2015 Cornell University
  Copyright 2016 Cornell University
  Copyright 2017 Cornell University

  This file is part of VPrl (the Verified Nuprl project).

  VPrl is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  VPrl is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with VPrl.  If not, see <http://www.gnu.org/licenses/>.

  Websites: http://nuprl.org/html/verification/
            http://nuprl.org/html/Nuprl2Coq
            https://github.com/vrahli/NuprlInCoq

  Authors: Vincent Rahli

 *)

Require Export terms.

Lemma canonical_dec_op {o} :
  forall (x y : @CanonicalOp o), option (x = y).
Proof.
  introv.
  destruct x; destruct y; try (complete (left; reflexivity));
    match goal with
    | [ |- option (?x _ = ?x _) ] => idtac
    | _ => exact None
    end.

  - destruct (can_inj_deq c c0) as [d|d]; subst;[left|right];auto.

  - destruct (Z.eq_dec z z0) as [d|d]; subst;[left|right];auto.

  - right; auto.

  - destruct (String.string_dec s s0) as [d|d]; subst;[left|right]; auto.

  - assert (Deq (get_patom_set o)) as d by (destruct o; destruct patom; auto).
    pose proof (d g g0) as h; dorn h;subst;[left|right]; auto.

  - destruct (deq_nat n n0) as [d|d]; subst; [left|right]; auto.

  - right; auto.

  - right; auto.
Defined.

Lemma opid_dec_op {o} :
  forall (x y : @Opid o), option (x = y).
Proof.
  introv.
  dopid x as [can1|ncan1|exc1|abs1] Case;
    dopid y as [can2|ncan2|exc2|abs2] SCase.

  - Case "Can"; SCase "Can".
    pose proof (canonical_dec_op can1 can2) as h; destruct h as [h|h]; subst;
      try (complete (left; auto)).
    right; auto.

  - Case "Can"; SCase "NCan".
    right; auto.

  - Case "Can"; SCase "Exc".
    right; auto.

  - Case "Can"; SCase "Abs".
    right; auto.

  - Case "NCan"; SCase "Can".
    right; auto.

  - destruct ncan1; destruct ncan2; try (complete (left; auto));
      match goal with
      | [ |- option (NCan (?x _) = NCan (?x _)) ] => idtac
      | _ => exact None
      end.

    + try destruct c; try destruct c0; try (complete (left; auto)); right; auto.
    + try destruct a; try destruct a0; try (complete (left; auto)); right; auto.
    + try destruct c; try destruct c0; try (complete (left; auto)); right; auto.

  - Case "NCan"; SCase "Exc".
    right; auto.

  - Case "NCan"; SCase "Abs".
    right; auto.

  - Case "Exc"; SCase "Can".
    right; auto.

  - Case "Exc"; SCase "NCan".
    right; auto.

  - Case "Exc"; SCase "Exc".
    left; auto.

  - Case "Exc"; SCase "Abs".
    right; auto.

  - Case "Abs"; SCase "Can".
    right; auto.

  - Case "Abs"; SCase "NCan".
    right; auto.

  - Case "Abs"; SCase "Exc".
    right; auto.

  - destruct abs1, abs2.
    pose proof (String.string_dec opabs_name opabs_name0) as h.
    dorn h; subst;[|right].
    pose proof (parameters_dec opabs_params opabs_params0) as h.
    dorn h; subst;[|right].
    pose proof (opsign_dec opabs_sign opabs_sign0) as h.
    dorn h; subst;[|right].
    left; auto.
Defined.

Lemma term_dec_op {o} :
  forall (x y : @NTerm o), option (x = y).
Proof.
  sp_nterm_ind1 x as [v1|f1|op1 bs1 ind] Case; introv.

  - Case "vterm".
    destruct y as [v2|f1|op bs2];[|exact None|exact None].
    destruct (deq_nvar v1 v2); subst;[|exact None].
    left; reflexivity.

  - Case "sterm".
    right.

  - Case "oterm".
    destruct y as [v2|f2|op2 bs2];[right|right|].
    destruct (opid_dec_op op1 op2); subst;[|right].

    assert (option (bs1 = bs2)) as opbs.
    {
      revert bs2.
      induction bs1; introv.
      - destruct bs2;[left|right]; auto.
      - destruct bs2;[right|].
        destruct a as [l1 t1], b as [l2 t2].
        simpl in *.
        autodimp IHbs1 hyp.
        { introv i; introv; eapply ind; eauto. }
        pose proof (ind t1 l1) as q; autodimp q hyp; clear ind.
        pose proof (q t2) as h; clear q.
        destruct (list_eq_dec deq_nvar l1 l2) as [d|d]; subst;[|right].
        destruct h as [d|d]; subst;[|right].
        destruct (IHbs1 bs2) as [d|d]; subst;[|right].
        left; auto.
    }

    destruct opbs as [d|d]; subst;[left|right];auto.
Defined.


From CE Require Export Tests.

Import ListNotations.

Goal
  exists n res eff, | [], 0, large_example, [] | -e> | n , res, eff |.
Proof.
  unfold large_example.
  eexists. eexists. eexists.
  solve.
Qed.