Ltac drop_LOCAL n :=
   first [apply (drop_LOCAL n) | apply (drop_LOCAL' n) | apply (drop_LOCAL'' n)];
    unfold delete_nth.Ltac drop_LOCAL_by_name name := match goal with
  | |- semax _ (PROPx ?P (LOCALx ?Q (SEPx ?R))) _ _ =>
    let r := eval hnf in (find_LOCAL_index name O Q) in match r with
    | Some ?i => drop_LOCAL i
    | None => fail 1 "No variable named" name "found"
    end 
  end.Ltac drop_LOCALs l := match l with
| ?h :: ?t => drop_LOCAL_by_name h; drop_LOCALs t
| nil => idtac
end.Ltac clean_up_app_carefully := (* useful after rewriting by SEP_PROP *)
 repeat
  match goal with
  | |- context [@app Prop (?a :: ?b) ?c] =>
    change (app (a::b) c) with (a :: app b c)
  | |- context [@app (environ->Prop) (?a :: ?b) ?c] =>
    change (app (a::b) c) with (a :: app b c)
  | |- context [@app (lifted (LiftEnviron Prop)) (?a :: ?b) ?c] =>
    change (app (a::b) c) with (a :: app b c)
  | |- context [@app (environ->mpred) (?a :: ?b) ?c] =>
    change (app (a::b) c) with (a :: app b c)
  | |- context [@app (lifted (LiftEnviron mpred)) (?a :: ?b) ?c] =>
    change (app (a::b) c) with (a :: app b c)
  | |- context [@app Prop nil ?c] =>
     change (app nil c) with c
  | |- context [@app (environ->Prop) nil ?c] =>
     change (app nil c) with c
  | |- context [@app (lifted (LiftEnviron Prop)) nil ?c] =>
     change (app nil c) with c
  | |- context [@app (lifted (environ->mpred)) nil ?c] =>
     change (app nil c) with c
  | |- context [@app (lifted (LiftEnviron mpred)) nil ?c] =>
     change (app nil c) with c
 end.Ltac not_conj_notation :=
 match goal with
 | |- not_conj_notation (_ <= _ <= _)%Z => fail 1
 | |- not_conj_notation (_ <= _ < _)%Z => fail 1
 | |- not_conj_notation (_ < _ <= _)%Z => fail 1
 | |- not_conj_notation (_ <= _ <= _)%nat => fail 1
 | |- not_conj_notation (_ <= _ < _)%nat => fail 1
 | |- not_conj_notation (_ < _ <= _)%nat => fail 1
 | |- _ => apply Coq.Init.Logic.I
 end.

(* Specifications: *)
Lemma split_first_PROP: forall P Q R S, not_conj_notation (P/\Q) -> PROPx ((P/\Q)::R) S = PROPx (P::Q::R) S.
Lemma semax_frame_perm: forall (Qframe : list localdef) (Rframe : list mpred) (Espec : OracleKind) {cs: compspecs} (Delta : tycontext) (P : list Prop) (Q : list localdef) (c : statement) (R : list mpred) (Q1 : list localdef) (R1 : list mpred) (P2 : list Prop) (Q2 : list localdef) (R2 : list mpred), closed_wrt_modvars c (LOCALx Qframe (SEPx Rframe)) -> Permutation (Qframe ++ Q1) Q -> Permutation (Rframe ++ R1) R -> semax Delta (PROPx P (LOCALx Q1 (SEPx R1))) c (normal_ret_assert (PROPx P2 (LOCALx Q2 (SEPx R2)))) -> semax Delta (PROPx P (LOCALx Q (SEPx R))) c (normal_ret_assert (PROPx P2 (LOCALx (Q2 ++ Qframe) (SEPx (R2 ++ Rframe))))).
Lemma semax_frame': forall {Espec: OracleKind}{CS: compspecs}, forall Delta P1 P2 P3 s Q1 Q2 Q3 F, @semax CS Espec Delta (PROPx P1 (LOCALx P2 (SEPx P3))) s (normal_ret_assert (PROPx Q1 (LOCALx Q2 (SEPx Q3)))) -> @semax CS Espec Delta (PROPx P1 (LOCALx P2 (SEPx (F :: P3)))) s
Lemma semax_frame'': forall {Espec: OracleKind}{CS: compspecs}, forall Delta P1 P2 P3 s t Q1 Q2 Q3 F, @semax CS Espec Delta (PROPx P1 (LOCALx P2 (SEPx P3))) s (frame_ret_assert (function_body_ret_assert t (PROPx Q1 (LOCALx Q2 (SEPx Q3)))) emp) -> @semax CS Espec Delta (PROPx P1 (LOCALx P2 (SEPx (F :: P3)))) s (frame_ret_assert