(*** Machine specification ***)

(** Abstract syntax of machine code **)

Definition var: Set := positive.Definition var_eq: forall (v1 v2: var), {v1=v2} + {v1<>v2} := peq.Definition V (n: nat) : var := P_of_succ_nat n.Definition label: Set := positive.Definition label_eq: forall (l1 l2: label), {l1=l2} + {l1<>l2} := peq.Definition L (n: nat) : label := P_of_succ_nat n.Inductive instr: Set :=
  | instr_jump: var -> instr
  | instr_getlabel: label -> var -> instr
  | instr_branch_if_nil: var -> label -> instr
  | instr_fetch_field: var -> nat -> var -> instr
  | instr_cons: var -> var -> var -> instr
  | instr_halt: instr
  | instr_seq: instr -> instr -> instr.Inductive instr: Set :=
  | instr_jump: var -> instr
  | instr_getlabel: label -> var -> instr
  | instr_branch_if_nil: var -> label -> instr
  | instr_fetch_field: var -> nat -> var -> instr
  | instr_cons: var -> var -> var -> instr
  | instr_halt: instr
  | instr_seq: instr -> instr -> instr.Inductive instr: Set :=
  | instr_jump: var -> instr
  | instr_getlabel: label -> var -> instr
  | instr_branch_if_nil: var -> label -> instr
  | instr_fetch_field: var -> nat -> var -> instr
  | instr_cons: var -> var -> var -> instr
  | instr_halt: instr
  | instr_seq: instr -> instr -> instr.Inductive instr: Set :=
  | instr_jump: var -> instr
  | instr_getlabel: label -> var -> instr
  | instr_branch_if_nil: var -> label -> instr
  | instr_fetch_field: var -> nat -> var -> instr
  | instr_cons: var -> var -> var -> instr
  | instr_halt: instr
  | instr_seq: instr -> instr -> instr.Inductive instr: Set :=
  | instr_jump: var -> instr
  | instr_getlabel: label -> var -> instr
  | instr_branch_if_nil: var -> label -> instr
  | instr_fetch_field: var -> nat -> var -> instr
  | instr_cons: var -> var -> var -> instr
  | instr_halt: instr
  | instr_seq: instr -> instr -> instr.Inductive instr: Set :=
  | instr_jump: var -> instr
  | instr_getlabel: label -> var -> instr
  | instr_branch_if_nil: var -> label -> instr
  | instr_fetch_field: var -> nat -> var -> instr
  | instr_cons: var -> var -> var -> instr
  | instr_halt: instr
  | instr_seq: instr -> instr -> instr.Inductive instr: Set :=
  | instr_jump: var -> instr
  | instr_getlabel: label -> var -> instr
  | instr_branch_if_nil: var -> label -> instr
  | instr_fetch_field: var -> nat -> var -> instr
  | instr_cons: var -> var -> var -> instr
  | instr_halt: instr
  | instr_seq: instr -> instr -> instr.Inductive instr: Set :=
  | instr_jump: var -> instr
  | instr_getlabel: label -> var -> instr
  | instr_branch_if_nil: var -> label -> instr
  | instr_fetch_field: var -> nat -> var -> instr
  | instr_cons: var -> var -> var -> instr
  | instr_halt: instr
  | instr_seq: instr -> instr -> instr.Infix ";;" := instr_seq (at level 60, right associativity).Definition program := map instr.(** Run-time values and environments **)

Inductive value: Set :=
  | value_label: label -> value
  | value_cons: value -> value -> value.(** Run-time values and environments **)

Inductive value: Set :=
  | value_label: label -> value
  | value_cons: value -> value -> value.(** Run-time values and environments **)

Inductive value: Set :=
  | value_label: label -> value
  | value_cons: value -> value -> value.Definition store := map value.Definition store_empty : store := empty value.(* Safety property *)

Definition entry_label := L 0.Inductive stepstar (p: program) : store -> instr -> store -> instr ->  Prop :=
 | stepstar_O: forall s i, stepstar p s i s i
 | stepstar_S: forall s i s' i' s'' i'',
              step p s i s' i' -> stepstar p s' i' s'' i'' -> stepstar p s i s'' i''.Inductive stepstar (p: program) : store -> instr -> store -> instr ->  Prop :=
 | stepstar_O: forall s i, stepstar p s i s i
 | stepstar_S: forall s i s' i' s'' i'',
              step p s i s' i' -> stepstar p s' i' s'' i'' -> stepstar p s i s'' i''.Inductive stepstar (p: program) : store -> instr -> store -> instr ->  Prop :=
 | stepstar_O: forall s i, stepstar p s i s i
 | stepstar_S: forall s i s' i' s'' i'',
              step p s i s' i' -> stepstar p s' i' s'' i'' -> stepstar p s i s'' i''.Inductive step_or_halt: program -> store -> instr -> Prop :=
  | step_or_halt_step: forall p r i r' i',
      step p r i r' i' -> step_or_halt p r i
  | step_or_halt_halt: forall p r,
      step_or_halt p r instr_halt.Inductive step_or_halt: program -> store -> instr -> Prop :=
  | step_or_halt_step: forall p r i r' i',
      step p r i r' i' -> step_or_halt p r i
  | step_or_halt_halt: forall p r,
      step_or_halt p r instr_halt.Inductive step_or_halt: program -> store -> instr -> Prop :=
  | step_or_halt_step: forall p r i r' i',
      step p r i r' i' -> step_or_halt p r i
  | step_or_halt_halt: forall p r,
      step_or_halt p r instr_halt.Definition safe (p: program) (s: store) (i: instr) :=
   forall s' i', stepstar p s i s' i' -> step_or_halt p s' i'.Definition safe_prog (p: program) := forall s i, start_state p s i -> safe p s i.Inductive stepN (p: program) : nat -> store -> instr -> store -> instr ->  Prop :=
 | stepN_O: forall s i, stepN p O s i s i
 | stepN_S: forall n s i s' i' s'' i'',
              step p s i s' i' -> stepN p n s' i' s'' i'' -> stepN p (S n) s i s'' i''.Inductive stepN (p: program) : nat -> store -> instr -> store -> instr ->  Prop :=
 | stepN_O: forall s i, stepN p O s i s i
 | stepN_S: forall n s i s' i' s'' i'',
              step p s i s' i' -> stepN p n s' i' s'' i'' -> stepN p (S n) s i s'' i''.Inductive stepN (p: program) : nat -> store -> instr -> store -> instr ->  Prop :=
 | stepN_O: forall s i, stepN p O s i s i
 | stepN_S: forall n s i s' i' s'' i'',
              step p s i s' i' -> stepN p n s' i' s'' i'' -> stepN p (S n) s i s'' i''.

(* Specifications: *)
Lemma stepstar_stepN : forall p s i s' i', stepstar p s i s' i' -> exists n, stepN p n s i s' i'.
Lemma stepN_stepstar : forall p n s i s' i', stepN p n s i s' i' -> stepstar p s i s' i'.
Lemma step_deterministic : forall p s i s1 s2 i1 i2, step p s i s1 i1 -> step p s i s2 i2 -> s1 = s2 /\ i1 = i2.
Lemma step_confluent : forall p n s s1 s2 s' i i1 i2 i', step p s i s1 i1 -> step p s i s2 i2 -> stepN p n s1 i1 s' i' -> stepN p n s2 i2 s' i'.
Lemma step_triangle : forall p n1 n2 s s1 s2 i i1 i2, n1 <= n2 -> stepN p n1 s i s1 i1 -> stepN p n2 s i s2 i2 -> stepN p (n2 - n1) s1 i1 s2 i2.