Theory DRA_Construction

(*
    Author:   Benedikt Seidl
    License:  BSD
*)

section ‹Constructing DRAs for LTL Formulas›

theory DRA_Construction
  imports
    Transition_Functions

    "../Quotient_Type"
    "../Omega_Words_Fun_Stream"

    "HOL-Library.Log_Nat"

    "../Logical_Characterization/Master_Theorem"
    "../Logical_Characterization/Restricted_Master_Theorem"

    Transition_Systems_and_Automata.DBA_Combine
    Transition_Systems_and_Automata.DCA_Combine
    Transition_Systems_and_Automata.DRA_Combine
begin

― ‹We use prefix and suffix on infinite words.›
hide_const Sublist.prefix Sublist.suffix

locale dra_construction = transition_functions eq normalise + quotient eq Rep Abs
  for
    eq :: "'a ltln  'a ltln  bool" (infix  75)
  and
    normalise :: "'a ltln  'a ltln"
  and
    Rep :: "'ltlq  'a ltln"
  and
    Abs :: "'a ltln  'ltlq"
begin

subsection ‹Lifting Setup›

abbreviation truen_lifted :: "'ltlq" (↑truen) where
  "↑truen  Abs truen"

abbreviation falsen_lifted :: "'ltlq" (↑falsen) where
  "↑falsen  Abs falsen"

abbreviation af_letter_lifted :: "'a set  'ltlq  'ltlq" (↑afletter) where
  "↑afletter ν φ  Abs (af_letter (Rep φ) ν)"

abbreviation af_lifted :: "'ltlq  'a set list  'ltlq" (↑af) where
  "↑af φ w  fold ↑afletter w φ"

abbreviation GF_advice_lifted :: "'ltlq  'a ltln set  'ltlq" (‹_↑[_]ν [90,60] 89) where
  "φ↑[X]ν  Abs ((Rep φ)[X]ν)"


lemma af_letter_lifted_semantics:
  "↑afletter ν (Abs φ) = Abs (af_letter φ ν)"
  by (metis Rep_Abs_eq af_letter_congruent Abs_eq)

lemma af_lifted_semantics:
  "↑af (Abs φ) w = Abs (af φ w)"
  by (induction w rule: rev_induct) (auto simp: Abs_eq, insert Rep_Abs_eq af_letter_congruent eq_sym, blast)

lemma af_lifted_range:
  "range (↑af (Abs φ))  {Abs ψ | ψ. nested_prop_atoms ψ  nested_prop_atoms φ}"
  using af_lifted_semantics af_nested_prop_atoms by blast


definition af_letterF_lifted :: "'a ltln  'a set  'ltlq  'ltlq" (↑afletterF)
where
  "↑afletterF φ ν ψ  Abs (af_letterF φ (Rep ψ) ν)"

definition af_letterG_lifted :: "'a ltln  'a set  'ltlq  'ltlq" (↑afletterG)
where
  "↑afletterG φ ν ψ  Abs (af_letterG φ (Rep ψ) ν)"

lemma af_letterF_lifted_semantics:
  "↑afletterF φ ν (Abs ψ) = Abs (af_letterF φ ψ ν)"
  by (metis af_letterF_lifted_def Rep_inverse af_letterF_def af_letter_congruent Abs_eq)

lemma af_letterG_lifted_semantics:
  "↑afletterG φ ν (Abs ψ) = Abs (af_letterG φ ψ ν)"
  by (metis af_letterG_lifted_def Rep_inverse af_letterG_def af_letter_congruent Abs_eq)

abbreviation afF_lifted :: "'a ltln  'ltlq  'a set list  'ltlq" (↑afF)
where
  "↑afF φ ψ w  fold (↑afletterF φ) w ψ"

abbreviation afG_lifted :: "'a ltln  'ltlq  'a set list  'ltlq" (↑afG)
where
  "↑afG φ ψ w  fold (↑afletterG φ) w ψ"

lemma afF_lifted_semantics:
  "↑afF φ (Abs ψ) w = Abs (afF φ ψ w)"
  by (induction w rule: rev_induct) (auto simp: af_letterF_lifted_semantics)

lemma afG_lifted_semantics:
  "↑afG φ (Abs ψ) w = Abs (afG φ ψ w)"
  by (induction w rule: rev_induct) (auto simp: af_letterG_lifted_semantics)


definition af_letterν_lifted :: "'a ltln set  'a set  'ltlq × 'ltlq  'ltlq × 'ltlq" (↑afletterν)
where
  "↑afletterν X ν p 
    (Abs (fst (af_letterν X (Rep (fst p), Rep (snd p)) ν)),
     Abs (snd (af_letterν X (Rep (fst p), Rep (snd p)) ν)))"

abbreviation afν_lifted :: "'a ltln set  'ltlq × 'ltlq  'a set list  'ltlq × 'ltlq" (↑afν)
where
  "↑afν X p w  fold (↑afletterν X) w p"

lemma af_letterν_lifted_semantics:
  "↑afletterν X ν (Abs x, Abs y) = (Abs (fst (af_letterν X (x, y) ν)), Abs (snd (af_letterν X (x, y) ν)))"
  by (simp add: af_letterν_def af_letterν_lifted_def) (insert GF_advice_congruent Rep_Abs_eq Rep_inverse af_letter_lifted_semantics eq_trans Abs_eq, blast)

lemma afν_lifted_semantics:
  "↑afν X (Abs ξ, Abs ζ) w = (Abs (fst (afν X (ξ, ζ) w)), Abs (snd (afν X (ξ, ζ) w)))"
  apply (induction w rule: rev_induct) 
   apply (auto simp: af_letterν_lifted_def af_letterν_lifted_semantics af_letter_lifted_semantics)
  by (metis (no_types, opaque_lifting) af_letterν_lifted_def afν_fst af_letterν_lifted_semantics eq_fst_iff prod.sel(2))



subsection ‹Büchi automata for basic languages›

definition 𝔄μ :: "'a ltln  ('a set, 'ltlq) dba" where
  "𝔄μ φ = dba UNIV (Abs φ) ↑afletter (λψ. ψ = ↑truen)"

definition 𝔄μ_GF :: "'a ltln  ('a set, 'ltlq) dba" where
  "𝔄μ_GF φ = dba UNIV (Abs (Fn φ)) (↑afletterF φ) (λψ. ψ = ↑truen)"

definition 𝔄ν :: "'a ltln  ('a set, 'ltlq) dca" where
  "𝔄ν φ = dca UNIV (Abs φ) ↑afletter (λψ. ψ = ↑falsen)"

definition 𝔄ν_FG :: "'a ltln  ('a set, 'ltlq) dca" where
  "𝔄ν_FG φ = dca UNIV (Abs (Gn φ)) (↑afletterG φ) (λψ. ψ = ↑falsen)"


lemma dba_run:
  "DBA.run (dba UNIV p δ α) (to_stream w) p" unfolding dba.run_alt_def by simp

lemma dca_run:
  "DCA.run (dca UNIV p δ α) (to_stream w) p" unfolding dca.run_alt_def by simp


lemma 𝔄μ_language:
  "φ  μLTL  to_stream w  DBA.language (𝔄μ φ)  w n φ"
proof -
  assume "φ  μLTL"

  then have "w n φ  (n. kn. af φ (w[0  k])  truen)"
    by (meson af_μLTL af_prefix_true le_cases)

  also have "  (n. kn. af φ (w[0  Suc k])  truen)"
    by (meson af_prefix_true le_SucI order_refl)

  also have "  infs (λψ. ψ = ↑truen) (DBA.trace (𝔄μ φ) (to_stream w) (Abs φ))"
    by (simp add: infs_snth 𝔄μ_def DBA.transition_def af_lifted_semantics Abs_eq[symmetric] af_letter_lifted_semantics)

  also have "  to_stream w  DBA.language (𝔄μ φ)"
    unfolding 𝔄μ_def dba.initial_def dba.accepting_def by (auto simp: dba_run)

  finally show ?thesis
    by simp
qed

lemma 𝔄μ_GF_language:
  "φ  μLTL  to_stream w  DBA.language (𝔄μ_GF φ)  w n Gn (Fn φ)"
proof -
  assume "φ  μLTL"

  then have "w n Gn (Fn φ)  (n. k. af (Fn φ) (w[n  k]) L truen)"
    using ltl_lang_equivalence.af_μLTL_GF by blast

  also have "  (n. k>n. afF φ (Fn φ) (w[0  k])  truen)"
    using afF_semantics_ltr afF_semantics_rtl
    using φ  μLTL af_μLTL_GF calculation by blast

  also have "  (n. kn. afF φ (Fn φ) (w[0  Suc k])  truen)"
    by (metis less_Suc_eq_le less_imp_Suc_add)

  also have "  infs (λψ. ψ = ↑truen) (DBA.trace (𝔄μ_GF φ) (to_stream w) (Abs (Fn φ)))"
    by (simp add: infs_snth 𝔄μ_GF_def DBA.transition_def afF_lifted_semantics Abs_eq[symmetric] af_letterF_lifted_semantics)

  also have "  to_stream w  DBA.language (𝔄μ_GF φ)"
    unfolding 𝔄μ_GF_def dba.initial_def dba.accepting_def by (auto simp: dba_run)

  finally show ?thesis
    by simp
qed

lemma 𝔄ν_language:
  "φ  νLTL  to_stream w  DCA.language (𝔄ν φ)  w n φ"
proof -
  assume "φ  νLTL"

  then have "w n φ  (n. kn. ¬ af φ (w[0  k])  falsen)"
    by (meson af_νLTL af_prefix_false le_cases order_refl)

  also have "  (n. kn. ¬ af φ (w[0  Suc k])  falsen)"
    by (meson af_prefix_false le_SucI order_refl)

  also have "  fins (λψ. ψ = ↑falsen) (DCA.trace (𝔄ν φ) (to_stream w) (Abs φ))"
    by (simp add: infs_snth 𝔄ν_def DBA.transition_def af_lifted_semantics Abs_eq[symmetric] af_letter_lifted_semantics)

  also have "  to_stream w  DCA.language (𝔄ν φ)"
    unfolding 𝔄ν_def dca.initial_def dca.rejecting_def by (auto simp: dca_run)

  finally show ?thesis
    by simp
qed

lemma 𝔄ν_FG_language:
  "φ  νLTL  to_stream w  DCA.language (𝔄ν_FG φ)  w n Fn (Gn φ)"
proof -
  assume "φ  νLTL"

  then have "w n Fn (Gn φ)  (k. j. ¬ af (Gn φ) (w[k  j]) L falsen)"
    using ltl_lang_equivalence.af_νLTL_FG by blast

  also have "  (n. k>n. ¬ afG φ (Gn φ) (w[0  k])  falsen)"
    using afG_semantics_ltr afG_semantics_rtl
    using φ  νLTL af_νLTL_FG calculation by blast

  also have "  (n. kn. ¬ afG φ (Gn φ) (w[0  Suc k])  falsen)"
    by (metis less_Suc_eq_le less_imp_Suc_add)

  also have "  fins (λψ. ψ = ↑falsen) (DCA.trace (𝔄ν_FG φ) (to_stream w) (Abs (Gn φ)))"
    by (simp add: infs_snth 𝔄ν_FG_def DBA.transition_def afG_lifted_semantics Abs_eq[symmetric] af_letterG_lifted_semantics)

  also have "  to_stream w  DCA.language (𝔄ν_FG φ)"
    unfolding 𝔄ν_FG_def dca.initial_def dca.rejecting_def by (auto simp: dca_run)

  finally show ?thesis
    by simp
qed


subsection ‹A DCA checking the GF-advice Function›

definition  :: "'a ltln  'a ltln set  ('a set, 'ltlq × 'ltlq) dca" where
  " φ X = dca UNIV (Abs φ, Abs ((normalise φ)[X]ν)) (↑afletterν X) (λp. snd p = ↑falsen)"

lemma ℭ_language:
  "to_stream w  DCA.language ( φ X)  (i. suffix i w n af φ (prefix i w)[X]ν)"
proof -

  have "(i. suffix i w n af φ (prefix i w)[X]ν)
         (m. km. ¬ snd (afν X (φ, (normalise φ)[X]ν) (prefix (Suc k) w))  falsen)"
    using afν_semantics_ltr afν_semantics_rtl by blast

  also have "  fins (λp. snd p = ↑falsen) (DCA.trace ( φ X) (to_stream w) (Abs φ, Abs ((normalise φ)[X]ν)))"
    by(simp add: infs_snth ℭ_def DCA.transition_def afν_lifted_semantics af_letterν_lifted_semantics Abs_eq)

  also have "  to_stream w  DCA.language ( φ X)"
    by (simp add: ℭ_def dca.initial_def dca.rejecting_def dca.language_def dca_run)

  finally show ?thesis
    by blast
qed


subsection ‹A DRA for each combination of sets X and Y›

lemma dba_language:
  "(w. to_stream w  DBA.language 𝔄  w n φ)  DBA.language 𝔄 = {w. to_omega w n φ}"
  by (metis (mono_tags, lifting) Collect_cong dba.language_def mem_Collect_eq to_stream_to_omega)

lemma dca_language:
  "(w. to_stream w  DCA.language 𝔄  w n φ)  DCA.language 𝔄 = {w. to_omega w n φ}"
  by (metis (mono_tags, lifting) Collect_cong dca.language_def mem_Collect_eq to_stream_to_omega)


definition 𝔄1 :: "'a ltln  'a ltln list  ('a set, 'ltlq × 'ltlq) dca" where
  "𝔄1 φ xs =  φ (set xs)"

lemma 𝔄1_language:
  "to_omega ` DCA.language (𝔄1 φ xs) = L1 φ (set xs)"
  by (simp add: 𝔄1_def L1_def set_eq_iff ℭ_language)

lemma 𝔄1_alphabet:
  "DCA.alphabet (𝔄1 φ xs) = UNIV"
  unfolding 𝔄1_def ℭ_def by simp


definition 𝔄2 :: "'a ltln list  'a ltln list  ('a set, 'ltlq list degen) dba" where
  "𝔄2 xs ys = DBA_Combine.intersect_list (map (λψ. 𝔄μ_GF (ψ[set ys]μ)) xs)"

lemma 𝔄2_language:
  "to_omega ` DBA.language (𝔄2 xs ys) = L2 (set xs) (set ys)"
  by (simp add: 𝔄2_def L2_def set_eq_iff dba_language[OF 𝔄μ_GF_language[OF FG_advice_μLTL(1)]])

lemma 𝔄2_alphabet:
  "DBA.alphabet (𝔄2 xs ys) = UNIV"
  by (simp add: 𝔄2_def 𝔄μ_GF_def)


definition 𝔄3 :: "'a ltln list  'a ltln list  ('a set, 'ltlq list) dca" where
  "𝔄3 xs ys = DCA_Combine.intersect_list (map (λψ. 𝔄ν_FG (ψ[set xs]ν)) ys)"

lemma 𝔄3_language:
  "to_omega ` DCA.language (𝔄3 xs ys) = L3 (set xs) (set ys)"
  by (simp add: 𝔄3_def L3_def set_eq_iff dca_language[OF 𝔄ν_FG_language[OF GF_advice_νLTL(1)]])

lemma 𝔄3_alphabet:
  "DCA.alphabet (𝔄3 xs ys) = UNIV"
  by (simp add: 𝔄3_def 𝔄ν_FG_def)


definition "𝔄' φ xs ys = intersect_bc (𝔄2 xs ys) (DCA_Combine.intersect (𝔄1 φ xs) (𝔄3 xs ys))"

lemma 𝔄'_language:
  "to_omega ` DRA.language (𝔄' φ xs ys) = (L1 φ (set xs)  L2 (set xs) (set ys)  L3 (set xs) (set ys))"
  by (simp add: 𝔄'_def 𝔄1_language 𝔄2_language 𝔄3_language) fastforce

lemma 𝔄'_alphabet:
  "DRA.alphabet (𝔄' φ xs ys) = UNIV"
  by (simp add: 𝔄'_def 𝔄1_alphabet 𝔄2_alphabet 𝔄3_alphabet)



subsection ‹A DRA for @{term "L(φ)"}

text ‹
  This is the final constant constructing a deterministic Rabin automaton
  using the pure version of the  @{thm master_theorem}.
›

definition "ltl_to_dra φ = DRA_Combine.union_list (map (λ(xs, ys). 𝔄' φ xs ys) (advice_sets φ))"

lemma ltl_to_dra_language:
  "to_omega ` DRA.language (ltl_to_dra φ) = language_ltln φ"
proof -
  have "((a, b)set (advice_sets φ). dra.alphabet (𝔄' φ a b)) =
    ((a, b)set (advice_sets φ). dra.alphabet (𝔄' φ a b))"
    using advice_sets_not_empty by (simp add: 𝔄'_alphabet)
  then have *: "DRA.language (DRA_Combine.union_list (map (λ(x, y). 𝔄' φ x y) (advice_sets φ))) =
     (DRA.language ` set (map (λ(x, y). 𝔄' φ x y) (advice_sets φ)))"
    by (simp add: split_def)
  have "language_ltln φ =  {(L1 φ X  L2 X Y  L3 X Y) | X Y. X  subformulasμ φ  Y  subformulasν φ}"
    unfolding master_theorem_language by auto
  also have " =  {L1 φ (set xs)  L2 (set xs) (set ys)  L3 (set xs) (set ys) | xs ys. (xs, ys)  set (advice_sets φ)}"
    unfolding advice_sets_subformulas by (metis (no_types, lifting))
  also have " =  {to_omega ` DRA.language (𝔄' φ xs ys) | xs ys. (xs, ys)  set (advice_sets φ)}"
    by (simp add: 𝔄'_language)
  finally show ?thesis
    using * by (auto simp add: ltl_to_dra_def)
qed

lemma ltl_to_dra_alphabet:
  "alphabet (ltl_to_dra φ) = UNIV"
  by (auto simp: ltl_to_dra_def 𝔄'_alphabet)


subsection ‹A DRA for @{term "L(φ)"} with Restricted Advice Sets›

text ‹
  The following constant uses the @{thm master_theorem_restricted} to reduce
  the size of the resulting automaton.
›

definition "ltl_to_dra_restricted φ = DRA_Combine.union_list (map (λ(xs, ys). 𝔄' φ xs ys) (restricted_advice_sets φ))"

lemma ltl_to_dra_restricted_language:
  "to_omega ` DRA.language (ltl_to_dra_restricted φ) = language_ltln φ"
proof -
  have "((a, b)set (restricted_advice_sets φ). dra.alphabet (𝔄' φ a b)) =
    ((a, b)set (restricted_advice_sets φ). dra.alphabet (𝔄' φ a b))"
    using restricted_advice_sets_not_empty by (simp add: 𝔄'_alphabet)
  then have *: "DRA.language (DRA_Combine.union_list (map (λ(x, y). 𝔄' φ x y) (restricted_advice_sets φ))) =
     (DRA.language ` set (map (λ(x, y). 𝔄' φ x y) (restricted_advice_sets φ)))"
    by (simp add: split_def)
  have "language_ltln φ =  {(L1 φ X  L2 X Y  L3 X Y) | X Y. X  subformulasμ φ  restricted_subformulas φ  Y  subformulasν φ  restricted_subformulas φ}"
    unfolding master_theorem_restricted_language by auto
  also have " =  {L1 φ (set xs)  L2 (set xs) (set ys)  L3 (set xs) (set ys) | xs ys. (xs, ys)  set (restricted_advice_sets φ)}"
    unfolding restricted_advice_sets_subformulas by (metis (no_types, lifting))
  also have " =  {to_omega ` DRA.language (𝔄' φ xs ys) | xs ys. (xs, ys)  set (restricted_advice_sets φ)}"
    by (simp add: 𝔄'_language)
  finally show ?thesis
    using * by (auto simp add: ltl_to_dra_restricted_def)
qed

lemma ltl_to_dra_restricted_alphabet:
  "alphabet (ltl_to_dra_restricted φ) = UNIV"
  by (auto simp: ltl_to_dra_restricted_def 𝔄'_alphabet)


subsection ‹A DRA for @{term "L(φ)"} with a finite alphabet›

text ‹
  Until this point, we use @{term UNIV} as the alphabet in all places.
  To explore the automaton, however, we need a way to fix the alphabet
  to some finite set.
›

definition dra_set_alphabet :: "('a set, 'b) dra  'a set set  ('a set, 'b) dra"
where
  "dra_set_alphabet 𝔄 Σ = dra Σ (initial 𝔄) (transition 𝔄) (condition 𝔄)"

lemma dra_set_alphabet_language:
  "Σ  alphabet 𝔄  language (dra_set_alphabet 𝔄 Σ) = language 𝔄  {s. sset s  Σ}"
  by (auto simp add: dra_set_alphabet_def dra.language_def set_eq_iff dra.run_alt_def streams_iff_sset)

lemma dra_set_alphabet_alphabet[simp]:
  "alphabet (dra_set_alphabet 𝔄 Σ) = Σ"
  unfolding dra_set_alphabet_def by simp

lemma dra_set_alphabet_nodes:
  "Σ  alphabet 𝔄  DRA.nodes (dra_set_alphabet 𝔄 Σ)  DRA.nodes 𝔄"
  unfolding dra_set_alphabet_def dra.nodes_alt_def dra.reachable_alt_def dra.path_alt_def
  by auto


definition "ltl_to_dra_alphabet φ Ap = dra_set_alphabet (ltl_to_dra_restricted φ) (Pow Ap)"

lemma ltl_to_dra_alphabet_language:
  assumes
    "atoms_ltln φ  Ap"
  shows
    "to_omega ` language (ltl_to_dra_alphabet φ Ap) = language_ltln φ  {w. range w  Pow Ap}"
proof -
  have 1: "Pow Ap  alphabet (ltl_to_dra_restricted φ)"
    unfolding ltl_to_dra_restricted_alphabet by simp

  show ?thesis
    unfolding ltl_to_dra_alphabet_def dra_set_alphabet_language[OF 1]
    by (simp add: ltl_to_dra_restricted_language sset_range) force
qed

lemma ltl_to_dra_alphabet_alphabet[simp]:
  "alphabet (ltl_to_dra_alphabet φ Ap) = Pow Ap"
  unfolding ltl_to_dra_alphabet_def by simp

lemma ltl_to_dra_alphabet_nodes:
  "DRA.nodes (ltl_to_dra_alphabet φ Ap)  DRA.nodes (ltl_to_dra_restricted φ)"
  unfolding ltl_to_dra_alphabet_def
  by (rule dra_set_alphabet_nodes) (simp add: ltl_to_dra_restricted_alphabet)

end


subsection ‹Verified Bounds for Number of Nodes›

text ‹
  Using two additional assumptions, we can show a double-exponential size bound
  for the constructed automaton.
›

(* TODO add to HOL/Groups_List.thy *)
lemma list_prod_mono:
  "f  g  (xxs. f x)  (xxs. g x)" for f g :: "'a  nat"
  by (induction xs) (auto simp: le_funD mult_le_mono)

(* TODO add to HOL/Groups_List.thy *)
lemma list_prod_const:
  "(x. x  set xs  f x  c)  (xxs. f x)  c ^ length xs" for f :: "'a  nat"
  by (induction xs) (auto simp: mult_le_mono)

(* TODO add to HOL/Finite_Set.thy *)
lemma card_insert_Suc:
  "card (insert x S)  Suc (card S)"
  by (metis Suc_n_not_le_n card.infinite card_insert_if finite_insert linear)

(* TODO add to HOL/Power.thy *)
lemma nat_power_le_imp_le:
  "0 < a  a  b  x ^ a  x ^ b" for x :: nat
  by (metis leD linorder_le_less_linear nat_power_less_imp_less neq0_conv power_eq_0_iff)

(* TODO add to HOL/Power.thy *)
lemma const_less_power:
  "n < x ^ n" if "x > 1"
  using that by (induction n) (auto simp: less_trans_Suc)

(* TODO add to HOL-Library/Log_Nat.thy *)
lemma floorlog_le_const:
  "floorlog x n  n"
  by (induction n) (simp add: floorlog_eq_zero_iff, metis Suc_lessI floorlog_le_iff le_SucI power_inject_exp)


locale dra_construction_size = dra_construction + transition_functions_size +
  assumes
    equiv_finite: "finite P  finite {Abs ψ | ψ. prop_atoms ψ  P}"
  assumes
    equiv_card: "finite P  card {Abs ψ | ψ. prop_atoms ψ  P}  2 ^ 2 ^ card P"
begin

lemma afF_lifted_range:
  "nested_prop_atoms ψ  nested_prop_atoms (Fn φ)  range (↑afF φ (Abs ψ))   {Abs ψ | ψ. nested_prop_atoms ψ  nested_prop_atoms (Fn φ)}"
  using afF_lifted_semantics afF_nested_prop_atoms by blast

lemma afG_lifted_range:
  "nested_prop_atoms ψ  nested_prop_atoms (Gn φ)  range (↑afG φ (Abs ψ))   {Abs ψ | ψ. nested_prop_atoms ψ  nested_prop_atoms (Gn φ)}"
  using afG_lifted_semantics afG_nested_prop_atoms by blast


lemma 𝔄μ_nodes:
  "DBA.nodes (𝔄μ φ)  {Abs ψ | ψ. nested_prop_atoms ψ  nested_prop_atoms φ}"
  unfolding 𝔄μ_def
  using af_lifted_semantics af_nested_prop_atoms by fastforce

lemma 𝔄μ_GF_nodes:
  "DBA.nodes (𝔄μ_GF φ)  {Abs ψ | ψ. nested_prop_atoms ψ  nested_prop_atoms (Fn φ)}"
  unfolding 𝔄μ_GF_def dba.nodes_alt_def dba.reachable_alt_def
  using afF_nested_prop_atoms[of "Fn φ"] by (auto simp: afF_lifted_semantics)

lemma 𝔄ν_nodes:
  "DCA.nodes (𝔄ν φ)  {Abs ψ | ψ. nested_prop_atoms ψ  nested_prop_atoms φ}"
  unfolding 𝔄ν_def
  using af_lifted_semantics af_nested_prop_atoms by fastforce

lemma 𝔄ν_FG_nodes:
  "DCA.nodes (𝔄ν_FG φ)  {Abs ψ | ψ. nested_prop_atoms ψ  nested_prop_atoms (Gn φ)}"
  unfolding 𝔄ν_FG_def dca.nodes_alt_def dca.reachable_alt_def
  using afG_nested_prop_atoms[of "Gn φ"] by (auto simp: afG_lifted_semantics)

lemma ℭ_nodes_normalise:
  "DCA.nodes ( φ X)  {Abs ψ | ψ. nested_prop_atoms ψ  nested_prop_atoms φ} × {Abs ψ | ψ. nested_prop_atoms ψ  nested_prop_atomsν (normalise φ) X}"
  unfolding ℭ_def dca.nodes_alt_def dca.reachable_alt_def
  apply (auto simp add: afν_lifted_semantics af_letterν_lifted_semantics)
  using afν_fst_nested_prop_atoms apply force
  by (metis GF_advice_nested_prop_atomsν afν_snd_nested_prop_atoms Abs_eq afν_lifted_semantics fst_conv normalise_eq snd_conv sup.absorb_iff1)

lemma ℭ_nodes:
  "DCA.nodes ( φ X)  {Abs ψ | ψ. nested_prop_atoms ψ  nested_prop_atoms φ} × {Abs ψ | ψ. nested_prop_atoms ψ  nested_prop_atomsν φ X}"
  unfolding ℭ_def dca.nodes_alt_def dca.reachable_alt_def
  apply (auto simp add: afν_lifted_semantics af_letterν_lifted_semantics)
  using afν_fst_nested_prop_atoms apply force
  by (metis (no_types, opaque_lifting) GF_advice_nested_prop_atomsν afν_snd_nested_prop_atoms fst_eqD nested_prop_atomsν_subset normalise_nested_propos order_refl order_trans snd_eqD sup.order_iff)



lemma equiv_subset:
  "{Abs ψ |ψ. nested_prop_atoms ψ  P}  {Abs ψ |ψ. prop_atoms ψ  P}"
  using prop_atoms_nested_prop_atoms by blast

lemma equiv_finite':
  "finite P  finite {Abs ψ | ψ. nested_prop_atoms ψ  P}"
  using equiv_finite equiv_subset finite_subset by fast

lemma equiv_card':
  "finite P  card {Abs ψ | ψ. nested_prop_atoms ψ  P}  2 ^ 2 ^ card P"
  by (metis (mono_tags, lifting) equiv_card equiv_subset equiv_finite card_mono le_trans)


lemma nested_prop_atoms_finite:
  "finite {Abs ψ | ψ. nested_prop_atoms ψ  nested_prop_atoms φ}"
  using equiv_finite'[OF Equivalence_Relations.nested_prop_atoms_finite] .

lemma nested_prop_atoms_card:
  "card {Abs ψ | ψ. nested_prop_atoms ψ  nested_prop_atoms φ}  2 ^ 2 ^ card (nested_prop_atoms φ)"
  using equiv_card'[OF Equivalence_Relations.nested_prop_atoms_finite] .

lemma nested_prop_atomsν_finite:
  "finite {Abs ψ | ψ. nested_prop_atoms ψ  nested_prop_atomsν φ X}"
  using equiv_finite'[OF nested_prop_atomsν_finite] by fast

lemma nested_prop_atomsν_card:
  "card {Abs ψ | ψ. nested_prop_atoms ψ  nested_prop_atomsν φ X}  2 ^ 2 ^ card (nested_prop_atoms φ)" (is "?lhs  ?rhs")
proof -
  have "finite {Abs ψ | ψ. prop_atoms ψ  nested_prop_atomsν φ X}"
    by (simp add: nested_prop_atomsν_finite Advice.nested_prop_atomsν_finite equiv_finite)

  then have "?lhs  card {Abs ψ | ψ. prop_atoms ψ  (nested_prop_atomsν φ X)}"
    using card_mono equiv_subset by blast

  also have "  2 ^ 2 ^ card (nested_prop_atomsν φ X)"
    using equiv_card[OF Advice.nested_prop_atomsν_finite] by fast

  also have "  ?rhs"
    using nested_prop_atomsν_card by auto

  finally show ?thesis .
qed


lemma 𝔄μ_GF_nodes_finite:
  "finite (DBA.nodes (𝔄μ_GF φ))"
  using finite_subset[OF 𝔄μ_GF_nodes nested_prop_atoms_finite] .

lemma 𝔄ν_FG_nodes_finite:
  "finite (DCA.nodes (𝔄ν_FG φ))"
  using finite_subset[OF 𝔄ν_FG_nodes nested_prop_atoms_finite] .

lemma 𝔄μ_GF_nodes_card:
  "card (DBA.nodes (𝔄μ_GF φ))  2 ^ 2 ^ card (nested_prop_atoms (Fn φ))"
  using le_trans[OF card_mono[OF nested_prop_atoms_finite 𝔄μ_GF_nodes] nested_prop_atoms_card] .

lemma 𝔄ν_FG_nodes_card:
  "card (DCA.nodes (𝔄ν_FG φ))  2 ^ 2 ^ card (nested_prop_atoms (Gn φ))"
  using le_trans[OF card_mono[OF nested_prop_atoms_finite 𝔄ν_FG_nodes] nested_prop_atoms_card] .


lemma 𝔄2_nodes_finite_helper:
  "list_all (finite  DBA.nodes) (map (λψ. 𝔄μ_GF (ψ[set ys]μ)) xs)"
  by (auto simp: list.pred_map list_all_iff 𝔄μ_GF_nodes_finite)

lemma 𝔄2_nodes_finite:
  "finite (DBA.nodes (𝔄2 xs ys))"
  unfolding 𝔄2_def using DBA_Combine.intersect_list_nodes_finite 𝔄2_nodes_finite_helper .

lemma 𝔄3_nodes_finite_helper:
  "list_all (finite  DCA.nodes) (map (λψ. 𝔄ν_FG (ψ[set xs]ν)) ys)"
  by (auto simp: list.pred_map list_all_iff 𝔄ν_FG_nodes_finite)

lemma 𝔄3_nodes_finite:
  "finite (DCA.nodes (𝔄3 xs ys))"
  unfolding 𝔄3_def using DCA_Combine.intersect_list_nodes_finite 𝔄3_nodes_finite_helper .

lemma 𝔄2_nodes_card:
  assumes
    "length xs  n"
  and
    "ψ. ψ  set xs  card (nested_prop_atoms ψ)  n"
  shows
    "card (DBA.nodes (𝔄2 xs ys))  2 ^ 2 ^ (n + floorlog 2 n + 2)"
proof -
  have 1: "ψ. ψ  set xs  card (nested_prop_atoms (Fn ψ[set ys]μ))  Suc n"
  proof -
    fix ψ
    assume "ψ  set xs"

    have "card (nested_prop_atoms (Fn (ψ[set ys]μ)))
           Suc (card (nested_prop_atoms (ψ[set ys]μ)))"
      by (simp add: card_insert_Suc)

    also have "  Suc (card (nested_prop_atoms ψ))"
      by (simp add: FG_advice_nested_prop_atoms_card)

    also have "  Suc n"
      by (simp add: assms(2) ψ  set xs)

    finally show "card (nested_prop_atoms (Fn (ψ[set ys]μ)))  Suc n" .
  qed

  have "(ψxs. card (DBA.nodes (𝔄μ_GF (ψ[set ys]μ))))
         (ψxs. 2 ^ 2 ^ card (nested_prop_atoms (Fn (ψ[set ys]μ))))"
    by (rule list_prod_mono) (insert 𝔄μ_GF_nodes_card le_fun_def, blast)

  also have "  (2 ^ 2 ^ Suc n) ^ length xs"
    by (rule list_prod_const) (metis 1 Suc_leI nat_power_le_imp_le nat_power_eq_Suc_0_iff neq0_conv pos2 zero_less_power)

  also have "  (2 ^ 2 ^ Suc n) ^ n"
    using assms(1) nat_power_le_imp_le by fastforce

  also have " = 2 ^ (n * 2 ^ Suc n)"
    by (metis Groups.mult_ac(2) power_mult)

  also have "  2 ^ (2 ^ floorlog 2 n * 2 ^ Suc n)"
    by (cases "n = 0") (auto simp: floorlog_bounds less_imp_le_nat)

  also have " = 2 ^ 2 ^ (Suc n + floorlog 2 n)"
    by (simp add: power_add)

  finally have 2: "(ψxs. card (DBA.nodes (𝔄μ_GF (ψ[set ys]μ))))  2 ^ 2 ^ (Suc n + floorlog 2 n)" .

  have "card (DBA.nodes (𝔄2 xs ys))  max 1 (length xs) * (ψxs. card (DBA.nodes (𝔄μ_GF (ψ[set ys]μ))))"
    using DBA_Combine.intersect_list_nodes_card[OF 𝔄2_nodes_finite_helper]
    by (auto simp: 𝔄2_def comp_def)

  also have "  max 1 n * 2 ^ 2 ^ (Suc n + floorlog 2 n)"
    using assms(1) 2 by (simp add: mult_le_mono)

  also have "  2 ^ (floorlog 2 n) * 2 ^ 2 ^ (Suc n + floorlog 2 n)"
    by (cases "n = 0") (auto simp: floorlog_bounds less_imp_le_nat)

  also have " = 2 ^ (floorlog 2 n + 2 ^ (Suc n + floorlog 2 n))"
    by (simp add: power_add)

  also have "  2 ^ (n + 2 ^ (Suc n + floorlog 2 n))"
    by (simp add: floorlog_le_const)

  also have "  2 ^ 2 ^ (n + floorlog 2 n + 2)"
    by simp (metis const_less_power Suc_1 add_Suc_right add_leE lessI less_imp_le_nat power_Suc)

  finally show ?thesis .
qed


lemma 𝔄3_nodes_card:
  assumes
    "length ys  n"
  and
    "ψ. ψ  set ys  card (nested_prop_atoms ψ)  n"
  shows
    "card (DCA.nodes (𝔄3 xs ys))  2 ^ 2 ^ (n + floorlog 2 n + 1)"
proof -
  have 1: "ψ. ψ  set ys  card (DCA.nodes (𝔄ν_FG (ψ[set xs]ν)))  2 ^ 2 ^ Suc n"
  proof -
    fix ψ
    assume "ψ  set ys"

    have "card (nested_prop_atoms (Gn ψ[set xs]ν))
           Suc (card (nested_prop_atoms (ψ[set xs]ν)))"
      by (simp add: card_insert_Suc)

    also have "  Suc (card (nested_prop_atoms ψ))"
      by (simp add: GF_advice_nested_prop_atoms_card)

    also have "  Suc n"
      by (simp add: assms(2) ψ  set ys)

    finally have 2: "card (nested_prop_atoms (Gn ψ[set xs]ν))  Suc n" .

    then show "?thesis ψ"
      by (intro le_trans[OF 𝔄ν_FG_nodes_card]) (meson one_le_numeral power_increasing)
  qed


  have "card (DCA.nodes (𝔄3 xs ys))  (ψys. card (DCA.nodes (𝔄ν_FG (ψ[set xs]ν))))"
    unfolding 𝔄3_def using DCA_Combine.intersect_list_nodes_card[OF 𝔄3_nodes_finite_helper]
    by (auto simp: comp_def)

  also have "  (2 ^ 2 ^ Suc n) ^ length ys"
    by (rule list_prod_const) (rule 1)

  also have "  (2 ^ 2 ^ Suc n) ^ n"
    by (simp add: assms(1) power_increasing)

  also have "  2 ^ (n * 2 ^ Suc n)"
    by (metis le_refl mult.commute power_mult)

  also have "  2 ^ (2 ^ floorlog 2 n * 2 ^ Suc n)"
    by (cases n > 0) (simp_all add: floorlog_bounds less_imp_le_nat)

  also have " = 2 ^ 2 ^ (n + floorlog 2 n + 1)"
    by (simp add: power_add)

  finally show ?thesis .
qed


lemma 𝔄1_nodes_finite:
  "finite (DCA.nodes (𝔄1 φ xs))"
  unfolding 𝔄1_def
  by (metis (no_types, lifting) finite_subset ℭ_nodes finite_SigmaI nested_prop_atomsν_finite nested_prop_atoms_finite)

lemma 𝔄1_nodes_card:
  assumes
    "card (subfrmlsn φ)  n"
  shows
    "card (DCA.nodes (𝔄1 φ xs))  2 ^ 2 ^ (n + 1)"
proof -
  let ?fst = "{Abs ψ | ψ. nested_prop_atoms ψ  nested_prop_atoms φ}"
  let ?snd = "{Abs ψ | ψ. nested_prop_atoms ψ  nested_prop_atomsν φ (set xs)}"

  have 1: "card (nested_prop_atoms φ)  n"
    by (meson card_mono[OF subfrmlsn_finite nested_prop_atoms_subfrmlsn] assms le_trans)


  have "card (DCA.nodes (𝔄1 φ xs))  card (?fst × ?snd)"
    unfolding 𝔄1_def
    by (rule card_mono) (simp_all add: ℭ_nodes nested_prop_atomsν_finite nested_prop_atoms_finite)

  also have " = card ?fst * card ?snd"
    using nested_prop_atomsν_finite card_cartesian_product by blast

  also have "  2 ^ 2 ^ card (nested_prop_atoms φ) * 2 ^ 2 ^ card (nested_prop_atoms φ)"
    using nested_prop_atomsν_card nested_prop_atoms_card mult_le_mono by blast

  also have " = 2 ^ 2 ^ (card (nested_prop_atoms φ) + 1)"
    by (simp add: semiring_normalization_rules(36))

  also have "  2 ^ 2 ^ (n + 1)"
    using assms 1 by simp

  finally show ?thesis .
qed


lemma 𝔄'_nodes_finite:
  "finite (DRA.nodes (𝔄' φ xs ys))"
  unfolding 𝔄'_def
  using intersect_nodes_finite intersect_bc_nodes_finite
  using 𝔄1_nodes_finite 𝔄2_nodes_finite 𝔄3_nodes_finite
  by fast

lemma 𝔄'_nodes_card:
  assumes
    "length xs  n"
  and
    "ψ. ψ  set xs  card (nested_prop_atoms ψ)  n"
  and
    "length ys  n"
  and
    "ψ. ψ  set ys  card (nested_prop_atoms ψ)  n"
  and
    "card (subfrmlsn φ)  n"
  shows
    "card (DRA.nodes (𝔄' φ xs ys))  2 ^ 2 ^ (n + floorlog 2 n + 4)"
proof -
  have "n + 1  n + floorlog 2 n + 2"
    by auto

  then have 1: "(2::nat) ^ (n + 1)  2 ^ (n + floorlog 2 n + 2)"
    using one_le_numeral power_increasing by blast


  have "card (DRA.nodes (𝔄' φ xs ys))  card (DCA.nodes (𝔄1 φ xs)) * card (DBA.nodes (𝔄2 xs ys)) * card (DCA.nodes (𝔄3 xs ys))" (is "?lhs  ?rhs")
  proof (unfold 𝔄'_def)
    have "card (DBA.nodes (𝔄2 xs ys)) * card (DCA.nodes (DCA_Combine.intersect (𝔄1 φ xs) (𝔄3 xs ys)))  ?rhs"
      by (simp add: intersect_nodes_card[OF 𝔄1_nodes_finite 𝔄3_nodes_finite])
    then show "card (DRA.nodes (intersect_bc (𝔄2 xs ys) (DCA_Combine.intersect (𝔄1 φ xs) (𝔄3 xs ys))))  ?rhs"
      by (meson intersect_bc_nodes_card[OF 𝔄2_nodes_finite intersect_nodes_finite[OF 𝔄1_nodes_finite 𝔄3_nodes_finite]] basic_trans_rules(23))
  qed

  also have "  2 ^ 2 ^ (n + 1) * 2 ^ 2 ^ (n + floorlog 2 n + 2) * 2 ^ 2 ^ (n + floorlog 2 n + 1)"
    using 𝔄1_nodes_card[OF assms(5)] 𝔄2_nodes_card[OF assms(1,2)] 𝔄3_nodes_card[OF assms(3,4)]
    by (metis mult_le_mono)

  also have " = 2 ^ (2 ^ (n + 1) + 2 ^ (n + floorlog 2 n + 2) + 2 ^ (n + floorlog 2 n + 1))"
    by (metis power_add)

  also have "  2 ^ (4 * 2 ^ (n + floorlog 2 n + 2))"
    using 1 by auto

  finally show ?thesis
    by (simp add: numeral.simps(2) power_add)
qed

lemma subformula_nested_prop_atoms_subfrmlsn:
  "ψ  subfrmlsn φ  nested_prop_atoms ψ  subfrmlsn φ"
  using nested_prop_atoms_subfrmlsn subfrmlsn_subset by blast


lemma ltl_to_dra_nodes_finite:
  "finite (DRA.nodes (ltl_to_dra φ))"
  unfolding ltl_to_dra_def
  apply (rule DRA_Combine.union_list_nodes_finite)
  apply (simp add: split_def 𝔄'_alphabet advice_sets_not_empty)
  apply (simp add: list.pred_set split_def 𝔄'_nodes_finite)
  done

lemma ltl_to_dra_restricted_nodes_finite:
  "finite (DRA.nodes (ltl_to_dra_restricted φ))"
  unfolding ltl_to_dra_restricted_def
  apply (rule DRA_Combine.union_list_nodes_finite)
  apply (simp add: split_def 𝔄'_alphabet advice_sets_not_empty)
  apply (simp add: list.pred_set split_def 𝔄'_nodes_finite)
  done

lemma ltl_to_dra_alphabet_nodes_finite:
  "finite (DRA.nodes (ltl_to_dra_alphabet φ AP))"
  using ltl_to_dra_alphabet_nodes ltl_to_dra_restricted_nodes_finite finite_subset by fast


lemma ltl_to_dra_nodes_card:
  assumes
    "card (subfrmlsn φ)  n"
  shows
    "card (DRA.nodes (ltl_to_dra φ))  2 ^ 2 ^ (2 * n + floorlog 2 n + 4)"
proof -
  let ?map = "map (λ(x, y). 𝔄' φ x y) (advice_sets φ)"

  have 1: "x::nat. x > 0  x ^ length (advice_sets φ)  x ^ 2 ^ card (subfrmlsn φ)"
    by (metis advice_sets_length linorder_not_less nat_power_less_imp_less)

  have "card (DRA.nodes (ltl_to_dra φ))  prod_list (map (card  DRA.nodes) ?map)"
    unfolding ltl_to_dra_def
    apply (rule DRA_Combine.union_list_nodes_card)
    unfolding list.pred_set using 𝔄'_nodes_finite by auto

  also have " = ((x, y)advice_sets φ. card (DRA.nodes (𝔄' φ x y)))"
    by (induction "advice_sets φ") (auto, metis (no_types, lifting) comp_apply split_def)

  also have "  (2 ^ 2 ^ (n + floorlog 2 n + 4)) ^ length (advice_sets φ)"
  proof (rule list_prod_const, unfold split_def, rule 𝔄'_nodes_card)
    show "x. x  set (advice_sets φ)  length (fst x)  n"
      using advice_sets_element_length assms by fastforce

    show "x ψ. x  set (advice_sets φ); ψ  set (fst x)  card (nested_prop_atoms ψ)  n"
      using advice_sets_element_subfrmlsn(1) assms subformula_nested_prop_atoms_subfrmlsn subformulasμ_subfrmlsn
      by (metis (no_types, lifting) card_mono subfrmlsn_finite subset_iff sup.absorb_iff2 sup.coboundedI1 surjective_pairing)

    show "x. x  set (advice_sets φ)  length (snd x)  n"
      using advice_sets_element_length assms by fastforce

    show "x ψ. x  set (advice_sets φ); ψ  set (snd x)  card (nested_prop_atoms ψ)  n"
      using advice_sets_element_subfrmlsn(2) assms subformula_nested_prop_atoms_subfrmlsn subformulasν_subfrmlsn
      by (metis (no_types, lifting) card_mono subfrmlsn_finite subset_iff sup.absorb_iff2 sup.coboundedI1 surjective_pairing)
  qed (insert assms, blast)

  also have "  (2 ^ 2 ^ (n + floorlog 2 n + 4)) ^ (2 ^ card (subfrmlsn φ))"
    by (simp add: 1)

  also have "  (2 ^ 2 ^ (n + floorlog 2 n + 4)) ^ (2 ^ n)"
    by (simp add: assms power_increasing)

  also have " = 2 ^ (2 ^ n * 2 ^ (n + floorlog 2 n + 4))"
    by (simp add: ac_simps power_mult [symmetric])

  also have " = 2 ^ 2 ^ (2 * n + floorlog 2 n + 4)"
    by (simp add: power_add) (simp add: mult_2 power_add)

  finally show ?thesis .
qed

text ‹We verify the size bound of the automaton to be double exponential.›

theorem ltl_to_dra_size:
  "card (DRA.nodes (ltl_to_dra φ))  2 ^ 2 ^ (2 * size φ + floorlog 2 (size φ) + 4)"
  using ltl_to_dra_nodes_card subfrmlsn_card by blast

end

end