Theory Finitely_Generated_Abelian_Groups.Finite_And_Cyclic_Groups

(*
    File:     Finite_And_Cyclic_Groups.thy
    Author:   Joseph Thommes, TU München; Manuel Eberl, TU München
*)
section ‹Finite and cyclic groups›

theory Finite_And_Cyclic_Groups
  imports Group_Hom Generated_Groups_Extend General_Auxiliary
begin

subsection ‹Finite groups›

text ‹We define the notion of finite groups and prove some trivial facts about them.›

locale finite_group = group +
  assumes fin[simp]: "finite (carrier G)"

(* Manuel Eberl *)
lemma (in finite_group) ord_pos: 
  assumes "x  carrier G"
  shows   "ord x > 0"
  using ord_ge_1[of x] assms by auto

lemma (in finite_group) order_gt_0 [simp,intro]: "order G > 0"
  by (subst order_gt_0_iff_finite) auto

lemma (in finite_group) finite_ord_conv_Least:
  assumes "x  carrier G"
  shows "ord x = (LEAST n::nat. 0 < n  x [^] n = 𝟭)"
  using pow_order_eq_1 order_gt_0_iff_finite ord_conv_Least assms by auto

lemma (in finite_group) non_trivial_group_ord_gr_1:
  assumes "carrier G  {𝟭}"
  shows "e  carrier G. ord e > 1"
proof -
  from one_closed obtain e where e: "e  𝟭" "e  carrier G" using assms carrier_not_empty by blast
  thus ?thesis using ord_eq_1[of e] le_neq_implies_less ord_ge_1 by fastforce
qed

(* Manuel Eberl *)
lemma (in finite_group) max_order_elem:
  obtains a where "a  carrier G" "x  carrier G. ord x  ord a"
proof -
  have "x. x  carrier G  (y. y  carrier G  ord y  ord x)"
  proof (rule ex_has_greatest_nat[of _ 𝟭 _ "order G + 1"], safe)
    show "𝟭  carrier G"
      by auto
  next
    fix x assume "x  carrier G"
    hence "ord x  order G"
      by (intro ord_le_group_order fin)
    also have " < order G + 1"
      by simp
    finally show "ord x < order G + 1" .
  qed
  thus ?thesis using that by blast
qed

lemma (in finite_group) iso_imp_finite:
  assumes "G  H" "group H"
  shows "finite_group H"
proof -
  interpret H: group H by fact
  show ?thesis
  proof(unfold_locales)
    show "finite (carrier H)" using iso_same_card[OF assms(1)]
      by (metis card_gt_0_iff order_def order_gt_0)
  qed
qed

lemma (in finite_group) finite_FactGroup:
  assumes "H  G"
  shows "finite_group (G Mod H)"
proof -
  interpret H: normal H G by fact
  interpret Mod: group "G Mod H" using H.factorgroup_is_group .
  show ?thesis
    by (unfold_locales, unfold FactGroup_def RCOSETS_def, simp)
qed

lemma (in finite_group) bigger_subgroup_is_group:
  assumes "subgroup H G" "card H  order G"
  shows "H = carrier G"
  using subgroup.subset fin assms by (metis card_seteq order_def)

text ‹All generated subgroups of a finite group are obviously also finite.›

lemma (in finite_group) finite_generate:
  assumes "A  carrier G"
  shows "finite (generate G A)"
  using generate_incl[of A] rev_finite_subset[of "carrier G" "generate G A"] assms by simp

text ‹We also provide an induction rule for finite groups inspired by Manuel Eberl's AFP entry
"Dirichlet L-Functions and Dirichlet's Theorem" and the contained theory "Group\_Adjoin". A property
that is true for a subgroup generated by some set and stays true when adjoining an element, is also
true for the whole group.›

lemma (in finite_group) generate_induct[consumes 1, case_names base adjoin]:
  assumes "A0  carrier G"
  assumes "A0  carrier G  P (Gcarrier := generate G A0)"
  assumes "a A. A  carrier G; a  carrier G - generate G A; A0  A;
           P (Gcarrier := generate G A)  P (Gcarrier := generate G (A  {a}))"
  shows "P G"
proof -
  define A where A: "A = carrier G"
  hence gA: "generate G A = carrier G"
    using generate_incl[of "carrier G"] generate_sincl[of "carrier G"] by simp
  hence "finite A" using fin A by argo
  moreover have "A0  A" using assms(1) A by argo
  moreover have "A  carrier G" using A by simp
  moreover have "generate G A0  generate G A" using gA generate_incl[OF assms(1)] by argo
  ultimately have "P (Gcarrier := generate G A)" using assms(2, 3)
  proof (induction "A" taking: card rule: measure_induct_rule)
    case (less A)
    then show ?case
    proof(cases "generate G A0 = generate G A")
      case True
      thus ?thesis using less by force
    next
      case gA0: False
      with less(3) have s: "A0  A" by blast
      then obtain a where a: "a  A - A0" by blast
      have P1: "P (Gcarrier := generate G (A - {a}))"
      proof(rule less(1))
        show "card (A - {a}) < card A" using a less(2) by (meson DiffD1 card_Diff1_less)
        show "A0  A - {a}" using a s by blast
        thus "generate G A0  generate G (A - {a})" using mono_generate by presburger
      qed (use less a s in auto)
      show ?thesis
      proof (cases "generate G A = generate G (A - {a})")
        case True
        then show ?thesis using P1 by simp
      next
        case False
        have "a  carrier G - generate G (A - {a})"
        proof -
          have "a  generate G (A - {a})"
          proof
            assume a2: "a  generate G (A - {a})"
            have "generate G (A - {a}) = generate G A"
            proof (rule equalityI)
              show "generate G (A - {a})  generate G A" using mono_generate by auto
              show "generate G A  generate G (A - {a})"
              proof(subst (2) generate_idem[symmetric])
                show "generate G A  generate G (generate G (A - {a}))"
                  by (intro mono_generate, use generate_sincl[of "A - {a}"] a2 in blast)
              qed (use less in auto)
            qed
            with False show False by argo
          qed
          with a less show ?thesis by fast
        qed
        from less(7)[OF _ this _ P1] less(4) s a have "P (Gcarrier := generate G (A - {a}  {a}))"
          by blast
        moreover have "A - {a}  {a} = A" using a by blast
        ultimately show ?thesis by auto
      qed
    qed
  qed
  with gA show ?thesis by simp
qed

subsection ‹Finite abelian groups›

text ‹Another trivial locale: the finite abelian group with some trivial facts.›

locale finite_comm_group = finite_group + comm_group

lemma (in finite_comm_group) iso_imp_finite_comm:
  assumes "G  H" "group H"
  shows "finite_comm_group H"
proof -
  interpret H: group H by fact
  interpret H: comm_group H by (intro iso_imp_comm_group[OF assms(1)], unfold_locales)
  interpret H: finite_group H by (intro iso_imp_finite[OF assms(1)], unfold_locales)
  show ?thesis by unfold_locales
qed

lemma (in finite_comm_group) finite_comm_FactGroup:
  assumes "subgroup H G"
  shows "finite_comm_group (G Mod H)"
  unfolding finite_comm_group_def
proof(safe)
  show "finite_group (G Mod H)" using finite_FactGroup[OF subgroup_imp_normal[OF assms]] .
  show "comm_group (G Mod H)" by (simp add: abelian_FactGroup assms)
qed

(* Manuel Eberl *)
lemma (in finite_comm_group) subgroup_imp_finite_comm_group:
  assumes "subgroup H G"
  shows   "finite_comm_group (Gcarrier := H)"
proof -
  interpret G': group "Gcarrier := H" by (intro subgroup_imp_group) fact+
  interpret H: subgroup H G by fact
  show ?thesis by standard (use finite_subset[OF H.subset] in auto simp: m_comm)
qed


subsection ‹Cyclic groups›

text ‹Now, the central notion of a cyclic group is introduced: a group generated
by a single element.›

locale cyclic_group = group +
  fixes gen :: "'a"
  assumes gen_closed[intro, simp]: "gen  carrier G"
  assumes generator: "carrier G = generate G {gen}"

lemma (in cyclic_group) elem_is_gen_pow:
  assumes "x  carrier G"
  shows "n :: int. x = gen [^] n"
proof -
  from generator have x_g:"x  generate G {gen}" using assms by fast
  with generate_pow[of gen] show ?thesis using gen_closed by blast
qed

text ‹Every cyclic group is commutative/abelian.›

sublocale cyclic_group  comm_group
proof(unfold_locales)
  fix x y
  assume "x  carrier G" "y  carrier G"
  then obtain a b where ab:"x = gen [^] (a::int)" "y = gen [^] (b::int)"
    using elem_is_gen_pow by presburger
  then have "x  y = gen [^] (a + b)" by (simp add: int_pow_mult)                 
  also have " = y  x" using ab int_pow_mult
    by (metis add.commute gen_closed)
  finally show "x  y = y  x" .
qed

text ‹Some trivial intro rules for showing that a group is cyclic.›

lemma (in group) cyclic_groupI0:
  assumes "a  carrier G" "carrier G = generate G {a}"
  shows "cyclic_group G a"
  using assms by (unfold_locales; auto) 

lemma (in group) cyclic_groupI1:
  assumes "a  carrier G" "carrier G  generate G {a}"
  shows "cyclic_group G a"
  using assms by (unfold_locales, use generate_incl[of "{a}"] in auto)

lemma (in group) cyclic_groupI2:
  assumes "a  carrier G"
  shows "cyclic_group (Gcarrier := generate G {a}) a"
proof (intro group.cyclic_groupI0)
  show "group (Gcarrier := generate G {a})"
    by (intro subgroup.subgroup_is_group group.generate_is_subgroup, use assms in simp_all)
  show "a  carrier (Gcarrier := generate G {a})" using generate.incl[of a "{a}"] by auto
  show "carrier (Gcarrier := generate G {a}) = generate (Gcarrier := generate G {a}) {a}"
    using assms
    by (simp add: generate_consistent generate.incl group.generate_is_subgroup)
qed

text ‹The order of the generating element is always the same as the group order.›

lemma (in cyclic_group) ord_gen_is_group_order:
  shows "ord gen = order G"
proof (cases "finite (carrier G)")
  case True
  with generator show "ord gen = order G"
    using generate_pow_card[of gen] order_def[of G] gen_closed by simp
next
  case False
  thus ?thesis
    using generate_pow_card generator order_def[of G] card_eq_0_iff[of "carrier G"] by force
qed

text ‹In the case of a finite group, it is sufficient to have one element of group order to know
that the group is cyclic.›

lemma (in finite_group) element_ord_generates_cyclic:
  assumes "a  carrier G" "ord a = order G"
  shows "cyclic_group G a"
proof (unfold_locales)
  show "a  carrier G" using assms(1) by simp
  show "carrier G = generate G {a}"
    using assms bigger_subgroup_is_group[OF generate_is_subgroup]
    by (metis empty_subsetI fin generate_pow_card insert_subset ord_le_group_order)
qed

text ‹Another useful fact is that a group of prime order is also cyclic.›

lemma (in group) prime_order_group_is_cyc:
  assumes "Factorial_Ring.prime (order G)"
  obtains g where "cyclic_group G g"
proof (unfold_locales)
  obtain p where order_p: "order G = p" and p_prime: "Factorial_Ring.prime p" using assms by blast
  then have "card (carrier G)  2" by (simp add: order_def prime_ge_2_nat)
  then obtain a where a_in: "a  carrier G" and a_not_one: "a  𝟭" using one_unique
    by (metis (no_types, lifting) card_2_iff' obtain_subset_with_card_n subset_iff)
  interpret fin: finite_group G
    using assms order_gt_0_iff_finite unfolding order_def by unfold_locales auto
  have "ord a dvd p" using a_in order_p ord_dvd_group_order by blast
  hence "ord a = p" using prime_nat_iff[of p] p_prime ord_eq_1 a_in a_not_one by blast
  then interpret cyclic_group G a
    using fin.element_ord_generates_cyclic order_p a_in by simp
  show ?thesis using that cyclic_group_axioms .
qed

text ‹What follows is an induction principle for cyclic groups: a predicate is true for all elements
of the group if it is true for all elements that can be formed by the generating element by just
multiplication and if it also holds under the forming of the inverse (as we by this cover
all elements of the group),›

(* Manuel Eberl *)
lemma (in cyclic_group) generator_induct [consumes 1, case_names generate inv]:
  assumes x: "x  carrier G"
  assumes IH1: "n::nat. P (gen [^] n)"
  assumes IH2: "x. x  carrier G  P x  P (inv x)"
  shows   "P x"
proof -
  from x obtain n :: int where n: "x = gen [^] n"
    using elem_is_gen_pow[of x] by auto
  show ?thesis
  proof (cases "n  0")
    case True
    have "P (gen [^] nat n)"
      by (rule IH1)
    with True n show ?thesis by simp
  next
    case False
    have "P (inv (gen [^] nat (-n)))"
      by (intro IH1 IH2) auto
    also have "gen [^] nat (-n) = gen [^] (-n)"
      using False by simp
    also have "inv  = x"
      using n by (simp add: int_pow_neg)
    finally show ?thesis .
  qed
qed


subsection ‹Finite cyclic groups›

text ‹Additionally, the notion of the finite cyclic group is introduced.›

locale finite_cyclic_group = finite_group + cyclic_group

sublocale finite_cyclic_group  finite_comm_group
  by unfold_locales

lemma (in finite_cyclic_group) ord_gen_gt_zero:
  "ord gen > 0"
  using ord_ge_1[OF fin gen_closed] by simp

text ‹In order to prove something about an element in a finite abelian group, it is possible to show
this property for the neutral element or the generating element and inductively for the elements
that are formed by multiplying with the generator.›

lemma (in finite_cyclic_group) generator_induct0 [consumes 1, case_names one step]:
  assumes x: "x  carrier G"
  assumes IH1: "P 𝟭"
  assumes IH2: "x. x  carrier G; P x  P (x  gen)"
  shows   "P x"
proof -
  from ord_gen_gt_zero generate_nat_pow[OF _ gen_closed] obtain n::nat where n: "x = gen [^] n"
    using generator x by blast
  thus ?thesis by (induction n arbitrary: x, use assms in auto)
qed

lemma (in finite_cyclic_group) generator_induct1 [consumes 1, case_names gen step]:
  assumes x: "x  carrier G"
  assumes IH1: "P gen"
  assumes IH2: "x. x  carrier G; P x  P (x  gen)"
  shows   "P x"
proof(rule generator_induct0[OF x])
  show "x. x  carrier G; P x  P (x  gen)" using IH2 by blast
  have "P x" if "n > 0" "x = gen [^] n" for n::nat and x using that
    by (induction n arbitrary: x; use assms in fastforce)
  from this[OF ord_pos[OF gen_closed] pow_ord_eq_1[OF gen_closed, symmetric]] show "P 𝟭" .
qed

subsection get_exp› - discrete logarithm›

text ‹What now follows is the discrete logarithm for groups. It is used at several times througout
this entry and is initially used to show that two cyclic groups of the same order are isomorphic.›

definition (in group) get_exp where
  "get_exp g = (λa. SOME k::int. a = g [^] k)"

text ‹For each element with itself as the basis the discrete logarithm indeed does what expected.
This is not the strongest possible statement, but sufficient for our needs.›

lemma (in group) get_exp_self_fulfills:
  assumes "a  carrier G"
  shows "a = a [^] get_exp a a"
proof -
  have "a = a [^] (1::int)" using assms by auto
  moreover have "a [^] (1::int) = a [^] (SOME x::int. a [^] (1::int) = a [^] x)"
    by (intro someI_ex[of "λx::int. a [^] (1::int) = a [^] x"]; blast)
  ultimately show ?thesis unfolding get_exp_def by simp
qed

lemma (in group) get_exp_self:
  assumes "a  carrier G"
  shows "get_exp a a mod ord a = (1::int) mod ord a"
  by (intro pow_eq_int_mod[OF assms], use get_exp_self_fulfills[OF assms] assms in auto)

text ‹For cyclic groups, the discrete logarithm "works" for every element.›

lemma (in cyclic_group) get_exp_fulfills:
  assumes "a  carrier G"
  shows "a = gen [^] get_exp gen a"
proof -
  from elem_is_gen_pow[OF assms] obtain k::int where k: "a = gen [^] k" by blast
  moreover have "gen [^] k = gen [^] (SOME x::int. gen [^] k = gen [^] x)"
    by(intro someI_ex[of "λx::int. gen [^] k = gen [^] x"]; blast)
  ultimately show ?thesis unfolding get_exp_def by blast
qed

lemma (in cyclic_group) get_exp_non_zero:
  assumes"b  carrier G" "b  𝟭"
  shows "get_exp gen b  0"
  using assms get_exp_fulfills[OF assms(1)] by auto 

text ‹One well-known logarithmic identity.›

lemma (in cyclic_group) get_exp_mult_mod:
  assumes "a  carrier G" "b  carrier G"
  shows "get_exp gen (a  b) mod (ord gen) = (get_exp gen a + get_exp gen b) mod (ord gen)"
proof (intro pow_eq_int_mod[OF gen_closed])
  from get_exp_fulfills[of "a  b"] have "gen [^] get_exp gen (a  b) = a  b" using assms by simp
  moreover have "gen [^] (get_exp gen a + get_exp gen b) = a  b"
  proof -
    have "gen [^] (get_exp gen a + get_exp gen b) = gen [^] (get_exp gen a)  gen [^] (get_exp gen b)"
      using int_pow_mult by blast
    with get_exp_fulfills assms show ?thesis by simp
  qed
  ultimately show "gen [^] get_exp gen (a  b) = gen [^] (get_exp gen a + get_exp gen b)" by simp
qed

text ‹We now show that all functions from a group generated by 'a' to a group generated by 'b'
that map elements from $a^k$ to $b^k$ in the other group are in fact isomorphisms between these two
groups.›

lemma (in group) iso_cyclic_groups_generate:
  assumes "a  carrier G" "b  carrier H" "group.ord G a = group.ord H b" "group H"
  shows "{f. k  (UNIV::int set). f (a [^] k) = b [^]Hk}
          iso (Gcarrier := generate G {a}) (Hcarrier := generate H {b})"
proof
  interpret H: group H by fact
  let ?A = "Gcarrier := generate G {a}"
  let ?B = "Hcarrier := generate H {b}"
  interpret A: cyclic_group ?A a by (intro group.cyclic_groupI2; use assms(1) in simp)
  interpret B: cyclic_group ?B b by (intro group.cyclic_groupI2; use assms(2) in simp)
  have sA: "subgroup (generate G {a}) G" by (intro generate_is_subgroup, use assms(1) in simp)
  have sB: "subgroup (generate H {b}) H" by (intro H.generate_is_subgroup, use assms(2) in simp)
  fix x
  assume x: "x  {f. k(UNIV::int set). f (a [^] k) = b [^]Hk}"
  have hom: "x  hom ?A ?B"
  proof (intro homI)
    fix c
    assume c: "c  carrier ?A"
    from A.elem_is_gen_pow[OF this] obtain k::int where k: "c = a [^] k"
      using int_pow_consistent[OF sA generate.incl[of a]] by auto
    with x have "x c = b [^]Hk" by blast
    thus "x c  carrier ?B"
      using B.int_pow_closed H.int_pow_consistent[OF sB] generate.incl[of b "{b}" H] by simp
    fix d
    assume d: "d  carrier ?A"
    from A.elem_is_gen_pow[OF this] obtain l::int where l: "d = a [^] l"
      using int_pow_consistent[OF sA generate.incl[of a]] by auto
    with k have "c  d = a [^] (k + l)" by (simp add: int_pow_mult assms(1))
    with x have "x (c ?Ad) = b [^]H(k + l)" by simp
    also have " = b [^]Hk Hb [^]Hl" by (simp add: H.int_pow_mult assms(2))
    finally show "x (c ?Ad) = x c ?Bx d" using x k l by simp
  qed
  then interpret xgh: group_hom ?A ?B x unfolding group_hom_def group_hom_axioms_def by blast
  have "kernel ?A ?B x = {𝟭}"
  proof(intro equalityI)
    show "{𝟭}  kernel ?A ?B x" using xgh.one_in_kernel by auto
    have "c = 𝟭" if "c  kernel ?A ?B x" for c
    proof -
      from that have c: "c  carrier ?A" unfolding kernel_def by blast
      from A.elem_is_gen_pow[OF this] obtain k::int where k: "c = a [^] k"
        using int_pow_consistent[OF sA generate.incl[of a]] by auto
      moreover have "x c = 𝟭H⇙" using that x unfolding kernel_def by auto
      ultimately have "𝟭H= b [^]Hk" using x by simp
      with assms(3) have "a [^] k = 𝟭"
        using int_pow_eq_id[OF assms(1), of k] H.int_pow_eq_id[OF assms(2), of k] by simp
      thus "c = 𝟭" using k by blast
    qed
    thus "kernel ?A ?B x  {𝟭}" by blast             
  qed
  moreover have "carrier ?B  x ` carrier ?A"
  proof
    fix c
    assume c: "c  carrier ?B"
    from B.elem_is_gen_pow[OF this] obtain k::int where k: "c = b [^]Hk"
      using H.int_pow_consistent[OF sB generate.incl[of b]] by auto
    then have "x (a [^] k) = c" using x by blast
    moreover have "a [^] k  carrier ?A"
      using int_pow_consistent[OF sA generate.incl[of a]] A.int_pow_closed generate.incl[of a]
      by fastforce
    ultimately show "c  x ` carrier ?A" by blast
  qed
  ultimately show "x  iso ?A ?B" using hom xgh.iso_iff unfolding kernel_def by auto
qed

text ‹This is then used to derive the isomorphism of two cyclic groups of the same order as a
direct consequence.›

lemma (in cyclic_group) iso_cyclic_groups_same_order:
  assumes "cyclic_group H h" "order G = order H"
  shows "G  H"
proof(intro is_isoI)
  interpret H: cyclic_group H h by fact
  define f where "f = (λa. h [^]Hget_exp gen a)"
  from assms(2) have o: "ord gen = H.ord h" using ord_gen_is_group_order H.ord_gen_is_group_order
    by simp
  have "k  (UNIV::int set). f (gen [^] k) = h [^]Hk"
  proof
    fix k
    assume k: "k  (UNIV::int set)"
    have "gen [^] k = gen [^] (SOME x::int. gen [^] k = gen [^] x)"
      by(intro someI_ex[of "λx::int. gen [^] k = gen [^] x"]; blast)
    moreover have "(SOME x::int. gen [^] k = gen [^] x) = (SOME x::int. h [^]Hk = h [^]Hx)"
    proof -
      have "gen [^] k = gen [^] x  h [^]Hk = h [^]Hx" for x::int
        by (simp add: o group.int_pow_eq)  
      thus ?thesis by simp
    qed
    moreover have "h [^]Hk = h [^]H(SOME x::int. h [^]Hk = h [^]Hx)"
      by(intro someI_ex[of "λx::int. h [^]Hk = h [^]Hx"]; blast)
    ultimately show "f (gen [^] k) = h [^]Hk" unfolding f_def get_exp_def by metis
  qed
  thus "f  iso G H"
    using iso_cyclic_groups_generate[OF gen_closed H.gen_closed o H.is_group]
    by (auto simp flip: generator H.generator)
qed

subsection ‹Integer modular groups›

text ‹We show that integer_mod_group› (written as Z n›) is in fact a cyclic group.
For $n \neq 1$ it is generated by $1$ and in the other case by $0$.›

notation integer_mod_group ("Z")

lemma Zn_neq1_cyclic_group:
  assumes "n  1"
  shows "cyclic_group (Z n) 1"
proof(unfold cyclic_group_def cyclic_group_axioms_def, safe)
  show "group (Z n)" using group_integer_mod_group .
  then interpret group "Z n" .
  show oc: "1  carrier (Z n)"
    unfolding integer_mod_group_def integer_group_def using assms by force
  show "x  generate (Z n) {1}" if "x  carrier (Z n)" for x
    using generate_pow[OF oc] that int_pow_integer_mod_group solve_equation subgroup_self
    by fastforce
  show "x  carrier (Z n)" if "x  generate (Z n) {1}" for x using generate_incl[of "{1}"] that oc
    by fast
qed

lemma Z1_cyclic_group: "cyclic_group (Z 1) 0"
proof(unfold cyclic_group_def cyclic_group_axioms_def, safe)
  show "group (Z 1)" using group_integer_mod_group .
  then interpret group "Z 1" .
  show "0  carrier (Z 1)" unfolding integer_mod_group_def by simp
  thus "x  carrier (Z 1)" if "x  generate (Z 1) {0}" for x using generate_incl[of "{0}"] that
    by fast
  show "x  generate (Z 1) {0}" if "x  carrier (Z 1)" for x
  proof -
    from that have "x = 0" unfolding integer_mod_group_def by auto
    with generate.one[of "Z 1" "{0}"] show "x  generate (Z 1) {0}" unfolding integer_mod_group_def
      by simp
  qed
qed

lemma Zn_cyclic_group:
  obtains x where "cyclic_group (Z n) x"
  using Z1_cyclic_group Zn_neq1_cyclic_group by metis

text ‹Moreover, its order is just $n$.›

lemma Zn_order: "order (Z n) = n"
  by (unfold integer_mod_group_def integer_group_def order_def, auto)

text ‹Consequently, Z n› is isomorphic to any cyclic group of order $n$.›

lemma (in cyclic_group) Zn_iso:
  assumes "order G = n"
  shows "G  Z n"
  using Zn_order Zn_cyclic_group iso_cyclic_groups_same_order assms by metis

no_notation integer_mod_group ("Z")
end