text: from sympy.combinatorics.group_constructs import DirectProduct
  from sympy.combinatorics.perm_groups import PermutationGroup
  from sympy.combinatorics.permutations import Permutation
  
  _af_new = Permutation._af_new
  
  
  def AbelianGroup(*cyclic_orders):
      """
      Returns the direct product of cyclic groups with the given orders.
  
      Explanation
      ===========
  
      According to the structure theorem for finite abelian groups ([1]),
      every finite abelian group can be written as the direct product of
      finitely many cyclic groups.
  
      Examples
      ========
  
      >>> from sympy.combinatorics.named_groups import AbelianGroup
      >>> AbelianGroup(3, 4)
      PermutationGroup([
              (6)(0 1 2),
              (3 4 5 6)])
      >>> _.is_group
      True
  
      See Also
      ========
  
      DirectProduct
  
      References
      ==========
  
      .. [1] http://groupprops.subwiki.org/wiki/Structure_theorem_for_finitely_generated_abelian_groups
  
      """
      groups = []
      degree = 0
      order = 1
      for size in cyclic_orders:
          degree += size
          order *= size
          groups.append(CyclicGroup(size))
      G = DirectProduct(*groups)
      G._is_abelian = True
      G._degree = degree
      G._order = order
  
      return G
  
  
  def AlternatingGroup(n):
      """
      Generates the alternating group on ``n`` elements as a permutation group.
  
      Explanation
      ===========
  
      For ``n > 2``, the generators taken are ``(0 1 2), (0 1 2 ... n-1)`` for
      ``n`` odd
      and ``(0 1 2), (1 2 ... n-1)`` for ``n`` even (See [1], p.31, ex.6.9.).
      After the group is generated, some of its basic properties are set.
      The cases ``n = 1, 2`` are handled separately.
  
      Examples
      ========
  
      >>> from sympy.combinatorics.named_groups import AlternatingGroup
      >>> G = AlternatingGroup(4)
      >>> G.is_group
      True
      >>> a = list(G.generate_dimino())
      >>> len(a)
      12
      >>> all(perm.is_even for perm in a)
      True
  
      See Also
      ========
  
      SymmetricGroup, CyclicGroup, DihedralGroup
  
      References
      ==========
  
      .. [1] Armstrong, M. "Groups and Symmetry"
  
      """
      # small cases are special
      if n in (1, 2):
          return PermutationGroup([Permutation([0])])
  
      a = list(range(n))
      a[0], a[1], a[2] = a[1], a[2], a[0]
      gen1 = a
      if n % 2:
          a = list(range(1, n))
          a.append(0)
          gen2 = a
      else:
          a = list(range(2, n))
          a.append(1)
          a.insert(0, 0)
          gen2 = a
      gens = [gen1, gen2]
      if gen1 == gen2:
          gens = gens[:1]
      G = PermutationGroup([_af_new(a) for a in gens], dups=False)
  
      if n < 4:
          G._is_abelian = True
          G._is_nilpotent = True
      else:
          G._is_abelian = False
          G._is_nilpotent = False
      if n < 5:
          G._is_solvable = True
      else:
          G._is_solvable = False
      G._degree = n
      G._is_transitive = True
      G._is_alt = True
      return G
  
  
  def CyclicGroup(n):
      """
      Generates the cyclic group of order ``n`` as a permutation group.
  
      Explanation
      ===========
  
      The generator taken is the ``n``-cycle ``(0 1 2 ... n-1)``
      (in cycle notation). After the group is generated, some of its basic
      properties are set.
  
      Examples
      ========
  
      >>> from sympy.combinatorics.named_groups import CyclicGroup
      >>> G = CyclicGroup(6)
      >>> G.is_group
      True
      >>> G.order()
      6
      >>> list(G.generate_schreier_sims(af=True))
      [[0, 1, 2, 3, 4, 5], [1, 2, 3, 4, 5, 0], [2, 3, 4, 5, 0, 1],
      [3, 4, 5, 0, 1, 2], [4, 5, 0, 1, 2, 3], [5, 0, 1, 2, 3, 4]]
  
      See Also
      ========
  
      SymmetricGroup, DihedralGroup, AlternatingGroup
  
      """
      a = list(range(1, n))
      a.append(0)
      gen = _af_new(a)
      G = PermutationGroup([gen])
  
      G._is_abelian = True
      G._is_nilpotent = True
      G._is_solvable = True
      G._degree = n
      G._is_transitive = True
      G._order = n
      return G
  
  
  def DihedralGroup(n):
      r"""
      Generates the dihedral group `D_n` as a permutation group.
  
      Explanation
      ===========
  
      The dihedral group `D_n` is the group of symmetries of the regular
      ``n``-gon. The generators taken are the ``n``-cycle ``a = (0 1 2 ... n-1)``
      (a rotation of the ``n``-gon) and ``b = (0 n-1)(1 n-2)...``
      (a reflection of the ``n``-gon) in cycle rotation. It is easy to see that
      these satisfy ``a**n = b**2 = 1`` and ``bab = ~a`` so they indeed generate
      `D_n` (See [1]). After the group is generated, some of its basic properties
      are set.
  
      Examples
      ========
  
      >>> from sympy.combinatorics.named_groups import DihedralGroup
      >>> G = DihedralGroup(5)
      >>> G.is_group
      True
      >>> a = list(G.generate_dimino())
      >>> [perm.cyclic_form for perm in a]
      [[], [[0, 1, 2, 3, 4]], [[0, 2, 4, 1, 3]],
      [[0, 3, 1, 4, 2]], [[0, 4, 3, 2, 1]], [[0, 4], [1, 3]],
      [[1, 4], [2, 3]], [[0, 1], [2, 4]], [[0, 2], [3, 4]],
      [[0, 3], [1, 2]]]
  
      See Also
      ========
  
      SymmetricGroup, CyclicGroup, AlternatingGroup
  
      References
      ==========
  
      .. [1] https://en.wikipedia.org/wiki/Dihedral_group
  
      """
      # small cases are special
      if n == 1:
          return PermutationGroup([Permutation([1, 0])])
      if n == 2:
          return PermutationGroup([Permutation([1, 0, 3, 2]),
                 Permutation([2, 3, 0, 1]), Permutation([3, 2, 1, 0])])
  
      a = list(range(1, n))
      a.append(0)
      gen1 = _af_new(a)
      a = list(range(n))
      a.reverse()
      gen2 = _af_new(a)
      G = PermutationGroup([gen1, gen2])
      # if n is a power of 2, group is nilpotent
      if n & (n-1) == 0:
          G._is_nilpotent = True
      else:
          G._is_nilpotent = False
      G._is_abelian = False
      G._is_solvable = True
      G._degree = n
      G._is_transitive = True
      G._order = 2*n
      return G
  
  
  def SymmetricGroup(n):
      """
      Generates the symmetric group on ``n`` elements as a permutation group.
  
      Explanation
      ===========
  
      The generators taken are the ``n``-cycle
      ``(0 1 2 ... n-1)`` and the transposition ``(0 1)`` (in cycle notation).
      (See [1]). After the group is generated, some of its basic properties
      are set.
  
      Examples
      ========
  
      >>> from sympy.combinatorics.named_groups import SymmetricGroup
      >>> G = SymmetricGroup(4)
      >>> G.is_group
      True
      >>> G.order()
      24
      >>> list(G.generate_schreier_sims(af=True))
      [[0, 1, 2, 3], [1, 2, 3, 0], [2, 3, 0, 1], [3, 1, 2, 0], [0, 2, 3, 1],
      [1, 3, 0, 2], [2, 0, 1, 3], [3, 2, 0, 1], [0, 3, 1, 2], [1, 0, 2, 3],
      [2, 1, 3, 0], [3, 0, 1, 2], [0, 1, 3, 2], [1, 2, 0, 3], [2, 3, 1, 0],
      [3, 1, 0, 2], [0, 2, 1, 3], [1, 3, 2, 0], [2, 0, 3, 1], [3, 2, 1, 0],
      [0, 3, 2, 1], [1, 0, 3, 2], [2, 1, 0, 3], [3, 0, 2, 1]]
  
      See Also
      ========
  
      CyclicGroup, DihedralGroup, AlternatingGroup
  
      References
      ==========
  
      .. [1] https://en.wikipedia.org/wiki/Symmetric_group#Generators_and_relations
  
      """
      if n == 1:
          G = PermutationGroup([Permutation([0])])
      elif n == 2:
          G = PermutationGroup([Permutation([1, 0])])
      else:
          a = list(range(1, n))
          a.append(0)
          gen1 = _af_new(a)
          a = list(range(n))
          a[0], a[1] = a[1], a[0]
          gen2 = _af_new(a)
          G = PermutationGroup([gen1, gen2])
      if n < 3:
          G._is_abelian = True
          G._is_nilpotent = True
      else:
          G._is_abelian = False
          G._is_nilpotent = False
      if n < 5:
          G._is_solvable = True
      else:
          G._is_solvable = False
      G._degree = n
      G._is_transitive = True
      G._is_sym = True
      return G
  
  
  def RubikGroup(n):
      """Return a group of Rubik's cube generators
  
      >>> from sympy.combinatorics.named_groups import RubikGroup
      >>> RubikGroup(2).is_group
      True
      """
      from sympy.combinatorics.generators import rubik
      if n <= 1:
          raise ValueError("Invalid cube. n has to be greater than 1")
      return PermutationGroup(rubik(n))
  