(* A state contain the PC (current instruction) and data *)
  
  Notation state := (nat * data)%type.Notation " P // s1 -]] s2 " := (sss_compute_max P s1 s2).(* A code is a contiguous list of instructions starting at some position *)
  
  Notation code := (nat * list instr)%type.(** ** Semantics *)
  
  (* This a one step computation *) 

  Definition sss_step P st1 st2 := exists k l i r d, P = (k,l++i::r)
                                                  /\ st1 = (k+length l,d)
                                                  /\ i // st1 -1> st2.(* Functionality of one step computations *)

  Fact sss_step_fun P s t1 t2 : P // s :1> t1 -> P // s :1> t2 -> t1 = t2.(* A "more general/usable" constructor *)
  
  Fact in_sss_step k l i r st1 st2 : fst st1 = k+length l        
                               ->       i     // st1  -1>  st2 
                               -> (k,l++i::r) // st1  :1>  st2.(* A destructor *)

  Fact sss_step_subcode_inv P ii st st' : 
        (fst st, ii::nil) <sc P -> P // st :1> st' -> ii // st -1> st'.Fact sss_step_supcode P Q st st' :
       P <sc Q -> in_code (fst st) P -> Q // st :1> st' -> P // st :1> st'.(* sss_step is decidable *)
  
  Fact sss_step_dec P st1 st2 : { P // st1 :1> st2 } + { ~ P // st1 :1> st2 }.(* This is a n steps computation *)
  
  Inductive sss_steps (P : code) : nat -> state -> state -> Prop :=
    | in_sss_steps_0 : forall st,                  P // st   -[0]->    st
    | in_sss_steps_S : forall n st1 st2 st3,       P // st1   :1>      st2
                                              ->   P // st2  -[n]->    st3
                                              ->   P // st1  -[S n]->  st3
  where "P // r -[ n ]-> s" := (sss_steps P n r s).(* This is a n steps computation *)
  
  Inductive sss_steps (P : code) : nat -> state -> state -> Prop :=
    | in_sss_steps_0 : forall st,                  P // st   -[0]->    st
    | in_sss_steps_S : forall n st1 st2 st3,       P // st1   :1>      st2
                                              ->   P // st2  -[n]->    st3
                                              ->   P // st1  -[S n]->  st3
  where "P // r -[ n ]-> s" := (sss_steps P n r s).(* This is a n steps computation *)
  
  Inductive sss_steps (P : code) : nat -> state -> state -> Prop :=
    | in_sss_steps_0 : forall st,                  P // st   -[0]->    st
    | in_sss_steps_S : forall n st1 st2 st3,       P // st1   :1>      st2
                                              ->   P // st2  -[n]->    st3
                                              ->   P // st1  -[S n]->  st3
  where "P // r -[ n ]-> s" := (sss_steps P n r s).(* This is a n steps computation *)
  
  Inductive sss_steps (P : code) : nat -> state -> state -> Prop :=
    | in_sss_steps_0 : forall st,                  P // st   -[0]->    st
    | in_sss_steps_S : forall n st1 st2 st3,       P // st1   :1>      st2
                                              ->   P // st2  -[n]->    st3
                                              ->   P // st1  -[S n]->  st3
  where "P // r -[ n ]-> s" := (sss_steps P n r s).(* More usable constructors *)
  
  Fact sss_steps_0 P st1 st2 : st1 = st2 -> P // st1 -[0]-> st2.Fact sss_steps_1 P st1 st2 : P // st1 :1> st2 -> P // st1 -[1]-> st2.Fact sss_steps_trans P n m st1 st2 st3 :
         P // st1 -[n]-> st2 -> P // st2 -[m]-> st3 -> P // st1 -[n+m]-> st3 .(* Inversion lemma *)
 
  Fact sss_steps_0_inv P st1 st2 : P // st1 -[0]-> st2 -> st1 = st2.Fact sss_steps_S_inv P st1 st3 k : 
                        st1 <> st3 
      ->                P // st1 -[k]-> st3 
      -> exists k' st2, k = S k' 
                     /\ P // st1 :1> st2
                     /\ P // st2 -[k']-> st3.Fact sss_steps_inv P k st1 st3 : 
          P // st1 -[k]-> st3 
       -> (k = 0 /\ st1 = st3)
        + { k' | exists st2, k = S k' 
                       /\ P // st1 :1> st2
                       /\ P // st2 -[k']-> st3 }%type.Fact sss_steps_S_inv' P st1 st3 k :  
                        P // st1 -[S k]-> st3 
      -> exists st2,    P // st1 :1> st2
                     /\ P // st2 -[k]-> st3.Fact sss_steps_fun P k s t1 t2 :
         P // s -[k]-> t1 
      -> P // s -[k]-> t2 
      -> t1 = t2.Fact sss_steps_plus_inv P n m st1 st3 : 
         P // st1 -[n+m]-> st3 
      -> exists st2, P // st1 -[n]-> st2 
                  /\ P // st2 -[m]-> st3.Definition sss_progress P st1 st2 := exists k, 0 < k /\ P // st1 -[k]-> st2.Definition sss_compute  P st1 st2 := exists k, P // st1 -[k]-> st2.Fact sss_progress_compute P st1 st2 : P // st1 -+> st2 -> P // st1 ->> st2.Fact sss_compute_trans P st1 st2 st3 : P // st1 ->> st2 -> P // st2 ->> st3 -> P // st1 ->> st3.Fact sss_progress_compute_trans P st1 st2 st3 : P // st1 -+> st2 -> P // st2 ->> st3 -> P // st1 -+> st3.Fact sss_compute_progress_trans P st1 st2 st3 : P // st1 ->> st2 -> P // st2 -+> st3 -> P // st1 -+> st3.Fact sss_progress_trans P st1 st2 st3 : P // st1 -+> st2 -> P // st2 -+> st3 -> P // st1 -+> st3.(* Computations must start inside code *) 
  
  Fact sss_step_in_code P st1 st2 : P // st1 :1> st2 -> in_code (fst st1) P.Fact sss_steps_compute P n st1 st2 : P // st1 -[n]-> st2 -> P // st1 ->> st2.(** We propose a library of locality lemmas that link computations
      in subcode to computations in the code *)

  (** ** Compisitional Reasoning over Subcode *)

  (* First inclusions lemmas *)

  Fact subcode_sss_step P Q st1 st2: P <sc Q -> P // st1 :1> st2 -> Q // st1 :1> st2.Fact subcode_sss_steps P Q k st1 st2: P <sc Q -> P // st1 -[k]-> st2 -> Q // st1 -[k]-> st2.Fact subcode_sss_progress P Q st1 st2: P <sc Q -> P // st1 -+> st2 -> Q // st1 -+> st2.Fact subcode_sss_compute P Q st1 st2: P <sc Q -> P // st1 ->> st2 -> Q // st1 ->> st2.Fact subcode_sss_compute_trans P Q st1 st2 st3 :
       P <sc Q -> P // st1 ->> st2 -> Q // st2 ->> st3 -> Q // st1 ->> st3.Fact subcode_sss_compute_linstr k li P st1 st2 st :
            (fst st1,li) // st1 -[k]-> st2
         -> (fst st1,li) <sc P
         -> P // st2 ->> st
         -> P // st1 ->> st.Fact subcode_sss_compute_instr P i st1 st2 st3 : 
            i // st1 -1> st2
        ->  (fst st1,i::nil) <sc P
        ->  P // st2 ->> st3
        ->  P // st1 ->> st3.(* Then are reverse inclusion lemmas *)

  Fact subcode_sss_step_inv P Q st1 st2 : 
         P <sc Q 
      -> in_code (fst st1) P
      -> Q // st1 :1> st2
      -> P // st1 :1> st2.Definition sss_output P st st' := P // st ->> st' /\ out_code (fst st') P.Definition sss_terminates P st := exists st', P // st ~~> st'.Fact subcode_sss_terminates_instr P i st1 st2 : 
            i // st1 -1> st2
        ->  (fst st1,i::nil) <sc P
        ->  P // st2 ↓
        ->  P // st1 ↓.(* No computation is possible *)
  
  Definition sss_stall ii st := forall st', ~ ii // st -1> st'.Definition sss_step_stall P st := forall st', ~ P // st :1> st'.Fact sss_steps_stall_inv P p s1 s2 : sss_step_stall P s1 -> P // s1 -[p]-> s2 -> p = 0 /\ s1 = s2.Fact sss_steps_stall_fun P p q s1 s2 :
         sss_step_stall P s2
      -> P // s1 -[p]-> s2 
      -> P // s1 -[q]-> s2
      -> p = q.Definition sss_compute_max P s1 s2 := (P // s1 ->> s2 /\ sss_step_stall P s2).(* No computation outside the code *)

  Fact sss_out_step_stall P st : out_code (fst st) P -> sss_step_stall P st.Fact sss_stall_step_stall ii P st :
          (fst st,ii::nil) <sc P
       -> sss_stall ii st
       -> sss_step_stall P st.Fact sss_stall_step_0 ii P q st st' :
          (fst st,ii::nil) <sc P
       -> sss_stall ii st
       -> P // st -[q]-> st' -> q = 0 /\ st = st'.Fact sss_step_stall_inv P st :
          sss_step_stall P st
       -> { ii | (fst st,ii::nil) <sc P /\ sss_stall ii st }
        + { out_code (fst st) P }.Fact sss_steps_stall k P st st' : 
         out_code (fst st) P
      -> P // st -[k]-> st' 
      -> k = 0 /\ st = st'.

(* Specifications: *)
Lemma subcode_sss_steps_inv P Q k st1 st3 : P <sc Q -> in_code (fst st1) P -> out_code (fst st3) P -> Q // st1 -[k]-> st3 -> exists k1 k2 st2, P // st1 -[k1]-> st2 /\ Q // st2 -[k2]-> st3 /\ k = k1+k2 /\ out_code (fst st2) P.
Lemma subcode_sss_compute_inv P Q st1 st3 : P <sc Q -> out_code (fst st3) P -> Q // st1 ->> st3 -> exists st2, P // st1 ->> st2 /\ Q // st2 ->> st3 /\ out_code (fst st2) P.