(* Unlike unfold_Ssequence in veric/SeparationLogic.v, unfold_seq preserves Sfor and Swhile. In fact, it folds them even if they were not explicitly written in the input. Note that we don't treat Sdowhile, because there are no floyd tactics for it, and because it would result in 2198 cases when unfold_seq is printed. *)
Fixpoint unfold_seq c : list statement :=
match c with
| Sloop (Ssequence (Sifthenelse e Sskip Sbreak) s) Sskip =>
[Swhile e s]
| Ssequence s1 (Sloop (Ssequence (Sifthenelse e2 Sskip Sbreak) s3) s4) =>
(* TODO check if this matches cases we don't want it to match and if so, check that the loop iteration variable of s1 is the same as in the increment in s4, but then we'll exclude multi-statement for loop initialisations *)
[Sfor s1 e2 s3 s4]
| Ssequence c1 c2 => unfold_seq c1 ++ unfold_seq c2
| _ => [c]
end.
Fixpoint fold_seq lc : statement :=
match lc with
| nil => Sskip
| [c1] => c1
| c :: lc0 => Ssequence c (fold_seq lc0)
end.

(* Specifications: *)
Lemma flat_map_app: forall {A B: Type} (f: A -> list B) l1 l2,
flat_map f l1 ++ flat_map f l2 = flat_map f (l1 ++ l2).
Lemma unfold_Ssequence_idempotent: forall c,
flat_map unfold_Ssequence (unfold_Ssequence c) = unfold_Ssequence c.
Lemma flat_map_unfold_Ssequence_idempotent: forall ls,
flat_map unfold_Ssequence (flat_map unfold_Ssequence ls) = flat_map unfold_Ssequence ls.
Lemma unfold_seq_to_unfold_Ssequence: forall cs,
unfold_Ssequence cs = flat_map unfold_Ssequence (unfold_seq cs).
Lemma semax_unfold_seq {Espec: OracleKind} {CS: compspecs} : forall c1 c2,
unfold_seq c1 = unfold_seq c2 ->
forall P Q Delta, semax Delta P c1 Q -> semax Delta P c2 Q.