Theory Trace
theory Trace
  imports "HOL-Library.Stream"
begin
section ‹Traces and trace prefixes›
subsection ‹Infinite traces›
coinductive ssorted :: "'a :: linorder stream ⇒ bool" where
  "shd s ≤ shd (stl s) ⟹ ssorted (stl s) ⟹ ssorted s"
lemma ssorted_siterate[simp]: "(⋀n. n ≤ f n) ⟹ ssorted (siterate f n)"
  by (coinduction arbitrary: n) auto
lemma ssortedD: "ssorted s ⟹ s !! i ≤ stl s !! i"
  by (induct i arbitrary: s) (auto elim: ssorted.cases)
lemma ssorted_sdrop: "ssorted s ⟹ ssorted (sdrop i s)"
  by (coinduction arbitrary: i s) (auto elim: ssorted.cases ssortedD)
lemma ssorted_monoD: "ssorted s ⟹ i ≤ j ⟹ s !! i ≤ s !! j"
proof (induct "j - i" arbitrary: j)
  case (Suc x)
  from Suc(1)[of "j - 1"] Suc(2-4) ssortedD[of s "j - 1"]
    show ?case by (cases j) (auto simp: le_Suc_eq Suc_diff_le)
qed simp
lemma sorted_stake: "ssorted s ⟹ sorted (stake i s)"
  by (induct i arbitrary: s)
    (auto elim: ssorted.cases simp: in_set_conv_nth
      intro!: ssorted_monoD[of _ 0, simplified, THEN order_trans, OF _ ssortedD])
lemma ssorted_monoI: "∀i j. i ≤ j ⟶ s !! i ≤ s !! j ⟹ ssorted s"
  by (coinduction arbitrary: s)
    (auto dest: spec2[of _ "Suc _" "Suc _"] spec2[of _ 0 "Suc 0"])
lemma ssorted_iff_mono: "ssorted s ⟷ (∀i j. i ≤ j ⟶ s !! i ≤ s !! j)"
  using ssorted_monoI ssorted_monoD by metis
lemma ssorted_iff_le_Suc: "ssorted s ⟷ (∀i. s !! i ≤ s !! Suc i)"
  using mono_iff_le_Suc[of "snth s"] by (simp add: mono_def ssorted_iff_mono)
definition "sincreasing s = (∀x. ∃i. x < s !! i)"
lemma sincreasingI: "(⋀x. ∃i. x < s !! i) ⟹ sincreasing s"
  by (simp add: sincreasing_def)
lemma sincreasing_grD:
  fixes x :: "'a :: semilattice_sup"
  assumes "sincreasing s"
  shows "∃j>i. x < s !! j"
proof -
  let ?A = "insert x {s !! n | n. n ≤ i}"
  from assms obtain j where *: "Sup_fin ?A < s !! j"
    by (auto simp: sincreasing_def)
  then have "x < s !! j"
    by (rule order.strict_trans1[rotated]) (auto intro: Sup_fin.coboundedI)
  moreover have "i < j"
  proof (rule ccontr)
    assume "¬ i < j"
    then have "s !! j ∈ ?A" by (auto simp: not_less)
    then have "s !! j ≤ Sup_fin ?A"
      by (auto intro: Sup_fin.coboundedI)
    with * show False by simp
  qed
  ultimately show ?thesis by blast
qed
lemma sincreasing_siterate_nat[simp]:
  fixes n :: nat
  assumes "(⋀n. n < f n)"
  shows "sincreasing (siterate f n)"
unfolding sincreasing_def proof
  fix x
  show "∃i. x < siterate f n !! i"
  proof (induction x)
    case 0
    have "0 < siterate f n !! 1"
      using order.strict_trans1[OF le0 assms] by simp
    then show ?case ..
  next
    case (Suc x)
    then obtain i where "x < siterate f n !! i" ..
    then have "Suc x < siterate f n !! Suc i"
      using order.strict_trans1[OF _ assms] by (simp del: snth.simps)
    then show ?case ..
  qed
qed
lemma sincreasing_stl: "sincreasing s ⟹ sincreasing (stl s)" for s :: "'a :: semilattice_sup stream"
  by (auto 0 4 simp: gr0_conv_Suc intro!: sincreasingI dest: sincreasing_grD[of s 0])
typedef 'a trace = "{s :: ('a set × nat) stream. ssorted (smap snd s) ∧ sincreasing (smap snd s)}"
  by (intro exI[of _ "smap (λi. ({}, i)) nats"])
    (auto simp: stream.map_comp stream.map_ident cong: stream.map_cong)
setup_lifting type_definition_trace
lift_definition Γ :: "'a trace ⇒ nat ⇒ 'a set" is
  "λs i. fst (s !! i)" .
lift_definition τ :: "'a trace ⇒ nat ⇒ nat" is
  "λs i. snd (s !! i)" .
lemma stream_eq_iff: "s = s' ⟷ (∀n. s !! n = s' !! n)"
  by (metis stream.map_cong0 stream_smap_nats)
lemma trace_eqI: "(⋀i. Γ σ i = Γ σ' i) ⟹ (⋀i. τ σ i = τ σ' i) ⟹ σ = σ'"
  by transfer (auto simp: stream_eq_iff intro!: prod_eqI)
lemma τ_mono[simp]: "i ≤ j ⟹ τ s i ≤ τ s j"
  by transfer (auto simp: ssorted_iff_mono)
lemma ex_le_τ: "∃j≥i. x ≤ τ s j"
  by (transfer fixing: i x) (auto dest!: sincreasing_grD[of _ i x] less_imp_le)
lemma le_τ_less: "τ σ i ≤ τ σ j ⟹ j < i ⟹ τ σ i = τ σ j"
  by (simp add: antisym)
lemma less_τD: "τ σ i < τ σ j ⟹ i < j"
  by (meson τ_mono less_le_not_le not_le_imp_less)
abbreviation "Δ s i ≡ τ s i - τ s (i - 1)"
lift_definition map_Γ :: "('a set ⇒ 'b set) ⇒ 'a trace ⇒ 'b trace" is
  "λf s. smap (λ(x, i). (f x, i)) s"
  by (auto simp: stream.map_comp prod.case_eq_if cong: stream.map_cong)
lemma Γ_map_Γ[simp]: "Γ (map_Γ f s) i = f (Γ s i)"
  by transfer (simp add: prod.case_eq_if)
lemma τ_map_Γ[simp]: "τ (map_Γ f s) i = τ s i"
  by transfer (simp add: prod.case_eq_if)
lemma map_Γ_id[simp]: "map_Γ id s = s"
  by transfer (simp add: stream.map_id)
lemma map_Γ_comp: "map_Γ g (map_Γ f s) = map_Γ (g ∘ f) s"
  by transfer (simp add: stream.map_comp comp_def prod.case_eq_if case_prod_beta')
lemma map_Γ_cong: "σ⇩1 = σ⇩2 ⟹ (⋀x. f⇩1 x = f⇩2 x) ⟹ map_Γ f⇩1 σ⇩1 = map_Γ f⇩2 σ⇩2"
  by transfer (auto intro!: stream.map_cong)
subsection ‹Finite trace prefixes›
typedef 'a prefix = "{p :: ('a set × nat) list. sorted (map snd p)}"
  by (auto intro!: exI[of _ "[]"])
setup_lifting type_definition_prefix
lift_definition pmap_Γ :: "('a set ⇒ 'b set) ⇒ 'a prefix ⇒ 'b prefix" is
  "λf. map (λ(x, i). (f x, i))"
  by (simp add: split_beta comp_def)
lift_definition last_ts :: "'a prefix ⇒ nat" is
  "λp. (case p of [] ⇒ 0 | _ ⇒ snd (last p))" .
lift_definition first_ts :: "nat ⇒ 'a prefix ⇒ nat" is
  "λn p. (case p of [] ⇒ n | _ ⇒ snd (hd p))" .
lift_definition pnil :: "'a prefix" is "[]" by simp
lift_definition plen :: "'a prefix ⇒ nat" is "length" .
lift_definition psnoc :: "'a prefix ⇒ 'a set × nat ⇒ 'a prefix" is
  "λp x. if (case p of [] ⇒ 0 | _ ⇒ snd (last p)) ≤ snd x then p @ [x] else []"
proof (goal_cases sorted_psnoc)
  case (sorted_psnoc p x)
  then show ?case
    by (induction p) (auto split: if_splits list.splits)
qed
instantiation prefix :: (type) order begin
lift_definition less_eq_prefix :: "'a prefix ⇒ 'a prefix ⇒ bool" is
  "λp q. ∃r. q = p @ r" .
definition less_prefix :: "'a prefix ⇒ 'a prefix ⇒ bool" where
  "less_prefix x y = (x ≤ y ∧ ¬ y ≤ x)"
instance
proof (standard, goal_cases less refl trans antisym)
  case (less x y)
  then show ?case unfolding less_prefix_def ..
next
  case (refl x)
  then show ?case by transfer auto
next
  case (trans x y z)
  then show ?case by transfer auto
next
  case (antisym x y)
  then show ?case by transfer auto
qed
end
lemma psnoc_inject[simp]:
  "last_ts p ≤ snd x ⟹ last_ts q ≤ snd y ⟹ psnoc p x = psnoc q y ⟷ (p = q ∧ x = y)"
  by transfer auto
lift_definition prefix_of :: "'a prefix ⇒ 'a trace ⇒ bool" is "λp s. stake (length p) s = p" .
lemma prefix_of_pnil[simp]: "prefix_of pnil σ"
  by transfer auto
lemma plen_pnil[simp]: "plen pnil = 0"
  by transfer auto
lemma prefix_of_pmap_Γ[simp]: "prefix_of π σ ⟹ prefix_of (pmap_Γ f π) (map_Γ f σ)"
  by transfer auto
lemma plen_mono: "π ≤ π' ⟹ plen π ≤ plen π'"
  by transfer auto
lemma prefix_of_psnocE: "prefix_of (psnoc p x) s ⟹ last_ts p ≤ snd x ⟹
  (prefix_of p s ⟹ Γ s (plen p) = fst x ⟹ τ s (plen p) = snd x ⟹ P) ⟹ P"
  by transfer (simp del: stake.simps add: stake_Suc)
lemma le_pnil[simp]: "pnil ≤ π"
  by transfer auto
lift_definition take_prefix :: "nat ⇒ 'a trace ⇒ 'a prefix" is stake
  by (auto dest: sorted_stake)
lemma plen_take_prefix[simp]: "plen (take_prefix i σ) = i"
  by transfer auto
lemma plen_psnoc[simp]: "last_ts π ≤ snd x ⟹ plen (psnoc π x) = plen π + 1"
  by transfer auto
lemma prefix_of_take_prefix[simp]: "prefix_of (take_prefix i σ) σ"
  by transfer auto
lift_definition pdrop :: "nat ⇒ 'a prefix ⇒ 'a prefix" is drop
  by (auto simp: drop_map[symmetric] sorted_wrt_drop)
lemma pdrop_0[simp]: "pdrop 0 π = π"
  by transfer auto
lemma prefix_of_antimono: "π ≤ π' ⟹ prefix_of π' s ⟹ prefix_of π s"
  by transfer (auto simp del: stake_add simp add: stake_add[symmetric])
lemma prefix_of_imp_linear: "prefix_of π σ ⟹ prefix_of π' σ ⟹ π ≤ π' ∨ π' ≤ π"
proof transfer
  fix π π' and σ :: "('a set × nat) stream"
  assume assms: "stake (length π) σ = π" "stake (length π') σ = π'"
  show "(∃r. π' = π @ r) ∨ (∃r. π = π' @ r)"
  proof (cases "length π" "length π'" rule: le_cases)
    case le
    then have "π' = take (length π) π' @ drop (length π) π'"
      by simp
    moreover have "take (length π) π' = π"
      using assms le by (metis min.absorb1 take_stake)
    ultimately show ?thesis by auto
  next
    case ge
    then have "π = take (length π') π @ drop (length π') π"
      by simp
    moreover have "take (length π') π = π'"
      using assms ge by (metis min.absorb1 take_stake)
    ultimately show ?thesis by auto
  qed
qed
lemma ex_prefix_of: "∃s. prefix_of p s"
proof (transfer, intro bexI CollectI conjI)
  fix p :: "('a set × nat) list"
  assume *: "sorted (map snd p)"
  let ?σ = "p @- smap (Pair undefined) (fromN (snd (last p)))"
  show "stake (length p) ?σ = p" by (simp add: stake_shift)
  have le_last: "snd (p ! i) ≤ snd (last p)" if "i < length p" for i
    using sorted_nth_mono[OF *, of i "length p - 1"] that
    by (cases p) (auto simp: last_conv_nth nth_Cons')
  with * show "ssorted (smap snd ?σ)"
    by (force simp: ssorted_iff_mono sorted_iff_nth_mono shift_snth)
  show "sincreasing (smap snd ?σ)"
  proof (rule sincreasingI)
    fix x
    have "x < smap snd ?σ !! Suc (length p + x)"
      by simp (metis Suc_pred add.commute diff_Suc_Suc length_greater_0_conv less_add_Suc1 less_diff_conv)
    then show "∃i. x < smap snd ?σ !! i" ..
  qed
qed
lemma τ_prefix_conv: "prefix_of p s ⟹ prefix_of p s' ⟹ i < plen p ⟹ τ s i = τ s' i"
  by transfer (simp add: stake_nth[symmetric])
lemma Γ_prefix_conv: "prefix_of p s ⟹ prefix_of p s' ⟹ i < plen p ⟹ Γ s i = Γ s' i"
  by transfer (simp add: stake_nth[symmetric])
lemma sincreasing_sdrop:
  fixes s :: "('a :: semilattice_sup) stream"
  assumes "sincreasing s"
  shows "sincreasing (sdrop n s)"
proof (rule sincreasingI)
  fix x
  obtain i where "n < i" and "x < s !! i"
    using sincreasing_grD[OF assms] by blast
  then have "x < sdrop n s !! (i - n)"
    by (simp add: sdrop_snth)
  then show "∃i. x < sdrop n s !! i" ..
qed
lemma ssorted_shift:
  "ssorted (xs @- s) = (sorted xs ∧ ssorted s ∧ (∀x∈set xs. ∀y∈sset s. x ≤ y))"
proof safe
  assume *: "ssorted (xs @- s)"
  then show "sorted xs"
    by (auto simp: ssorted_iff_mono shift_snth sorted_iff_nth_mono split: if_splits)
  from ssorted_sdrop[OF *, of "length xs"] show "ssorted s"
    by (auto simp: sdrop_shift)
  fix x y assume "x ∈ set xs" "y ∈ sset s"
  then obtain i j where "i < length xs" "xs ! i = x" "s !! j = y"
    by (auto simp: set_conv_nth sset_range)
  with ssorted_monoD[OF *, of i "j + length xs"] show "x ≤ y" by auto
next
  assume "sorted xs" "ssorted s" "∀x∈set xs. ∀y∈sset s. x ≤ y"
  then show "ssorted (xs @- s)"
  proof (coinduction arbitrary: xs s)
    case (ssorted xs s)
    with ‹ssorted s› show ?case
      by (subst (asm) ssorted.simps) (auto 0 4 simp: neq_Nil_conv shd_sset intro: exI[of _ "_ # _"])
  qed
qed
lemma sincreasing_shift:
  assumes "sincreasing s"
  shows "sincreasing (xs @- s)"
proof (rule sincreasingI)
  fix x
  from assms obtain i where "x < s !! i"
    unfolding sincreasing_def by blast
  then have "x < (xs @- s) !! (length xs + i)"
    by simp
  then show "∃i. x < (xs @- s) !! i" ..
qed
lift_definition replace_prefix :: "'a prefix ⇒ 'a trace ⇒ 'a trace" is
   "λπ σ. if ssorted (smap snd (π @- sdrop (length π) σ)) then
     π @- sdrop (length π) σ else smap (λi. ({}, i)) nats"
  by (auto split: if_splits simp: stream.map_comp stream.map_ident sdrop_smap[symmetric]
    simp del: sdrop_smap intro!: sincreasing_shift sincreasing_sdrop cong: stream.map_cong)
lemma prefix_of_replace_prefix:
  "prefix_of (pmap_Γ f π) σ ⟹ prefix_of π (replace_prefix π σ)"
proof (transfer; safe; goal_cases)
  case (1 f π σ)
  then show ?case
    by (subst (asm) (2) stake_sdrop[symmetric, of _ "length π"])
      (auto 0 3 simp: ssorted_shift split_beta o_def stake_shift sdrop_smap[symmetric]
        ssorted_sdrop not_le simp del: sdrop_smap)
qed
lemma map_Γ_replace_prefix:
  "∀x. f (f x) = f x ⟹ prefix_of (pmap_Γ f π) σ ⟹ map_Γ f (replace_prefix π σ) = map_Γ f σ"
proof (transfer; safe; goal_cases)
  case (1 f π σ)
  then show ?case
    by (subst (asm) (2) stake_sdrop[symmetric, of σ "length π"],
        subst (3) stake_sdrop[symmetric, of σ "length π"])
      (auto simp: ssorted_shift split_beta o_def stake_shift sdrop_smap[symmetric] ssorted_sdrop
        not_le simp del: sdrop_smap cong: map_cong)
qed
lemma prefix_of_pmap_Γ_D:
  assumes "prefix_of (pmap_Γ f π) σ"
  shows "∃σ'. prefix_of π σ' ∧ prefix_of (pmap_Γ f π) (map_Γ f σ')"
proof -
  from assms(1) obtain σ' where 1: "prefix_of π σ'"
    using ex_prefix_of by blast
  then have "prefix_of (pmap_Γ f π) (map_Γ f σ')"
    by transfer simp
  with 1 show ?thesis by blast
qed
lemma prefix_of_map_Γ_D:
  assumes "prefix_of π' (map_Γ f σ)"
  shows "∃π''. π' = pmap_Γ f π'' ∧ prefix_of π'' σ"
  using assms
  by transfer (auto intro!: exI[of _ "stake (length _) _"] elim: sym dest: sorted_stake)
lift_definition pts :: "'a prefix ⇒ nat list" is "map snd" .
lemma pts_pmap_Γ[simp]: "pts (pmap_Γ f π) = pts π"
  by (transfer fixing: f) (simp add: split_beta)
end