include "basics/bool.ma". include "basics/jmeq.ma". include "common/CostLabel.ma". include "utilities/option.ma". include "basics/lists/listb.ma". include "common/IO.ma". include "utilities/hide.ma". include "ASM/Util.ma". inductive status_class: Type[0] ≝ | cl_return: status_class | cl_jump: status_class | cl_call: status_class | cl_tailcall: status_class | cl_other: status_class. record abstract_status : Type[1] ≝ { as_status :> Type[0] ; as_execute : relation as_status ; as_pc : DeqSet ; as_pc_of : as_status → as_pc ; as_classify : as_status → status_class ; as_label_of_pc : as_pc → option costlabel ; as_after_return : (Σs:as_status. as_classify s = cl_call) → as_status → Prop ; as_result: as_status → option int ; as_call_ident : (Σs:as_status.as_classify s = cl_call) → ident ; as_tailcall_ident : (Σs:as_status.as_classify s = cl_tailcall) → ident }. definition as_classifier ≝ λS,s,cl.as_classify S s = cl. definition as_call ≝ λS,s,f.as_call_ident S s = f. definition as_final ≝ λS,s.as_result S s ≠ None ?. definition as_label ≝ λS : abstract_status. λs : S. as_label_of_pc ? (as_pc_of ? s). (* temporary alias for backward compatibility *) (* definition final_abstract_status ≝ abstract_status. *) definition as_costed : ∀a_s : abstract_status.a_s → Prop ≝ λa_s,st.as_label ? st ≠ None ?. lemma as_costed_exc : ∀S:abstract_status. ∀s:S. (as_costed S s) + (¬as_costed S s). #S #s whd in match (as_costed S s); cases (as_label S s) [ %2 % * /2/ | #c %1 % #E destruct ] qed. lemma not_costed_no_label : ∀S,st. ¬as_costed S st → as_label S st = None ?. #S #st * normalize cases (as_label_of_pc S ?) [ // | #l #H cases (H ?) % #E destruct ] qed. (* cost map generalities *) definition as_cost_labelled_at ≝ λS : abstract_status. λl.λpc.as_label_of_pc S pc = Some ? l. definition as_cost_labelled ≝ λS : abstract_status. λl.∃pc.as_cost_labelled_at S l pc. definition as_cost_label ≝ λS : abstract_status. Σl.as_cost_labelled S l. unification hint 0 ≔ S ⊢ as_cost_label S ≡ Sig costlabel (λl.as_cost_labelled S l). definition as_cost_labels ≝ λS : abstract_status. list (as_cost_label S). definition as_cost_get_label ≝ λS : abstract_status. λl_sig: as_cost_label S. pi1 … l_sig. (* (* JHM: this no longer is a complete definition! but not used; so commented out for now *) definition as_cost_get_labels ≝ λS : abstract_status. map … (as_cost_get_label S). *) definition as_cost_map ≝ λS : abstract_status. (as_cost_label S) → ℕ. definition as_label_safe : ∀a_s : abstract_status. (Σs : a_s.as_costed ? s) → as_cost_label a_s ≝ λa_s,st_sig.«opt_safe ?? (pi2 … st_sig), ?». @hide_prf @opt_safe_elim #c #EQ %{EQ} qed. definition lift_sigma_map_id : ∀A,B : Type[0].∀P_in,P_out : A → Prop.B → (∀a.P_out a + ¬ P_out a) → ((Σa.P_out a) → B) → (Σa.P_in a) → B ≝ λA,B,P_in,P_out,dflt,dec,m,a_sig. match dec a_sig with [ inl prf ⇒ m «a_sig, prf» | inr _ ⇒ dflt (* labels not present in out code get 0 *) ]. lemma lift_sigma_map_id_eq : ∀A,B,P_in,P_out,dflt,dec,m,a_in,a_out. pi1 ?? a_in = pi1 ?? a_out → lift_sigma_map_id A B P_in P_out dflt dec m a_in = m a_out. #A#B#P_in#P_out#dflt#dec#m#a_in#a_out#EQ whd in match lift_sigma_map_id; normalize nodelta cases (dec a_in) normalize nodelta >EQ cases a_out #a #H #G [ % | @⊥ /2 by absurd/ ] qed. notation > "Σ_{ ident i ∈ l } f" with precedence 20 for @{'fold plus 0 (λ${ident i}.true) (λ${ident i}. $f) $l}. notation < "Σ_{ ident i ∈ l } f" with precedence 20 for @{'fold plus 0 (λ${ident i}:$X.true) (λ${ident i}:$Y. $f) $l}. definition lift_cost_map_id : ∀S_in,S_out : abstract_status.? → as_cost_map S_out → as_cost_map S_in ≝ λS_in,S_out : abstract_status. lift_sigma_map_id costlabel ℕ (*λl.∃pc.as_label_of_pc S_in pc = Some ? l*) (as_cost_labelled S_in) (*λl.∃pc.as_label_of_pc S_out pc = Some ? l*) (as_cost_labelled S_out) 0. lemma lift_cost_map_same_cost : ∀S_in, S_out, dec, m_out, trace_in, trace_out. map … (pi1 ??) trace_in = map … (pi1 ??) trace_out → (Σ_{ l_sig ∈ trace_in } (lift_cost_map_id S_in S_out dec m_out l_sig)) = (Σ_{ l_sig ∈ trace_out } (m_out l_sig)). #S_in #S_out #dec #m_out #trace_in elim trace_in [2: #hd_in #tl_in #IH] * [2,4: #hd_out #tl_out] normalize in ⊢ (%→?); [2,3: #ABS destruct(ABS) |4: #_ % |1: #EQ destruct whd in ⊢(??%%); whd in match lift_cost_map_id; normalize nodelta >(lift_sigma_map_id_eq ????????? e0) >e0 in e1; normalize in ⊢(%→?); #EQ >(IH … EQ) % ] qed. (* structured traces: down to business *) inductive trace_ends_with_ret: Type[0] ≝ | ends_with_ret: trace_ends_with_ret | doesnt_end_with_ret: trace_ends_with_ret. inductive trace_label_return (S:abstract_status) : S → S → Type[0] ≝ | tlr_base: ∀status_before: S. ∀status_after: S. trace_label_label S ends_with_ret status_before status_after → trace_label_return S status_before status_after | tlr_step: ∀status_initial: S. ∀status_labelled: S. ∀status_final: S. trace_label_label S doesnt_end_with_ret status_initial status_labelled → trace_label_return S status_labelled status_final → trace_label_return S status_initial status_final with trace_label_label: trace_ends_with_ret → S → S → Type[0] ≝ | tll_base: ∀ends_flag: trace_ends_with_ret. ∀start_status: S. ∀end_status: S. trace_any_label S ends_flag start_status end_status → as_costed S start_status → trace_label_label S ends_flag start_status end_status with trace_any_label: trace_ends_with_ret → S → S → Type[0] ≝ (* Single steps within a function which reach a label. Note that this is the only case applicable for a jump. *) | tal_base_not_return: ∀start_status: S. ∀final_status: S. as_execute S start_status final_status → (as_classifier S start_status cl_jump ∨ as_classifier S start_status cl_other) → as_costed S final_status → trace_any_label S doesnt_end_with_ret start_status final_status | tal_base_return: ∀start_status: S. ∀final_status: S. as_execute S start_status final_status → as_classifier S start_status cl_return → trace_any_label S ends_with_ret start_status final_status (* A call followed by a label on return. *) | tal_base_call: ∀status_pre_fun_call: S. ∀status_start_fun_call: S. ∀status_final: S. as_execute S status_pre_fun_call status_start_fun_call → ∀H:as_classifier S status_pre_fun_call cl_call. as_after_return S «status_pre_fun_call, H» status_final → trace_label_return S status_start_fun_call status_final → as_costed S status_final → trace_any_label S doesnt_end_with_ret status_pre_fun_call status_final (* A tailcall, which ends the trace like a return. *) | tal_base_tailcall: ∀status_pre_fun_call: S. ∀status_start_fun_call: S. ∀status_final: S. as_execute S status_pre_fun_call status_start_fun_call → as_classifier S status_pre_fun_call cl_tailcall → trace_label_return S status_start_fun_call status_final → trace_any_label S ends_with_ret status_pre_fun_call status_final (* A call followed by a non-empty trace. *) | tal_step_call: ∀end_flag: trace_ends_with_ret. ∀status_pre_fun_call: S. ∀status_start_fun_call: S. ∀status_after_fun_call: S. ∀status_final: S. as_execute S status_pre_fun_call status_start_fun_call → ∀H:as_classifier S status_pre_fun_call cl_call. as_after_return S «status_pre_fun_call, H» status_after_fun_call → trace_label_return S status_start_fun_call status_after_fun_call → ¬ as_costed S status_after_fun_call → trace_any_label S end_flag status_after_fun_call status_final → trace_any_label S end_flag status_pre_fun_call status_final | tal_step_default: ∀end_flag: trace_ends_with_ret. ∀status_pre: S. ∀status_init: S. ∀status_end: S. as_execute S status_pre status_init → trace_any_label S end_flag status_init status_end → as_classifier S status_pre cl_other → ¬ (as_costed S status_init) → trace_any_label S end_flag status_pre status_end. let rec tal_pc_list (S : abstract_status) fl st1 st2 (tal : trace_any_label S fl st1 st2) on tal : list (as_pc S) ≝ match tal with [ tal_step_call fl' pre _ st1' st2' _ _ _ _ _ tl ⇒ as_pc_of … pre :: tal_pc_list … tl | tal_step_default fl' pre st1' st2' _ tl _ _ ⇒ as_pc_of … pre :: tal_pc_list … tl | tal_base_not_return pre _ _ _ _ ⇒ [as_pc_of … pre] | tal_base_return pre _ _ _ ⇒ [as_pc_of … pre] | tal_base_call pre _ _ _ _ _ _ _ ⇒ [as_pc_of … pre] | tal_base_tailcall pre _ _ _ _ _ ⇒ [as_pc_of … pre] ]. definition as_trace_any_label_length': ∀S: abstract_status. ∀trace_ends_flag: trace_ends_with_ret. ∀start_status: S. ∀final_status: S. ∀the_trace: trace_any_label S trace_ends_flag start_status final_status. nat ≝ λS: abstract_status. λtrace_ends_flag: trace_ends_with_ret. λstart_status: S. λfinal_status: S. λthe_trace: trace_any_label S trace_ends_flag start_status final_status. |tal_pc_list … the_trace|. let rec tlr_unrepeating S st1 st2 (tlr : trace_label_return S st1 st2) on tlr : Prop ≝ match tlr with [ tlr_base st1 st2 tll ⇒ tll_unrepeating … tll | tlr_step st1 st2 st3 tll tl ⇒ tll_unrepeating … tll ∧ tlr_unrepeating … tl ] and tll_unrepeating S fl st1 st2 (tll : trace_label_label S fl st1 st2) on tll : Prop ≝ match tll with [ tll_base fl st1 st2 tal _ ⇒ tal_unrepeating … tal ] and tal_unrepeating S fl st1 st2 (tal : trace_any_label S fl st1 st2) on tal : Prop ≝ match tal with [ tal_step_call fl st1 st2 st3 st4 _ _ _ tlr _ tl ⇒ bool_to_Prop (notb (memb ? (as_pc_of … st1) (tal_pc_list … tl))) ∧ tal_unrepeating … tl ∧ tlr_unrepeating … tlr | tal_step_default fl st1 st2 st3 _ tl _ _ ⇒ bool_to_Prop (notb (memb ? (as_pc_of … st1) (tal_pc_list … tl))) ∧ tal_unrepeating … tl | tal_base_call pre _ _ _ _ _ trace _ ⇒ tlr_unrepeating … trace | tal_base_tailcall pre _ _ _ _ trace ⇒ tlr_unrepeating … trace | _ ⇒ True ]. definition tll_hd_label : ∀S : abstract_status.∀fl,st1,st2. trace_label_label S fl st1 st2 → costlabel ≝ λS,fl,st1,st2,tr.match tr with [ tll_base _ st1' _ _ prf ⇒ as_label_safe … «st1', prf» ]. definition tlr_hd_label : ∀S : abstract_status.∀st1,st2. trace_label_return S st1 st2 → costlabel ≝ λS,st1,st2,tr.match tr with [ tlr_base st1' st2' tll ⇒ tll_hd_label … tll | tlr_step st1' st2' _ tll _ ⇒ tll_hd_label … tll ]. let rec tal_unrepeating_uniqueb S fl st1 st2 tal on tal : tal_unrepeating S fl st1 st2 tal → bool_to_Prop (uniqueb … (tal_pc_list … tal)) ≝ ?. cases tal // #fl' #st1' [#st_fun] #st2' #st3' #H [ #H0 #H1 #tlr #G #tal | #tal #H0 #G ] whd in ⊢ (% → ?%); [*]* #A #B [#_] >A normalize nodelta @tal_unrepeating_uniqueb assumption qed. lemma tal_pc_list_start : ∀S,fl,s1,s2. ∀tal: trace_any_label S fl s1 s2. ∃tl. tal_pc_list … tal = (as_pc_of S s1)::tl. #S #fl0 #s10 #s20 * [ #s1 #s2 #EX #CL #CS | #s1 #s2 #EX #CL | #s1 #s2 #s3 #EX #CL #AF #tlr #CS | #s1 #s2 #s3 #EX #CL #tlr | #fl #s1 #s2 #s3 #s4 #EX #CL #AF #tlr #CS #tal | #fl #s1 #s2 #s3 #EX #tal #CL #CS ] whd in ⊢ (??(λ_.??%?)); % [2,4,6,8,10,12: % | *: skip ] qed. let rec tal_tail_not_costed S fl st1 st2 tal (H:Not (as_costed S st1)) on tal : All ? (λpc. as_label_of_pc S pc = None ?) (tal_pc_list S fl st1 st2 tal) ≝ ?. cases tal in H ⊢ %; [ #start #final #EX #CL #CS #CS' % // @(not_costed_no_label ?? CS') | #start #final #EX #CL #CS % // @(not_costed_no_label ?? CS) | #pre #start #final #EX #CL #AF #tlr #CS #CS' % // @(not_costed_no_label ?? CS') | #pre #start #final #EX #CL #tlr #CS' % // @(not_costed_no_label ?? CS') | #fl' #pre #start #after #final #EX #CL #AF #tlr #CS #tal' #CS' cases (tal_pc_list_start … tal') #hd #E >E % [ @(not_costed_no_label ?? CS') | @tal_tail_not_costed assumption ] | #fl' #pre #init #end #EX #tal' #CL #CS #CS' cases (tal_pc_list_start … tal') #hd #E >E % [ @(not_costed_no_label ?? CS') | @tal_tail_not_costed assumption ] ] qed. inductive trace_any_call (S:abstract_status) : S → S → Type[0] ≝ | tac_base: ∀status: S. as_classifier S status cl_call ∨ as_classifier S status cl_tailcall → trace_any_call S status status | tac_step_call: ∀status_pre_fun_call: S. ∀status_after_fun_call: S. ∀status_final: S. ∀status_start_fun_call: S. as_execute S status_pre_fun_call status_start_fun_call → ∀H:as_classifier S status_pre_fun_call cl_call. as_after_return S (mk_Sig ?? status_pre_fun_call H) status_after_fun_call → trace_label_return S status_start_fun_call status_after_fun_call → ¬ as_costed S status_after_fun_call → trace_any_call S status_after_fun_call status_final → trace_any_call S status_pre_fun_call status_final | tac_step_default: ∀status_pre: S. ∀status_end: S. ∀status_init: S. as_execute S status_pre status_init → trace_any_call S status_init status_end → as_classifier S status_pre cl_other → ¬ (as_costed S status_init) → trace_any_call S status_pre status_end. inductive trace_label_call (S:abstract_status) : S → S → Type[0] ≝ | tlc_base: ∀start_status: S. ∀end_status: S. trace_any_call S start_status end_status → as_costed S start_status → trace_label_call S start_status end_status . definition tlc_hd_label : ∀S : abstract_status.∀st1,st2. trace_label_call S st1 st2 → costlabel ≝ λS,st1,st2,tr.match tr with [ tlc_base st1' _ _ prf ⇒ as_label_safe … «st1', prf» ]. coinductive trace_label_diverges (S:abstract_status) : S → Type[0] ≝ | tld_step: ∀status_initial: S. ∀status_labelled: S. trace_label_label S doesnt_end_with_ret status_initial status_labelled → trace_label_diverges S status_labelled → trace_label_diverges S status_initial | tld_base: ∀status_initial: S. ∀status_pre_fun_call: S. ∀status_start_fun_call: S. trace_label_call S status_initial status_pre_fun_call → as_execute S status_pre_fun_call status_start_fun_call → ∀H:as_classifier S status_pre_fun_call cl_call. trace_label_diverges S status_start_fun_call → trace_label_diverges S status_initial. definition tld_hd_label : ∀S : abstract_status.∀st. trace_label_diverges S st → costlabel ≝ λS,st,tr.match tr with [ tld_step st' st'' tll _ ⇒ tll_hd_label … tll | tld_base st' st'' _ tlc _ _ _ ⇒ tlc_hd_label … tlc ]. (* Version in Prop for showing existence. *) coinductive trace_label_diverges_exists (S:abstract_status) : S → Prop ≝ | tld_step': ∀status_initial: S. ∀status_labelled: S. trace_label_label S doesnt_end_with_ret status_initial status_labelled → trace_label_diverges_exists S status_labelled → trace_label_diverges_exists S status_initial | tld_base': ∀status_initial: S. ∀status_pre_fun_call: S. ∀status_start_fun_call: S. trace_label_call S status_initial status_pre_fun_call → as_execute S status_pre_fun_call status_start_fun_call → ∀H:as_classifier S status_pre_fun_call cl_call. trace_label_diverges_exists S status_start_fun_call → trace_label_diverges_exists S status_initial. inductive trace_whole_program (S: abstract_status) : S → Type[0] ≝ | twp_terminating: ∀status_initial: S. ∀status_start_fun: S. ∀status_final: S. as_classifier S status_initial cl_call → as_execute S status_initial status_start_fun → trace_label_return S status_start_fun status_final → as_final S status_final → trace_whole_program S status_initial | twp_diverges: ∀status_initial: S. ∀status_start_fun: S. as_classifier S status_initial cl_call → as_execute S status_initial status_start_fun → trace_label_diverges S status_start_fun → trace_whole_program S status_initial. (* Again, an identical version in Prop for existence proofs. *) inductive trace_whole_program_exists (S: abstract_status) : S → Prop ≝ | twp_terminating: ∀status_initial: S. ∀status_start_fun: S. ∀status_final: S. as_classifier S status_initial cl_call → as_execute S status_initial status_start_fun → trace_label_return S status_start_fun status_final → as_final S status_final → trace_whole_program_exists S status_initial | twp_diverges: ∀status_initial: S. ∀status_start_fun: S. as_classifier S status_initial cl_call → as_execute S status_initial status_start_fun → trace_label_diverges_exists S status_start_fun → trace_whole_program_exists S status_initial. let rec trace_any_label_label S s s' f (tr:trace_any_label S f s s') on tr : match f with [ doesnt_end_with_ret ⇒ as_costed S s' | ends_with_ret ⇒ True ] ≝ match tr return λf,s,s',tr. match f with [ doesnt_end_with_ret ⇒ as_costed S s' | ends_with_ret ⇒ True ] with [ tal_base_not_return start final _ _ C ⇒ C | tal_base_return _ _ _ _ ⇒ I | tal_base_call _ _ _ _ _ _ _ C ⇒ C | tal_base_tailcall _ _ _ _ _ _ ⇒ I | tal_step_call f pre start after final X C RET LR C' tr' ⇒ trace_any_label_label … tr' | tal_step_default f pre init end X tr' C C' ⇒ trace_any_label_label … tr' ]. definition tal_tl_label : ∀S : abstract_status.∀st1,st2. trace_any_label S doesnt_end_with_ret st1 st2 → costlabel ≝ λS,st1,st2,tr.as_label_safe … «st2, trace_any_label_label … tr». lemma trace_label_label_label : ∀S,s,s',f. ∀tr:trace_label_label S f s s'. match f with [ doesnt_end_with_ret ⇒ as_costed S s' | ends_with_ret ⇒ True ]. #S #s #s' #f #tr cases tr #f #start #end #tr' #C @(trace_any_label_label … tr') qed. definition tll_tl_label : ∀S : abstract_status.∀st1,st2. trace_label_label S doesnt_end_with_ret st1 st2 → costlabel ≝ λS,st1,st2,tr.as_label_safe … «st2, trace_label_label_label … tr». lemma trace_any_call_call : ∀S,s,s'. trace_any_call S s s' → as_classifier S s' cl_call ∨ as_classifier S s' cl_tailcall. #S #s #s' #T elim T [1,3: //] #H73 #H74 #H75 #H76 #H77 #H78 #H79 #H80 #H81 #H82 #H83 // qed. (* (* an trace of unlabeled and cl_other states, possibly empty *) inductive trace_no_label_any (S:abstract_status) : S → S → Type[0] ≝ | tna_base : ∀start_status: S. ¬as_costed … start_status → trace_no_label_any S start_status start_status | tna_step : ∀status_pre: S. ∀status_init: S. ∀status_end: S. as_execute S status_pre status_init → as_classifier S status_pre cl_other → ¬as_costed … status_pre → trace_no_label_any S status_init status_end → trace_no_label_any S status_pre status_end. let rec tna_append_tna S st1 st2 st3 (taa1 : trace_no_label_any S st1 st2) on taa1 : trace_no_label_any S st2 st3 → trace_no_label_any S st1 st3 ≝ match taa1 with [ tna_base st1' H ⇒ λtaa2.taa2 | tna_step st1' st2' st3' H G K tl ⇒ λtaa2.tna_step ???? H G K (tna_append_tna … tl taa2) ]. definition tna_non_empty ≝ λS,st1,st2.λtna : trace_no_label_any S st1 st2. match tna with [ tna_base _ _ ⇒ false | tna_step _ _ _ _ _ _ _ ⇒ true ]. coercion tna_to_bool : ∀S,st1,st2.∀tna:trace_no_label_any S st1 st2.bool ≝ tna_non_empty on _tna : trace_no_label_any ??? to bool. lemma tna_unlabelled : ∀S,st1,st2.trace_no_label_any S st1 st2 → ¬as_costed … st1. #S #st1 #st2 * [#st #H @H | #st #st' #st'' #_ #_ #H #_ @H] qed. let rec tna_append_tal S st1 fl st2 st3 (tna : trace_no_label_any S st1 st2) on tna : trace_any_label S fl st2 st3 → if tna then Not (as_costed … st2) else True → trace_any_label S fl st1 st3 ≝ match tna return λst1,st2.λx : trace_no_label_any S st1 st2. ∀fl,st3. trace_any_label S fl st2 st3 → if x then Not (as_costed … st2) else True → trace_any_label S fl st1 st3 with [ tna_base st1' H ⇒ λfl,st3,taa2,prf.taa2 | tna_step st1' st2' st3' H G K tl ⇒ λfl,st3,taa2,prf. tal_step_default ????? H (tna_append_tal ????? tl taa2 ?) G (tna_unlabelled … tl) ] fl st3. cases (tna_non_empty … tl) [@prf|%] qed. *) inductive trace_any_any (S : abstract_status) : S → S → Type[0] ≝ | taa_base : ∀st.trace_any_any S st st | taa_step : ∀st1,st2,st3. as_execute S st1 st2 → as_classifier S st1 cl_other → ¬as_costed S st2 → trace_any_any S st2 st3 → trace_any_any S st1 st3. definition taa_non_empty ≝ λS,st1,st2.λtaa : trace_any_any S st1 st2. match taa with [ taa_base _ ⇒ false | taa_step _ _ _ _ _ _ _ ⇒ true ]. coercion taa_to_bool : ∀S,st1,st2.∀taa:trace_any_any S st1 st2.bool ≝ taa_non_empty on _taa : trace_any_any ??? to bool. let rec taa_append_tal S st1 fl st2 st3 (taa : trace_any_any S st1 st2) on taa : trace_any_label S fl st2 st3 → trace_any_label S fl st1 st3 ≝ match taa return λst1,st2.λx : trace_any_any S st1 st2. ∀fl,st3. trace_any_label S fl st2 st3 → trace_any_label S fl st1 st3 with [ taa_base st1' ⇒ λfl,st3,tal2.tal2 | taa_step st1' st2' st3' H G K tl ⇒ λfl,st3,tal2. tal_step_default ????? H (taa_append_tal ????? tl tal2) G K ] fl st3. interpretation "trace any any label append" 'append taa tal = (taa_append_tal ????? taa tal). let rec tal_collapsable S fl s1 s2 (tal : trace_any_label S fl s1 s2) on tal : Prop ≝ match tal with [ tal_base_not_return _ _ _ _ _ ⇒ True | tal_step_default fl1 _ st1' st1'' _ tl1 _ _ ⇒ tal_collapsable … tl1 | _ ⇒ False ]. let rec tlr_rel S1 st1 st1' S2 st2 st2' (tlr1 : trace_label_return S1 st1 st1') (tlr2 : trace_label_return S2 st2 st2') on tlr1 : Prop ≝ match tlr1 with [ tlr_base st1 st1' tll1 ⇒ match tlr2 with [ tlr_base st2 st2' tll2 ⇒ tll_rel … tll1 tll2 | _ ⇒ False ] | tlr_step st1 st1' st1'' tll1 tl1 ⇒ match tlr2 with [ tlr_step st2 st2' st2'' tll2 tl2 ⇒ tll_rel … tll1 tll2 ∧ tlr_rel … tl1 tl2 | _ ⇒ False ] ] and tll_rel S1 fl1 st1 st1' S2 fl2 st2 st2' (tll1 : trace_label_label S1 fl1 st1 st1') (tll2 : trace_label_label S2 fl2 st2 st2') on tll1 : Prop ≝ match tll1 with [ tll_base fl1 st1 st1' tal1 H ⇒ match tll2 with [ tll_base fl2 st2 st2 tal2 G ⇒ pi1 … (as_label_safe … («?, H»)) = pi1 … (as_label_safe … («?, G»)) ∧ tal_rel … tal1 tal2 ] ] and tal_rel S1 fl1 st1 st1' S2 fl2 st2 st2' (tal1 : trace_any_label S1 fl1 st1 st1') (tal2 : trace_any_label S2 fl2 st2 st2') on tal1 : Prop ≝ match tal1 with [ tal_base_not_return st1 st1' _ _ _ ⇒ fl2 = doesnt_end_with_ret ∧ ∃st2mid,taa,H,G,K. tal2 ≃ taa_append_tal ? st2 ??? taa (tal_base_not_return ? st2mid st2' H G K) | tal_base_return st1 st1' _ _ ⇒ fl2 = ends_with_ret ∧ ∃st2mid,taa,H,G. tal2 ≃ taa_append_tal ? st2 ? st2mid st2' taa (tal_base_return ? st2mid st2' H G) | tal_base_call st1 st1' st1'' _ prf _ tlr1 _ ⇒ fl2 = doesnt_end_with_ret ∧ ∃st2mid,G.as_call_ident S2 («st2mid, G») = as_call_ident ? «st1, prf» ∧ ∃taa : trace_any_any ? st2 st2mid.∃st2mid',H. (* we must allow a tal_base_call to be similar to a call followed by a collapsable trace (trace_any_any followed by a base_not_return; we cannot use trace_any_any as it disallows labels in the end as soon as it is non-empty) *) (∃K.∃tlr2 : trace_label_return ? st2mid' st2'.∃L. tal2 ≃ taa @ (tal_base_call … H G K tlr2 L) ∧ tlr_rel … tlr1 tlr2) ∨ ∃st2mid'',K.∃tlr2 : trace_label_return ? st2mid' st2mid''.∃L. ∃tl2 : trace_any_label … doesnt_end_with_ret st2mid'' st2'. tal2 ≃ taa @ (tal_step_call … H G K tlr2 L tl2) ∧ tlr_rel … tlr1 tlr2 ∧ tal_collapsable … tl2 | tal_base_tailcall st1 st1' st1'' _ prf tlr1 ⇒ fl2 = ends_with_ret ∧ ∃st2mid,G.as_tailcall_ident S2 («st2mid, G») = as_tailcall_ident ? «st1, prf» ∧ ∃taa : trace_any_any ? st2 st2mid.∃st2mid',H. ∃tlr2 : trace_label_return ? st2mid' st2'. tal2 ≃ taa @ (tal_base_tailcall … H G tlr2) ∧ tlr_rel … tlr1 tlr2 | tal_step_call fl1 st1 st1' st1'' st1''' _ prf _ tlr1 _ tl1 ⇒ ∃st2mid,G.as_call_ident S2 («st2mid, G») = as_call_ident ? «st1, prf» ∧ ∃taa : trace_any_any ? st2 st2mid.∃st2mid',H. (fl2 = doesnt_end_with_ret ∧ ∃K.∃tlr2 : trace_label_return ? st2mid' st2'.∃L. tal2 ≃ taa @ tal_base_call … H G K tlr2 L ∧ tal_collapsable … tl1 ∧ tlr_rel … tlr1 tlr2) ∨ ∃st2mid'',K.∃tlr2 : trace_label_return ? st2mid' st2mid''.∃L. ∃tl2 : trace_any_label ? fl2 st2mid'' st2'. tal2 ≃ taa @ (tal_step_call … H G K tlr2 L tl2) ∧ tal_rel … tl1 tl2 ∧ tlr_rel … tlr1 tlr2 | tal_step_default fl1 st1 st1' st1'' _ tl1 _ _ ⇒ tal_rel … tl1 tal2 (* <- this makes it many to many *) ]. interpretation "trace any label rel" 'napart t1 t2 = (tal_rel ???????? t1 t2). interpretation "trace label label rel" 'napart t1 t2 = (tll_rel ???????? t1 t2). interpretation "trace label return rel" 'napart t1 t2 = (tlr_rel ?????? t1 t2). let rec tal_collapsable_eq_fl S1 fl1 s1 s1' (tal1 : trace_any_label S1 fl1 s1 s1') on tal1 : tal_collapsable … tal1 → fl1 = doesnt_end_with_ret ≝ match tal1 return λfl1,s1,s1',tal1. tal_collapsable S1 fl1 s1 s1' tal1 → fl1 = doesnt_end_with_ret with [ tal_base_not_return _ _ _ _ _ ⇒ λ_.refl … | tal_step_default fl1 _ st1' st1'' _ tl1 _ _ ⇒ tal_collapsable_eq_fl … tl1 | _ ⇒ Ⓧ ]. let rec tal_rel_eq_fl S1 fl1 s1 s1' S2 fl2 s2 s2' (tal1 : trace_any_label S1 fl1 s1 s1') on tal1 : ∀tal2 : trace_any_label S2 fl2 s2 s2'.tal_rel … tal1 tal2 → fl1 = fl2 ≝ match tal1 return λfl1,s1,s1',tal1.? with [ tal_base_not_return st1 st1' _ _ _ ⇒ let BASE_NR ≝ 0 in ? | tal_base_return st1 st1' _ _ ⇒ let BASE_R ≝ 0 in ? | tal_base_call st1 st1' st1'' _ _ _ tlr1 _ ⇒ let BASE_C ≝ 0 in ? | tal_base_tailcall st1 st1' st1'' _ _ tlr1 ⇒ let BASE_TC ≝ 0 in ? | tal_step_call flg1 st1 st1' st1'' st1''' _ _ _ tlr1 _ tl1 ⇒ let STEP_C ≝ 0 in ? | tal_step_default flg1 st1 st1' st1'' _ tl1 _ _ ⇒ let STEP ≝ 0 in ? ]. -fl1 -s1 -s1' [1,2,3,4: -tal_rel_eq_fl #tal2 * // | #tal2 * #s2_mid * #G2 * #call * #taa2 * #s2' *#H2 * [ * #EQ1 *#K2 *#tlr2 *#L2 ** #_ #coll #_ >(tal_collapsable_eq_fl … coll) // | * #s2_mid' *#K2 *#tlr2 *#L2 *#tl2 ** #_ #step #_ @(tal_rel_eq_fl … step) ] | #tal2 whd in ⊢ (%→?); #step @(tal_rel_eq_fl … step) ] qed. let rec taa_rel_inv S1 fl1 st1 st1mid st1' S2 fl2 st2 st2' (taa1 : trace_any_any S1 st1 st1mid) on taa1 : ∀tal1 : trace_any_label S1 fl1 st1mid st1'. ∀tal2 : trace_any_label S2 fl2 st2 st2'. tal_rel … (taa1 @ tal1) tal2 → tal_rel … tal1 tal2 ≝ ?. cases taa1 -taa1 [ -taa_rel_inv // | #st #st' #st'' #H #G #K #tl #tal1 #tal2 whd in ⊢ (%→?); @(taa_rel_inv … tl) ] qed. lemma taa_append_collapsable : ∀S,s1,fl,s2,s3. ∀taa,tal.tal_collapsable S fl s2 s3 tal → tal_collapsable S fl s1 s3 (taa@tal). #S #s1 #fl #s2 #s3 #taa elim taa -s1 -s2 /2/ qed. let rec tal_rel_collapsable S1 fl1 s1 s1' S2 fl2 s2 s2' (tal1 : trace_any_label S1 fl1 s1 s1') on tal1 : ∀tal2 : trace_any_label S2 fl2 s2 s2'.tal_collapsable … tal1 → tal_rel … tal1 tal2 → tal_collapsable … tal2 ≝ match tal1 return λfl1,s1,s1',tal1.? with [ tal_base_not_return st1 st1' _ _ _ ⇒ let BASE_NR ≝ 0 in ? | tal_base_return st1 st1' _ _ ⇒ let BASE_R ≝ 0 in ? | tal_base_call st1 st1' st1'' _ _ _ tlr1 _ ⇒ let BASE_C ≝ 0 in ? | tal_base_tailcall st1 st1' st1'' _ _ tlr1 ⇒ let BASE_TC ≝ 0 in ? | tal_step_call flg1 st1 st1' st1'' st1''' _ _ _ tlr1 _ tl1 ⇒ let STEP_C ≝ 0 in ? | tal_step_default flg1 st1 st1' st1'' _ tl1 _ _ ⇒ let STEP ≝ 0 in ? ]. -fl1 -s1 -s1' [1,2,3,4: -tal_rel_collapsable #tal2 * * #EQ * #s2 * #taa2 *#H *#G *#K #EQ' destruct @taa_append_collapsable % | #tal2 * | #tal2 #tal2 whd in ⊢ (%→?); #step @(tal_rel_collapsable … step) assumption ] qed. inductive intensional_event : Type[0] ≝ | IEVcost : costlabel → intensional_event | IEVcall : ident → intensional_event | IEVtailcall : ident → ident → intensional_event | IEVret : ident → intensional_event. (* NB: we don't restrict call idents, because it would need some more tinkering with abstract_status *) definition as_emittable : abstract_status → intensional_event → Prop ≝ λS,ev. match ev with [ IEVcost c ⇒ as_cost_labelled S c | _ ⇒ True ]. definition as_trace ≝ λS : abstract_status. Σl : list intensional_event.All … (as_emittable S) l. unification hint 0 ≔ S ⊢ as_trace S ≡ Sig (list intensional_event) (λl.All … (as_emittable S) l). definition cons_safe : ∀A,P.(Σx.P x) → (Σl.All A P l) → Σl.All A P l ≝ λA,P,x,l.«x::l, conj … (pi2 … x) (pi2 … l)». definition append_safe : ∀A,P.(Σl.All A P l) → (Σl.All A P l) → Σl.All A P l ≝ λA,P,l1,l2.«l1@l2, cic:/matita/cerco/utilities/lists/All_append.def(2) … (pi2 … l1) (pi2 … l2)». definition nil_safe : ∀A,P.(Σl.All A P l) ≝ λA,P.«[ ], I». interpretation "safe consing" 'vcons hd tl = (cons_safe ?? hd tl). interpretation "safe appending" 'vappend l1 l2 = (append_safe ?? l1 l2). interpretation "safe nil" 'vnil = (nil_safe ??). definition emittable_cost : ∀S.as_cost_label S → Σev.as_emittable S ev ≝ λS,l.«IEVcost l, pi2 … l». let rec observables_trace_label_label (S: abstract_status) (trace_ends_flag: trace_ends_with_ret) (start_status: S) (final_status: S) (the_trace: trace_label_label S trace_ends_flag start_status final_status) (curr : ident) on the_trace: as_trace S ≝ match the_trace with [ tll_base ends_flag initial final given_trace labelled_proof ⇒ let label ≝ as_label_safe … «?, labelled_proof» in emittable_cost … label ::: observables_trace_any_label S ends_flag initial final given_trace curr ] and observables_trace_any_label (S: abstract_status) (trace_ends_flag: trace_ends_with_ret) (start_status: S) (final_status: S) (the_trace: trace_any_label S trace_ends_flag start_status final_status) (curr : ident) on the_trace: as_trace S ≝ match the_trace with [ tal_base_not_return the_status _ _ _ _ ⇒ [[ ]] | tal_base_call pre_fun_call start_fun_call final _ cl _ call_trace _ ⇒ let id ≝ as_call_ident ? «?, cl» in IEVcall id ::: observables_trace_label_return ??? call_trace id | tal_base_tailcall pre_fun_call start_fun_call final _ cl call_trace ⇒ let id ≝ as_tailcall_ident ? «?, cl» in IEVtailcall curr id ::: observables_trace_label_return … call_trace id | tal_base_return the_status _ _ _ ⇒ [[ IEVret curr]] | tal_step_call end_flag pre_fun_call start_fun_call after_fun_call final _ cl _ call_trace _ final_trace ⇒ let id ≝ as_call_ident ? «?, cl» in let call_cost_trace ≝ observables_trace_label_return … call_trace id in let final_cost_trace ≝ observables_trace_any_label … end_flag … final_trace curr in IEVcall id ::: call_cost_trace @@ final_cost_trace | tal_step_default end_flag status_pre status_init status_end _ tail_trace _ _ ⇒ observables_trace_any_label … tail_trace curr ] and observables_trace_label_return (S: abstract_status) (start_status: S) (final_status: S) (the_trace: trace_label_return S start_status final_status) (curr : ident) on the_trace: as_trace S ≝ match the_trace with [ tlr_base before after trace_to_lift ⇒ observables_trace_label_label … trace_to_lift curr | tlr_step initial labelled final labelled_trace ret_trace ⇒ let labelled_cost ≝ observables_trace_label_label … doesnt_end_with_ret … labelled_trace curr in let return_cost ≝ observables_trace_label_return … ret_trace curr in labelled_cost @@ return_cost ]. % qed. let rec filter_map X Y (f : X → option Y) (l : list X) on l : list Y ≝ match l with [ nil ⇒ [ ] | cons hd tl ⇒ match f hd with [ Some y ⇒ [ y ] | None ⇒ [ ] ] @ filter_map … f tl ]. lemma filter_map_append: ∀X,Y,f,l1,l2. filter_map X Y f (l1@l2) = filter_map … f l1 @ filter_map … f l2. #X #Y #f #l1 elim l1 // #hd #tl #IH #l2 normalize >IH // qed. lemma map_to_filter_map : ∀X,Y,f,l.map X Y f l = filter_map X Y (λx.Some ? (f x)) l. #X #Y #f #l elim l normalize // qed. lemma filter_map_compose : ∀X,Y,Z,f,g,l. filter_map Y Z f (filter_map X Y g l) = filter_map X Z (λx.! y ← g x ; f y) l. #X #Y #Z #f #g #l elim l [%] #hd #tl #IH whd in ⊢ (??(????%)%); cases (g ?) normalize nodelta [2: #y >m_return_bind whd in ⊢ (??%?); @eq_f2 [ % ]] @IH qed. lemma filter_map_ext_eq : ∀X,Y,f,g,l. (∀x.f x = g x) → filter_map X Y f l = filter_map X Y g l. #X #Y #f #g #l #H elim l normalize [%] #hd #tl #IH >H @eq_f @IH qed. let rec list_distribute_sig_aux X P (l : list X) on l : All X P l → list (Σx.P x) ≝ match l return λl.All X P l → list (Σx.P x) with [ nil ⇒ λ_.[ ] | cons hd tl ⇒ λprf.«hd, proj1 … prf» :: list_distribute_sig_aux X P tl (proj2 … prf) ]. definition list_distribute_sig : ∀A,P.(Σl.All A P l) → list (Σx.P x) ≝ λA,P,l.list_distribute_sig_aux … l (pi2 … l). lemma list_distribute_sig_append: ∀A,P,l1,l2,prf1,prf2,prf3. list_distribute_sig A P «l1@l2,prf3» = list_distribute_sig … «l1,prf1» @ list_distribute_sig … «l2,prf2». #A #P #l1 whd in match list_distribute_sig; normalize nodelta elim l1 // #hd #tl #IH normalize #l2 #prf1 #prf2 #prf3 EQ >taa_append_tal_same_observables | whd in ⊢ (%→??(???%)?); @tal_rel_to_traces_same_observables ] whd in ⊢ (??%%); [1,2: % |3,5: >call >(tlr_rel_to_traces_same_observables … H_tlr) % |4,6: >call >(tal_collapsable_observables … H_tl) >append_nil >(tlr_rel_to_traces_same_observables … H_tlr) % |7: >call >(tlr_rel_to_traces_same_observables … H_tlr) >(tal_rel_to_traces_same_observables … H_tl) % ] |*: cases the_trace_r [1,3: #st_before_r #st_after_r #tll_r [ @tll_rel_to_traces_same_observables | * ] |*: #st_init_r #st_labld_r #st_fin_r #tll_r #tlr_r * #H_tll #H_tlr whd in ⊢ (??%%); >(tll_rel_to_traces_same_observables … H_tll) >(tlr_rel_to_traces_same_observables … H_tlr) % ] ] qed. (* cost maps specifics: summing over flattened traces *) (* lemma lift_cost_map_same_cost_tal : ∀S_in, S_out, dec, m_out, f_in, f_out, start_in, start_out, end_in, end_out. ∀the_trace_in : trace_any_label S_in f_in start_in end_in. ∀the_trace_out : trace_any_label S_out f_out start_out end_out. tal_rel … the_trace_in the_trace_out → (Σ_{l ∈ flatten_trace_any_label … the_trace_in} (lift_cost_map_id … dec m_out l)) = (Σ_{l ∈ flatten_trace_any_label … the_trace_out} (m_out l)). #S_in #S_out #dec #m_out #f_in #f_out #start_in #start_out #end_in #end_out #tal_in #tal_out #H_tal @(lift_cost_map_same_cost … (tal_rel_to_traces_same_flatten … H_tal)) qed. lemma lift_cost_map_same_cost_tll : ∀S_in, S_out, dec, m_out, f_in, f_out, start_in, start_out, end_in, end_out. ∀the_trace_in : trace_label_label S_in f_in start_in end_in. ∀the_trace_out : trace_label_label S_out f_out start_out end_out. tll_rel … the_trace_in the_trace_out → (Σ_{l ∈ flatten_trace_label_label … the_trace_in} (lift_cost_map_id … dec m_out l)) = (Σ_{l ∈ flatten_trace_label_label … the_trace_out} (m_out l)). #S_in #S_out #dec #m_out #f_in #f_out #start_in #start_out #end_in #end_out #tll_in #tll_out #H_tll @(lift_cost_map_same_cost … (tll_rel_to_traces_same_flatten … H_tll)) qed. *) lemma map_pi1_distribute_sig : ∀A,P,l. map ?? (pi1 ??) (list_distribute_sig A P l) = pi1 ?? l. #A #P * #l elim l -l normalize // qed. definition flatten_trace_label_return ≝ λS,st1,st2.λtlr : trace_label_return S st1 st2. (* as cost events do not depend on current function identifier, we can use a dummy one *) let dummy ≝ an_identifier ? one in costlabels_of_observables … (observables_trace_label_return … tlr dummy). definition flatten_trace_label_label ≝ λS,flag,st1,st2.λtll : trace_label_label S flag st1 st2. (* as cost events do not depend on current function identifier, we can use a dummy one *) let dummy ≝ an_identifier ? one in costlabels_of_observables … (observables_trace_label_label … tll dummy). definition flatten_trace_any_label ≝ λS,flag,st1,st2.λtll : trace_any_label S flag st1 st2. (* as cost events do not depend on current function identifier, we can use a dummy one *) let dummy ≝ an_identifier ? one in costlabels_of_observables … (observables_trace_any_label … tll dummy). lemma lift_cost_map_same_cost_tlr : ∀S_in, S_out, dec, m_out, start_in, start_out, end_in, end_out. ∀the_trace_in : trace_label_return S_in start_in end_in. ∀the_trace_out : trace_label_return S_out start_out end_out. tlr_rel … the_trace_in the_trace_out → (Σ_{l ∈ flatten_trace_label_return … the_trace_in} (lift_cost_map_id … dec m_out l)) = (Σ_{l ∈ flatten_trace_label_return … the_trace_out} (m_out l)). #S_in #S_out #dec #m_out #start_in #start_out #end_in #end_out #tlr_in #tlr_out #H_tlr @lift_cost_map_same_cost whd in match flatten_trace_label_return; normalize nodelta >map_to_filter_map >map_to_filter_map >filter_map_compose >filter_map_compose cut (∀S.∀x: (Σev.as_emittable S ev). ! y ← match pi1 ?? x return λx.as_emittable S x → option (Σc.as_cost_labelled S c) with [ IEVcost c ⇒ λprf.Some ? «c, prf» | _ ⇒ λ_.None ? ] (pi2 ?? x) ; Some ? (pi1 ?? y) = ! ev ← Some ? (pi1 ?? x) ; match ev with [ IEVcost c ⇒ Some ? c | _ ⇒ None ?]) [ #S ** [ #c #em | #i * | #i #j * | #i * ] %] #EQext >(filter_map_ext_eq … (EQext S_in)) >(filter_map_ext_eq … (EQext S_out)) map_pi1_distribute_sig >map_pi1_distribute_sig @(tlr_rel_to_traces_same_observables … H_tlr) % qed. lemma lift_cost_map_same_cost_tll : ∀S_in, S_out, dec, m_out, fl_in, fl_out, start_in, start_out, end_in, end_out. ∀the_trace_in : trace_label_label S_in fl_in start_in end_in. ∀the_trace_out : trace_label_label S_out fl_out start_out end_out. tll_rel … the_trace_in the_trace_out → (Σ_{l ∈ flatten_trace_label_label … the_trace_in} (lift_cost_map_id … dec m_out l)) = (Σ_{l ∈ flatten_trace_label_label … the_trace_out} (m_out l)). #S_in #S_out #dec #m_out #fl_in #fl_out #start_in #start_out #end_in #end_out #tlr_in #tlr_out #H_tlr @lift_cost_map_same_cost whd in match flatten_trace_label_label; normalize nodelta >map_to_filter_map >map_to_filter_map >filter_map_compose >filter_map_compose cut (∀S.∀x: (Σev.as_emittable S ev). ! y ← match pi1 ?? x return λx.as_emittable S x → option (Σc.as_cost_labelled S c) with [ IEVcost c ⇒ λprf.Some ? «c, prf» | _ ⇒ λ_.None ? ] (pi2 ?? x) ; Some ? (pi1 ?? y) = ! ev ← Some ? (pi1 ?? x) ; match ev with [ IEVcost c ⇒ Some ? c | _ ⇒ None ?]) [ #S ** [ #c #em | #i * | #i #j * | #i * ] %] #EQext >(filter_map_ext_eq … (EQext S_in)) >(filter_map_ext_eq … (EQext S_out)) map_pi1_distribute_sig >map_pi1_distribute_sig @(tll_rel_to_traces_same_observables … H_tlr) % qed.