Theory Schreier_Refinement

section ‹The Schreier refinement theorem›

theory Schreier_Refinement
  imports Normal_Chain Zassenhaus_Lemma
begin

subsection ‹Refining pairs of finite normal series›

text ‹
  The Zassenhaus lemma compares one refinement cell determined by a step from
  each of two normal series.  We name the two families of subgroup products
  independently of a particular cell.  Fixing one index produces a row of
  intermediate subgroups refining the corresponding step of the other series.
›

locale normal_series_pair =
  A: normal_series G "(⋅)" 𝟭 A m +
  B: normal_series G "(⋅)" 𝟭 B n
  for G and composition (infixl ‹⋅› 70) and unit (‹𝟭›)
    and A :: "nat ⇒ 'a set" and m
    and B :: "nat ⇒ 'a set" and n
begin

definition left_refinement_term :: "nat ⇒ nat ⇒ 'a set"
  where "left_refinement_term i j ≡ (case_prod (⋅)) ` ((A (Suc i) ∩ B j) × A i)"

definition right_refinement_term :: "nat ⇒ nat ⇒ 'a set"
  where "right_refinement_term j i ≡ (case_prod (⋅)) ` ((B (Suc j) ∩ A i) × B j)"

end

locale series_refinement_cell =
  normal_series_pair G "(⋅)" 𝟭 A m B n
  for G and composition (infixl ‹⋅› 70) and unit (‹𝟭›)
    and A :: "nat ⇒ 'a set" and m
    and B :: "nat ⇒ 'a set" and n
    and i j +
  assumes i_lt: "i < m" and j_lt: "j < n"
begin

lemma ambient_group:
  "Group G (⋅) 𝟭"
  by (rule A.G.Group_axioms)

lemma A_step_top_subgroup:
  "subgroup_of_group (A (Suc i)) G (⋅) 𝟭"
  by (simp add: A.term_subgroup Suc_leI ambient_group i_lt subgroup_of_group_def)

lemma B_step_top_subgroup:
  "subgroup_of_group (B (Suc j)) G (⋅) 𝟭"
  by (simp add: B.term_subgroup Suc_leI ambient_group j_lt subgroup_of_group_def)

interpretation cell: zassenhaus
    G "A (Suc i)" "B (Suc j)" "A i" "B j" "(⋅)" 𝟭
  by (simp add: zassenhaus.intro A.normal_step A_step_top_subgroup B.normal_step B_step_top_subgroup ambient_group i_lt
      j_lt)

lemma left_refinement_bottom_eq:
  "left_refinement_term i j = cell.left_bottom"
  using cell.left_bottom_def left_refinement_term_def by auto

lemma left_refinement_top_eq:
  "left_refinement_term i (Suc j) = cell.left_top"
  using cell.left_top_def left_refinement_term_def by auto

lemma right_refinement_bottom_eq:
  "right_refinement_term j i = cell.right_bottom"
  using cell.right_bottom_def right_refinement_term_def by auto

lemma right_refinement_top_eq:
  "right_refinement_term j (Suc i) = cell.right_top"
  using cell.right_top_def right_refinement_term_def by auto

lemma left_refinement_normal:
  "normal_subgroup (left_refinement_term i j) (left_refinement_term i (Suc j)) (⋅) 𝟭"
  using cell.left_bottom_normal left_refinement_bottom_eq left_refinement_top_eq by argo

lemma right_refinement_normal:
  "normal_subgroup (right_refinement_term j i) (right_refinement_term j (Suc i)) (⋅) 𝟭"
  using cell.right_bottom_normal right_refinement_bottom_eq right_refinement_top_eq by argo

text ‹
  Opposite sides of every refinement cell have the same native quotient-group
  isomorphism class.  This is the factor-level form of Zassenhaus needed by a
  later Schreier refinement theorem; the statement contains no HOL-Algebra
  records and no representatives of quotient isomorphisms.
›
theorem refinement_cell_factor_class_eq:
  "normal_factor_class (left_refinement_term i j) (left_refinement_term i (Suc j)) (⋅) 𝟭 =
   normal_factor_class (right_refinement_term j i) (right_refinement_term j (Suc i)) (⋅) 𝟭"
proof -
  have "normal_factor (left_refinement_term i j) (left_refinement_term i (Suc j)) (⋅) 𝟭 ≅G
        normal_factor (right_refinement_term j i) (right_refinement_term j (Suc i)) (⋅) 𝟭"
    unfolding normal_factor_def left_refinement_bottom_eq left_refinement_top_eq
      right_refinement_bottom_eq right_refinement_top_eq
    by (rule cell.butterfly_lemma)
  then show ?thesis
    by (simp add: left_refinement_normal normal_factor_class_eq_iff right_refinement_normal)
qed

end

text ‹
  The factors of a refinement are read row by row.  The following elementary
  lemmas turn a rectangular list of entries into finite sums of singleton
  multisets; this makes transposition of the refinement matrix explicit in the
  final proof.
›

lemma mset_map_upt:
  "mset (List.map f [0..<n]) = (∑i<n. {#f i#})"
  by (induction n) (simp_all add: sum.lessThan_Suc)

lemma mset_concat_map_upt:
  "mset (concat (List.map (λi. List.map (f i) [0..<n]) [0..<m])) = (∑i<m. ∑j<n. {#f i j#})"
proof (induction m)
  case 0
  show ?case by simp
next
  case (Suc m)
  have row: "mset (List.map (f m) [0..<n]) = (∑j<n. {#f m j#})"
    by (rule mset_map_upt)
  have "mset (concat (List.map (λi. List.map (f i) [0..<n]) [0..<Suc m])) =
      mset (concat (List.map (λi. List.map (f i) [0..<n]) [0..<m])) + mset (List.map (f m) [0..<n])"
    by simp
  also have "... = (∑i<Suc m. ∑j<n. {#f i j#})"
    using Suc row by auto
  finally show ?case .
qed

lemma length_concat_map_upt:
  "length (concat (List.map (λi. List.map (f i) [0..<n]) [0..<m])) = m * n"
  by (induction m) (simp_all add: algebra_simps)

text ‹
  At the endpoints of a refinement row, one factor in a subgroup product is
  either trivial or the whole carrier.  For intermediate terms, intersecting
  the carrier with another ambient subgroup and multiplying by a normal
  subgroup again gives an ambient subgroup.  These generic facts keep the row
  proofs below independent of quotient representations.
›

lemma subgroup_product_with_trivial_left:
  assumes K: "Subgroup K G composition unit"
  shows "(case_prod composition) ` ({unit} × K) = K"
proof -
  interpret K: Subgroup K G composition unit by fact
  show ?thesis
    by force
qed

lemma subgroup_product_with_carrier_left:
  assumes K: "Subgroup K H composition unit"
  shows "(case_prod composition) ` (H × K) = H"
proof -
  obtain K_submonoid: "Submonoid K H composition unit" and H_monoid: "Monoid H composition unit"
    using assms by (meson Subgroup_def Submonoid.axioms(1))
  have unit_K: "unit ∈ K"
    by (rule Submonoid.sub_unit_closed[OF K_submonoid])
  show ?thesis
  proof (intro equalityI subsetI)
    show "⋀x. x ∈ (λ(x, y). composition x y) ` (H × K) ⟹ x ∈ H"
      using H_monoid K_submonoid Monoid.composition_closed Submonoid.sub by fastforce
    show "⋀x. x ∈ H ⟹ x ∈ (λ(x, y). composition x y) ` (H × K)"
      using H_monoid unit_K by (metis Monoid.right_unit SigmaI pair_imageI)
  qed
qed

lemma intersection_normal_product_subgroup:
  assumes G: "Group G composition unit"
    and N: "normal_subgroup K H composition unit"
    and H: "Subgroup H G composition unit"
    and L: "Subgroup L G composition unit"
  shows "Subgroup ((case_prod composition) ` ((H ∩ L) × K))
    G composition unit"
proof -
  interpret N: normal_subgroup K H composition unit by fact
  have intersection_group:
      "subgroup_of_group (H ∩ L) H composition unit"
    using G H L N.Group_axioms
    by (meson Group.subgroup_intersection inf_le1 subgroup_of_group_def subgroup_restrict)
  show ?thesis
    using H N intersection_group
    by (metis normal_subgroup_product.HK_def normal_subgroup_product.HK_subgroup
        normal_subgroup_product.intro subgroup_transitive)
qed

context normal_series_pair
begin

text ‹
  For every pair of valid step indices, the assumptions of
  @{locale series_refinement_cell} follow directly from the two series.  We
  construct the locale predicate without interpreting it, so clients can
  reuse the proved cell API without re-elaborating the Zassenhaus locale.
›
lemma series_refinement_cellI:
  assumes i: "i < m" and j: "j < n"
  shows "series_refinement_cell G (⋅) 𝟭 A m B n i j"
  by (simp add: i j normal_series_pair_axioms series_refinement_cell_axioms.intro
      series_refinement_cell_def)

subsection ‹Refinement rows›

text ‹
  The left row at index @{term i} runs from @{term "A i"} to
  @{term "A (Suc i)"}; the right row at index @{term j} runs from
  @{term "B j"} to @{term "B (Suc j)"}.  Thus concatenating rows in increasing
  outer index refines the original series.  The normal-chain theorems verify
  every intermediate subgroup and adjacent normality condition.
›

lemma left_refinement_start:
  assumes i: "i < m"
  shows "left_refinement_term i 0 = A i"
proof -
  interpret ASi: Subgroup "A (Suc i)" G "(⋅)" 𝟭
    by (rule A.term_subgroup) (use i in simp)
  have intersection: "A (Suc i) ∩ B 0 = {𝟭}"
    using B.bottom ASi.sub_unit_closed by auto
  show ?thesis
    unfolding left_refinement_term_def intersection
    using subgroup_product_with_trivial_left[OF A.term_subgroup] i by simp
qed

lemma left_refinement_end:
  assumes i: "i < m"
  shows "left_refinement_term i n = A (Suc i)"
proof -
  interpret ASi: Subgroup "A (Suc i)" G "(⋅)" 𝟭
    by (rule A.term_subgroup) (use i in simp)
  have intersection: "A (Suc i) ∩ B n = A (Suc i)"
    using B.top ASi.subset by auto
  show ?thesis
    unfolding left_refinement_term_def intersection
    by (rule subgroup_product_with_carrier_left[OF A.normal_step_subgroup[OF i]])
qed

lemma right_refinement_start:
  assumes j: "j < n"
  shows "right_refinement_term j 0 = B j"
proof -
  interpret BSj: Subgroup "B (Suc j)" G "(⋅)" 𝟭
    by (rule B.term_subgroup) (use j in simp)
  have intersection: "B (Suc j) ∩ A 0 = {𝟭}"
    using A.bottom BSj.sub_unit_closed by auto
  show ?thesis
    unfolding right_refinement_term_def intersection
    using subgroup_product_with_trivial_left[OF B.term_subgroup] j by simp
qed

lemma right_refinement_end:
  assumes j: "j < n"
  shows "right_refinement_term j m = B (Suc j)"
proof -
  interpret BSj: Subgroup "B (Suc j)" G "(⋅)" 𝟭
    by (rule B.term_subgroup) (use j in simp)
  have intersection: "B (Suc j) ∩ A m = B (Suc j)"
    using A.top BSj.subset by auto
  show ?thesis
    unfolding right_refinement_term_def intersection
    by (rule subgroup_product_with_carrier_left[OF B.normal_step_subgroup[OF j]])
qed

lemma left_refinement_step:
  assumes i: "i < m" and j: "j < n"
  shows "normal_subgroup (left_refinement_term i j) (left_refinement_term i (Suc j)) (⋅) 𝟭"
  using i j series_refinement_cellI by (meson series_refinement_cell.left_refinement_normal)

lemma left_refinement_term_subgroup:
  assumes i: "i < m" and j: "j ≤ n"
  shows "Subgroup (left_refinement_term i j) G (⋅) 𝟭"
  unfolding left_refinement_term_def
  by (simp add: A.G.Group_axioms A.normal_step A.term_subgroup B.term_subgroup Suc_leI i
      intersection_normal_product_subgroup j)

lemma right_refinement_step:
  assumes i: "i < m" and j: "j < n"
  shows "normal_subgroup (right_refinement_term j i) (right_refinement_term j (Suc i)) (⋅) 𝟭"
  using i j series_refinement_cellI by (meson series_refinement_cell.right_refinement_normal)

lemma right_refinement_term_subgroup:
  assumes i: "i ≤ m" and j: "j < n"
  shows "Subgroup (right_refinement_term j i) G (⋅) 𝟭"
  unfolding right_refinement_term_def
  by (simp add: A.G.Group_axioms A.term_subgroup B.normal_step B.term_subgroup Suc_leI i
      intersection_normal_product_subgroup j)

lemma left_refinement_row:
  assumes i: "i < m"
  shows "normal_chain G (⋅) 𝟭 (left_refinement_term i) n"
proof (intro normal_chain.intro A.G.Group_axioms)
  show "normal_chain_axioms G (⋅) 𝟭 (left_refinement_term i) n"
    by (simp add: i left_refinement_step left_refinement_term_subgroup normal_chain_axioms_def)
qed

lemma right_refinement_row:
  assumes j: "j < n"
  shows "normal_chain G (⋅) 𝟭 (right_refinement_term j) m"
proof (intro normal_chain.intro A.G.Group_axioms)
  show "normal_chain_axioms G (⋅) 𝟭 (right_refinement_term j) m"
    by (simp add: j normal_chain_axioms_def right_refinement_step right_refinement_term_subgroup)
qed

subsection ‹Factor-class refinements›

text ‹
  The two lists below flatten the left and right refinement rows in row-major
  order.  Each entry is the native isomorphism class of the corresponding
  normal-chain factor; repeated or trivial factors are retained.
›

definition left_refinement_factor_class :: "nat ⇒ nat ⇒ 'a set monoid_iso_class"
  where
    "left_refinement_factor_class i j ≡
      normal_factor_class (left_refinement_term i j) (left_refinement_term i (Suc j)) (⋅) 𝟭"

definition right_refinement_factor_class :: "nat ⇒ nat ⇒ 'a set monoid_iso_class"
  where
    "right_refinement_factor_class j i ≡
      normal_factor_class (right_refinement_term j i) (right_refinement_term j (Suc i)) (⋅) 𝟭"

definition left_refinement_factor_classes :: "'a set monoid_iso_class list"
  where
    "left_refinement_factor_classes ≡
      concat (List.map (λi. List.map (left_refinement_factor_class i) [0..<n]) [0..<m])"

definition right_refinement_factor_classes :: "'a set monoid_iso_class list"
  where
    "right_refinement_factor_classes ≡
      concat (List.map
        (λj. List.map (right_refinement_factor_class j) [0..<m]) [0..<n])"

lemma length_left_refinement_factor_classes:
  "length left_refinement_factor_classes = m * n"
  unfolding left_refinement_factor_classes_def by (rule length_concat_map_upt)

lemma length_right_refinement_factor_classes:
  "length right_refinement_factor_classes = n * m"
  unfolding right_refinement_factor_classes_def by (rule length_concat_map_upt)

lemma refinement_factor_class_eq:
  assumes i: "i < m" and j: "j < n"
  shows "left_refinement_factor_class i j = right_refinement_factor_class j i"
proof -
  have C: "series_refinement_cell G (⋅) 𝟭 A m B n i j"
    by (rule series_refinement_cellI[OF i j])
  show ?thesis
    unfolding left_refinement_factor_class_def right_refinement_factor_class_def
    by (rule series_refinement_cell.refinement_cell_factor_class_eq[OF C])
qed

text ‹
  The two row-major factor lists need not have the same order.  Their
  multisets agree: transposing the refinement matrix sends the factor at
  position @{term "(i, j)"} to the isomorphic factor at @{term "(j, i)"}.
  This is the native, factor-class form of the Schreier refinement theorem.
›
theorem schreier_refinement:
  "mset left_refinement_factor_classes = mset right_refinement_factor_classes"
proof -
  have transpose:
      "(∑i<m. ∑j<n. {#left_refinement_factor_class i j#}) =
       (∑j<n. ∑i<m. {#right_refinement_factor_class j i#})"
    using refinement_factor_class_eq sum.swap by auto
  show ?thesis
    unfolding left_refinement_factor_classes_def right_refinement_factor_classes_def mset_concat_map_upt
    using transpose by blast
qed

end

subsection ‹Reduced Schreier refinements›

text ‹
  A finite normal chain may repeat a term.  Its corresponding quotient is the
  one-element group and contributes no genuine factor.  The operation below
  removes precisely the canonical trivial isomorphism class while retaining
  multiplicity and forgetting order.
›
definition nontrivial_factor_multiset ::
    "'a monoid_iso_class list ⇒ 'a monoid_iso_class multiset"
  where
    "nontrivial_factor_multiset factors ≡ filter_mset (λC. C ≠ trivial_monoid_iso_class) (mset factors)"

lemma nontrivial_factor_multiset_cong:
  assumes "mset factors = mset factors'"
  shows "nontrivial_factor_multiset factors = nontrivial_factor_multiset factors'"
  using assms unfolding nontrivial_factor_multiset_def by simp

lemma nontrivial_factor_multiset_append [simp]:
  "nontrivial_factor_multiset (factors @ factors') =
    nontrivial_factor_multiset factors + nontrivial_factor_multiset factors'"
  unfolding nontrivial_factor_multiset_def by simp

lemma nontrivial_factor_multiset_Nil [simp]:
  "nontrivial_factor_multiset [] = {#}"
  unfolding nontrivial_factor_multiset_def by simp

lemma nontrivial_factor_multiset_singleton [simp]:
  "nontrivial_factor_multiset [C] =
    (if C = trivial_monoid_iso_class then {#} else {#C#})"
  unfolding nontrivial_factor_multiset_def by simp

lemma nontrivial_factor_multiset_concat_map_upt:
  "nontrivial_factor_multiset (concat (List.map f [0..<m])) =
    (∑i<m. nontrivial_factor_multiset (f i))"
  by (induction m) (simp_all add: sum.lessThan_Suc)

context normal_series_pair
begin

text ‹
  The trivial-class test has a concrete chain interpretation: it detects
  exactly an adjacent repetition.  These equivalences are the bridge used by
  the later Jordan--Hölder argument when a refinement row lies inside a simple
  factor.
›
lemma left_refinement_factor_class_eq_trivial_iff:
  assumes i: "i < m" and j: "j < n"
  shows "left_refinement_factor_class i j = trivial_monoid_iso_class
    ⟷ left_refinement_term i j = left_refinement_term i (Suc j)"
  by (simp add: i j left_refinement_factor_class_def left_refinement_step normal_factor_class_eq_trivial_iff)

lemma right_refinement_factor_class_eq_trivial_iff:
  assumes i: "i < m" and j: "j < n"
  shows "right_refinement_factor_class j i = trivial_monoid_iso_class
    ⟷ right_refinement_term j i = right_refinement_term j (Suc i)"
  by (simp add: i j normal_factor_class_eq_trivial_iff right_refinement_factor_class_def right_refinement_step)

definition left_reduced_refinement_factor_multiset :: "'a set monoid_iso_class multiset"
  where "left_reduced_refinement_factor_multiset ≡ nontrivial_factor_multiset left_refinement_factor_classes"

definition right_reduced_refinement_factor_multiset :: "'a set monoid_iso_class multiset"
  where "right_reduced_refinement_factor_multiset ≡ nontrivial_factor_multiset right_refinement_factor_classes"

text ‹
  Removing repetition factors from equivalent Schreier refinements preserves
  equality.  Unlike @{thm schreier_refinement}, this form is ready to compare
  directly with the factor multiset of a composition series.
›
theorem reduced_schreier_refinement:
  "left_reduced_refinement_factor_multiset = right_reduced_refinement_factor_multiset"
  by (simp add: left_reduced_refinement_factor_multiset_def nontrivial_factor_multiset_def
      right_reduced_refinement_factor_multiset_def schreier_refinement)

end

end