Theory First_Order_Select

theory First_Order_Select
  imports 
    Selection_Function
    First_Order_Clause
    First_Order_Type_System
begin

type_synonym ('f, 'v, 'ty) typed_clause = "('f, 'v) atom clause × ('v, 'ty) var_types"

type_synonym 'f ground_select = "'f ground_atom clause  'f ground_atom clause"
type_synonym ('f, 'v) select = "('f, 'v) atom clause  ('f, 'v) atom clause"

definition is_select_grounding :: "('f, 'v) select  'f ground_select  bool" where 
  "select selectG.
        is_select_grounding select selectG = (clauseG. clause γ.
        clause.is_ground (clause  γ)   
        clauseG = clause.to_ground (clause  γ)  
        selectG clauseG = clause.to_ground ((select clause)  γ))"

lemma infinite_lists_per_length: "infinite {l :: ('a :: infinite) list. length (tl l) = y}"
proof(induction y)
  case 0

  show ?case
  proof
    assume a: "finite {l :: 'a list. length (tl l) = 0}"

    define f where "x:: 'a . f x  [x]"


    have "x y. f x = f y  x = y"
      unfolding f_def
      by (metis nth_Cons_0)

    moreover have "x. length (f x)  Suc 0"
      unfolding f_def
      by simp

    moreover have "x. length x = Suc 0  x  range f"
      unfolding f_def
      by (smt (z3) One_nat_def Suc_length_conv Suc_pred' diff_Suc_1 diff_is_0_eq' 
          length_0_conv nat.simps(3) not_gr0 rangeI)

    moreover have "x. x  range f; length x  Suc 0  x = []"
      using calculation(3) le_Suc_eq by auto

    moreover have "xa. f xa = []  False"
      unfolding f_def
      by simp

    ultimately have tt: "bij_betw f UNIV  ({l. length (tl l) = 0} - {[]})"
      unfolding bij_betw_def inj_def
      by auto presburger

    then have "infinite ({l :: 'a list. length (tl l) = 0} - {[]})"
      using bij_betw_finite infinite_UNIV by blast

    then have "infinite {l :: 'a list. length (tl l) = 0}"
      by simp

    with a show False
      by blast
  qed
next
  case (Suc y)


  have 1: "{l :: 'a list. length (tl l) = y} = 
    (if y = 0 then insert [] {l. length l = 1} else {l.  length l = Suc y})"
    by (auto simp: le_Suc_eq)

  have 2: "x. length x = Suc y  x  tl ` {l. length l - Suc 0 = Suc y}"
    by (metis (mono_tags, lifting) One_nat_def imageI length_tl list.sel(3) mem_Collect_eq)

  show ?case 
  proof
    assume "finite {l  :: 'a list. length (tl l) = Suc y}"

    then have "finite (tl ` {l :: 'a list. length (tl l) = Suc y})"
      by blast

    moreover have "tl ` {l :: 'a list. length (tl l) = Suc y} = {l :: 'a list. length l  = Suc y}"
      using 2
      by auto

    ultimately show False
      using Suc 1  
      by (smt (verit, ccfv_SIG) Collect_cong One_nat_def finite_insert)
  qed
qed

lemma infinite_prods': "{p :: 'a × 'a . fst p = y} = {y} × UNIV"
  by auto
 

lemma infinite_prods: "infinite {p :: (('a :: infinite) × 'a). fst p = y}"
  unfolding infinite_prods'
  using finite_cartesian_productD2 infinite_UNIV by blast

lemma nat_version': "f :: nat  nat. y :: nat. infinite {x. f x = y}"
proof-
  obtain g :: "nat  nat × nat" where bij_g: "bij g"
    using bij_prod_decode by blast

  define f :: "nat  nat" where 
    "x. f x  fst (g x)"

  have "y. infinite {x. f x = y}"
  proof-
    fix y
    have x: "{x. fst (g x) = y} =  inv g ` {p. fst p = y}"
      by (smt (verit, ccfv_SIG) Collect_cong bij_g bij_image_Collect_eq bij_imp_bij_inv inv_inv_eq)

    show "infinite {x. f x = y}"
      unfolding f_def x
      using infinite_prods
      by (metis bij_betw_def bij_g finite_imageI image_f_inv_f)
  qed

  then show ?thesis
    by blast    
qed

lemma not_nat_version': "f :: ('a :: infinite)  'a. y. infinite {x. f x = y}"
proof-
  obtain g :: "'a  'a × 'a" where bij_g: "bij g"
    using Times_same_infinite_bij_betw_types bij_betw_inv infinite_UNIV by blast

  define f :: "'a  'a" where 
    "x. f x  fst (g x)"

  have "y. infinite {x. f x = y}"
  proof-
    fix y
    have x: "{x. fst (g x) = y} =  inv g ` {p. fst p = y}"
      by (smt (verit, ccfv_SIG) Collect_cong bij_g bij_image_Collect_eq bij_imp_bij_inv inv_inv_eq)

    show "infinite {x. f x = y}"
      unfolding f_def x
      using infinite_prods 
      by (metis bij_g bij_is_surj finite_imageI image_f_inv_f)
  qed

  then show ?thesis
    by blast    
qed

lemma not_nat_version'': 
  assumes "|UNIV :: 'b set| ≤o |UNIV :: ('a :: infinite) set|"
  shows "f :: 'a  'b. y. infinite {x. f x = y}"
proof-
  obtain g :: "'a  'a × 'a" where bij_g: "bij g"
    using Times_same_infinite_bij_betw_types bij_betw_inv infinite_UNIV by blast

  define f :: "'a  'a" where 
    "x. f x  fst (g x)"

  have inf: "y. infinite {x. f x = y}"
  proof-
    fix y
    have x: "{x. fst (g x) = y} =  inv g ` {p. fst p = y}"
      by (smt (verit, ccfv_SIG) Collect_cong bij_g bij_image_Collect_eq bij_imp_bij_inv inv_inv_eq)

    show "infinite {x. f x = y}"
      unfolding f_def x
      using infinite_prods
      by (metis bij_g bij_is_surj finite_imageI image_f_inv_f)
  qed

  obtain f' ::  "'a  'b" where "surj f'"
    using assms
    by (metis card_of_ordLeq2 empty_not_UNIV)

  then have "y. infinite {x. f' (f x) = y}"
    using inf
    by (smt (verit, ccfv_SIG) Collect_mono finite_subset surjD)

  then show ?thesis
    by meson
qed



lemma nat_version: "f :: nat  nat. y :: nat. infinite {x. f x = y}"
proof-
  obtain g :: "nat  nat list" where bij_g: "bij g"
    using bij_list_decode by blast

  define f :: "nat  nat" where 
    "x. f x  length (tl (g x))"

  have "y. infinite {x. f x = y}"
  proof-
    fix y
    have "{x. length (tl (g x)) = y} = inv g ` {l. length (tl l) = y}"
      by (smt (verit, ccfv_SIG) Collect_cong bij_betw_def bij_g bij_image_Collect_eq image_inv_f_f 
          inv_inv_eq surj_imp_inj_inv)

    then show "infinite {x. f x = y}"
      unfolding f_def
      using infinite_lists_per_length
      by (metis bij_g bij_is_surj finite_imageI image_f_inv_f)
  qed

  then show ?thesis
    by blast    
qed

definition all_types where 
  "all_types 𝒱  ty. infinite {x. 𝒱 x = ty}"


lemma all_types_nat: "𝒱 :: nat  nat. all_types 𝒱"
  unfolding all_types_def
  using nat_version
  by blast

lemma all_types: "𝒱 :: ('v :: {infinite, countable}  'ty :: countable). all_types 𝒱"
proof-
  obtain 𝒱_nat :: "nat  nat" where 𝒱_nat: "all_types 𝒱_nat"
    using all_types_nat
    by blast

  obtain v_to_nat :: "'v  nat" where v_to_nat: "bij v_to_nat"
    using countableI_type infinite_UNIV to_nat_on_infinite by blast

  obtain nat_to_ty :: "nat  'ty" and N where nat_to_ty: "bij_betw nat_to_ty N UNIV"
    using countableE_bij
    by (metis countableI_type)

  define 𝒱 where "x. 𝒱 x  nat_to_ty (𝒱_nat (v_to_nat x))"

  have 1: "ty. {x. 𝒱_nat (v_to_nat x) = ty} = inv v_to_nat ` {x. 𝒱_nat x = ty}"
    by (smt (verit, best) Collect_cong bij_image_Collect_eq bij_imp_bij_inv inv_inv_eq v_to_nat)

  have 2: "ty. infinite {x. 𝒱_nat (v_to_nat x) = ty}"
    unfolding 1
    using 𝒱_nat 
    unfolding all_types_def
    by (metis bij_betw_def finite_imageI image_f_inv_f v_to_nat)


  have "ty. infinite {x. 𝒱 x = ty}"
    using 𝒱_nat
    unfolding 𝒱_def all_types_def
    by (smt (verit) "2" Collect_mono UNIV_I bij_betw_iff_bijections finite_subset nat_to_ty)
  
  then show "𝒱 :: 'v :: {infinite, countable}  'ty :: countable. all_types 𝒱"
    unfolding all_types_def
    by fast
qed

lemma all_types': 
  assumes "|UNIV :: 'ty set| ≤o |UNIV :: ('v :: infinite) set|"
  shows "𝒱 :: ('v :: infinite  'ty). all_types 𝒱"
  using not_nat_version''[OF assms]
  unfolding all_types_def
  by argo

definition clause_groundings :: "('f, 'ty) fun_types  ('f, 'v, 'ty) typed_clause  'f ground_atom clause set"  where
  "clause_groundings  clause = { clause.to_ground (fst clause  γ) | γ. 
    term_subst.is_ground_subst γ  
    welltypedc  (snd clause) (fst clause)  
    welltypedσ_on (clause.vars (fst clause))   (snd clause) γ  
    all_types (snd clause)
  }"

abbreviation select_subst_stability_on where
  "select selectG. select_subst_stability_on  select selectG premises 
    premiseG   (clause_groundings  ` premises). (premise, 𝒱)  premises. γ. 
      premise  γ = clause.from_ground premiseG  
      selectG (clause.to_ground (premise  γ)) = clause.to_ground ((select premise)  γ) 
      welltypedc  𝒱 premise  welltypedσ_on (clause.vars premise)  𝒱 γ  
      term_subst.is_ground_subst γ   
      all_types 𝒱"

lemma obtain_subst_stable_on_select_grounding:
  fixes select :: "('f, 'v) select"
  obtains selectG where 
    "select_subst_stability_on  select selectG premises"
    "is_select_grounding select selectG"
proof-
  let ?premise_groundings = "(clause_groundings  ` premises)"

  have selectG_exists_for_premises: 
    "premiseG  ?premise_groundings. selectG γ. (premise, 𝒱)  premises.
          premise  γ = clause.from_ground premiseG 
         selectG premiseG = clause.to_ground ((select premise)  γ) 
         welltypedc  𝒱 premise  welltypedσ_on (clause.vars premise)  𝒱 γ
         term_subst.is_ground_subst γ  all_types 𝒱"
    unfolding clause_groundings_def
    using clause.is_ground_subst_is_ground
    by fastforce

  obtain selectG_on_premise_groundings where 
    selectG_on_premise_groundings: "premiseG ?premise_groundings. (premise, 𝒱)  premises. γ.
        premise  γ = clause.from_ground premiseG 
       selectG_on_premise_groundings (clause.to_ground (premise  γ)) = 
          clause.to_ground ((select premise)  γ) 
       welltypedc  𝒱 premise  welltypedσ_on (clause.vars premise)  𝒱 γ 
       term_subst.is_ground_subst γ  all_types 𝒱"
    using Ball_Ex_comm(1)[OF selectG_exists_for_premises] 
      prod.case_eq_if clause.from_ground_inverse
    by fastforce

  define selectG where
    "clauseG. selectG clauseG = (
        if clauseG   ?premise_groundings 
        then selectG_on_premise_groundings clauseG 
        else clause.to_ground (select (clause.from_ground clauseG))
    )"

  have grounding: "is_select_grounding select selectG"
  proof-
    have "clauseG a b.
       ypremises.
           premiseGclause_groundings  y.
              xpremises.
                 case x of
                 (premise, 𝒱) 
                   γ. premise  γ = clause.from_ground premiseG 
                       selectG_on_premise_groundings (clause.to_ground (premise  γ)) =
                       clause.to_ground (select premise  γ) 
                       welltypedc  𝒱 premise 
                       welltypedσ_on (clause.vars premise)  𝒱 γ 
                       term_subst.is_ground_subst γ  all_types 𝒱;
        (a, b)  premises; clauseG  clause_groundings  (a, b)
        clause γ.
              clause.vars (clause  γ) = {} 
              clauseG = clause.to_ground (clause  γ) 
              selectG_on_premise_groundings clauseG = clause.to_ground (select clause  γ)"
      by force

    moreover have " clauseG.
       ypremises.
           premiseGclause_groundings  y.
              xpremises.
                 case x of
                 (premise, 𝒱) 
                   γ. premise  γ = clause.from_ground premiseG 
                       selectG_on_premise_groundings (clause.to_ground (premise  γ)) =
                       clause.to_ground (select premise  γ) 
                       welltypedc  𝒱 premise 
                       welltypedσ_on (clause.vars premise)  𝒱 γ 
                       term_subst.is_ground_subst γ  all_types 𝒱;
        xpremises. clauseG  clause_groundings  x
        clause γ.
              clause.vars (clause  γ) = {} 
              clauseG = clause.to_ground (clause  γ) 
              clause.to_ground (select (clause.from_ground clauseG)) =
              clause.to_ground (select clause  γ)"
      by (metis (no_types, opaque_lifting) clause.comp_subst.left.action_neutral 
            clause.ground_is_ground clause.from_ground_inverse)

    ultimately show ?thesis
      unfolding is_select_grounding_def selectG_def
      using selectG_on_premise_groundings
      by auto
  qed
   
  show ?thesis
    using that[OF _ grounding] selectG_on_premise_groundings
    unfolding selectG_def 
    by fastforce
qed

locale first_order_select = select select
  for select :: "('f, 'v) atom clause  ('f, 'v) atom clause"
begin

abbreviation is_grounding :: "'f ground_select  bool" where
  "is_grounding selectG  is_select_grounding select selectG"

definition selectGs where
  "selectGs = { ground_select. is_grounding ground_select }"

definition selectG_simple where
  "selectG_simple clause = clause.to_ground (select (clause.from_ground clause))"

lemma selectG_simple: "is_grounding selectG_simple"
  unfolding is_select_grounding_def selectG_simple_def
  by (metis clause.from_ground_inverse clause.ground_is_ground clause.subst_id_subst)

lemma select_from_ground_clause1: 
  assumes "clause.is_ground clause" 
  shows "clause.is_ground (select clause)"
  using select_subset sub_ground_clause assms
  by metis

lemma select_from_ground_clause2: 
  assumes "literal ∈# select (clause.from_ground clause)"  
  shows "literal.is_ground literal"
  using assms clause.sub_in_ground_is_ground select_subset
  by blast

lemma select_from_ground_clause3: 
  assumes "clause.is_ground clause" "literalG ∈# clause.to_ground clause"
  shows "literal.from_ground literalG ∈# clause"
  using assms 
  by (metis clause.to_ground_inverse clause.ground_sub_in_ground)

lemmas select_from_ground_clause =
  select_from_ground_clause1
  select_from_ground_clause2
  select_from_ground_clause3

lemma select_subst1:
  assumes "clause.is_ground (clause  γ)"  
  shows "clause.is_ground (select clause  γ)" 
  using assms
  by (metis image_mset_subseteq_mono select_subset sub_ground_clause clause.subst_def)

lemma select_subst2: 
  assumes "literal ∈# select clause  γ"  
  shows "is_neg literal"
  using assms subst_neg_stable select_negative_lits
  unfolding clause.subst_def
  by auto

lemmas select_subst = select_subst1 select_subst2

end

locale grounded_first_order_select = 
  first_order_select select for select +
fixes selectG 
assumes selectG: "is_select_grounding select selectG"
begin

abbreviation subst_stability_on where
  "subst_stability_on  premises  select_subst_stability_on  select selectG premises"

lemma selectG_subset: "selectG clause ⊆# clause"
  using selectG 
  unfolding is_select_grounding_def
  by (metis select_subset clause.to_ground_def image_mset_subseteq_mono clause.subst_def)

lemma selectG_negative:
  assumes "literalG ∈# selectG clauseG"
  shows "is_neg literalG"
proof -
  obtain clause γ where 
    is_ground: "clause.is_ground (clause  γ)" and
    selectG: "selectG clauseG = clause.to_ground (select clause  γ)"
    using selectG
    unfolding is_select_grounding_def
    by blast

  show ?thesis
    using 
      select_from_ground_clause(3)[
        OF select_subst(1)[OF is_ground] assms[unfolded selectG], 
        THEN select_subst(2)
        ]
    unfolding literal.from_ground_def
    by simp
qed

sublocale ground: select selectG
  by unfold_locales (simp_all add: selectG_subset selectG_negative)

end

end