Volker Braun avatar Volker Braun committed 1ffacf4

updated to sage-5.6

Comments (0)

Files changed (38)

13687_parent_for_groups_all.patch

+# HG changeset patch
+# User Volker Braun <vbraun@stp.dias.ie>
+# Date 1352835583 18000
+# Node ID 5a5673e6beb0a82cda2dcfedae5c874ab2176588
+# Parent  908396c26c29b73e36d91ae4821b8a353ef23c83
+New-style parents for Abelian groups
+
+diff --git a/doc/de/tutorial/tour_advanced.rst b/doc/de/tutorial/tour_advanced.rst
+--- a/doc/de/tutorial/tour_advanced.rst
++++ b/doc/de/tutorial/tour_advanced.rst
+@@ -362,7 +362,7 @@
+     Dirichlet character modulo 20 of conductor 5 mapping 11 |--> 1, 17 |--> i)
+ 
+     sage: G.unit_gens()
+-    [11, 17]
++    (11, 17)
+     sage: G.zeta()
+     i
+     sage: G.zeta_order()
+diff --git a/doc/de/tutorial/tour_groups.rst b/doc/de/tutorial/tour_groups.rst
+--- a/doc/de/tutorial/tour_groups.rst
++++ b/doc/de/tutorial/tour_groups.rst
+@@ -82,10 +82,10 @@
+     sage: d * b**2 * c**3 
+     b^2*c^3*d
+     sage: F = AbelianGroup(3,[2]*3); F
+-    Multiplicative Abelian Group isomorphic to C2 x C2 x C2
++    Multiplicative Abelian group isomorphic to C2 x C2 x C2
+     sage: H = AbelianGroup([2,3], names="xy"); H
+-    Multiplicative Abelian Group isomorphic to C2 x C3
++    Multiplicative Abelian group isomorphic to C2 x C3
+     sage: AbelianGroup(5)
+-    Multiplicative Abelian Group isomorphic to Z x Z x Z x Z x Z
++    Multiplicative Abelian group isomorphic to Z x Z x Z x Z x Z
+     sage: AbelianGroup(5).order()
+     +Infinity
+diff --git a/doc/en/bordeaux_2008/nf_galois_groups.rst b/doc/en/bordeaux_2008/nf_galois_groups.rst
+--- a/doc/en/bordeaux_2008/nf_galois_groups.rst
++++ b/doc/en/bordeaux_2008/nf_galois_groups.rst
+@@ -311,7 +311,7 @@
+     sage: category(C)
+     Category of groups
+     sage: C.gens()
+-    [Fractional ideal class (5, a), Fractional ideal class (3, a)]
++    (Fractional ideal class (5, a), Fractional ideal class (3, a))
+ 
+ 
+ Arithmetic in the class group
+diff --git a/doc/en/reference/groups.rst b/doc/en/reference/groups.rst
+--- a/doc/en/reference/groups.rst
++++ b/doc/en/reference/groups.rst
+@@ -9,9 +9,12 @@
+    sage/groups/group
+    sage/groups/generic
+    sage/groups/abelian_gps/abelian_group
++   sage/groups/abelian_gps/values
++   sage/groups/abelian_gps/dual_abelian_group
++   sage/groups/abelian_gps/element_base
+    sage/groups/abelian_gps/abelian_group_element
++   sage/groups/abelian_gps/dual_abelian_group_element
+    sage/groups/abelian_gps/abelian_group_morphism
+-   sage/groups/abelian_gps/dual_abelian_group
+    sage/groups/additive_abelian/additive_abelian_group
+    sage/groups/additive_abelian/additive_abelian_wrapper
+    sage/groups/perm_gps/permgroup
+diff --git a/doc/en/tutorial/tour_advanced.rst b/doc/en/tutorial/tour_advanced.rst
+--- a/doc/en/tutorial/tour_advanced.rst
++++ b/doc/en/tutorial/tour_advanced.rst
+@@ -360,7 +360,7 @@
+     Dirichlet character modulo 20 of conductor 5 mapping 11 |--> 1, 17 |--> i)
+ 
+     sage: G.unit_gens()
+-    [11, 17]
++    (11, 17)
+     sage: G.zeta()
+     i
+     sage: G.zeta_order()
+diff --git a/doc/en/tutorial/tour_groups.rst b/doc/en/tutorial/tour_groups.rst
+--- a/doc/en/tutorial/tour_groups.rst
++++ b/doc/en/tutorial/tour_groups.rst
+@@ -80,10 +80,10 @@
+     sage: d * b**2 * c**3 
+     b^2*c^3*d
+     sage: F = AbelianGroup(3,[2]*3); F
+-    Multiplicative Abelian Group isomorphic to C2 x C2 x C2
++    Multiplicative Abelian group isomorphic to C2 x C2 x C2
+     sage: H = AbelianGroup([2,3], names="xy"); H
+-    Multiplicative Abelian Group isomorphic to C2 x C3
++    Multiplicative Abelian group isomorphic to C2 x C3
+     sage: AbelianGroup(5)
+-    Multiplicative Abelian Group isomorphic to Z x Z x Z x Z x Z
++    Multiplicative Abelian group isomorphic to Z x Z x Z x Z x Z
+     sage: AbelianGroup(5).order()
+     +Infinity
+diff --git a/doc/fr/tutorial/tour_advanced.rst b/doc/fr/tutorial/tour_advanced.rst
+--- a/doc/fr/tutorial/tour_advanced.rst
++++ b/doc/fr/tutorial/tour_advanced.rst
+@@ -360,7 +360,7 @@
+     Dirichlet character modulo 20 of conductor 5 mapping 11 |--> 1, 17 |--> i)
+ 
+     sage: G.unit_gens()
+-    [11, 17]
++    (11, 17)
+     sage: G.zeta()
+     i
+     sage: G.zeta_order()
+diff --git a/doc/fr/tutorial/tour_groups.rst b/doc/fr/tutorial/tour_groups.rst
+--- a/doc/fr/tutorial/tour_groups.rst
++++ b/doc/fr/tutorial/tour_groups.rst
+@@ -83,10 +83,10 @@
+     sage: d * b**2 * c**3 
+     b^2*c^3*d
+     sage: F = AbelianGroup(3,[2]*3); F
+-    Multiplicative Abelian Group isomorphic to C2 x C2 x C2
++    Multiplicative Abelian group isomorphic to C2 x C2 x C2
+     sage: H = AbelianGroup([2,3], names="xy"); H
+-    Multiplicative Abelian Group isomorphic to C2 x C3
++    Multiplicative Abelian group isomorphic to C2 x C3
+     sage: AbelianGroup(5)
+-    Multiplicative Abelian Group isomorphic to Z x Z x Z x Z x Z
++    Multiplicative Abelian group isomorphic to Z x Z x Z x Z x Z
+     sage: AbelianGroup(5).order()
+     +Infinity
+diff --git a/doc/ru/tutorial/tour_advanced.rst b/doc/ru/tutorial/tour_advanced.rst
+--- a/doc/ru/tutorial/tour_advanced.rst
++++ b/doc/ru/tutorial/tour_advanced.rst
+@@ -325,7 +325,7 @@
+     Dirichlet character modulo 20 of conductor 5 mapping 11 |--> 1, 17 |--> i)
+ 
+     sage: G.unit_gens()
+-    [11, 17]
++    (11, 17)
+     sage: G.zeta()
+     i
+     sage: G.zeta_order()
+diff --git a/doc/ru/tutorial/tour_groups.rst b/doc/ru/tutorial/tour_groups.rst
+--- a/doc/ru/tutorial/tour_groups.rst
++++ b/doc/ru/tutorial/tour_groups.rst
+@@ -80,10 +80,10 @@
+     sage: d * b**2 * c**3 
+     b^2*c^3*d
+     sage: F = AbelianGroup(3,[2]*3); F
+-    Multiplicative Abelian Group isomorphic to C2 x C2 x C2
++    Multiplicative Abelian group isomorphic to C2 x C2 x C2
+     sage: H = AbelianGroup([2,3], names="xy"); H
+-    Multiplicative Abelian Group isomorphic to C2 x C3
++    Multiplicative Abelian group isomorphic to C2 x C3
+     sage: AbelianGroup(5)
+-    Multiplicative Abelian Group isomorphic to Z x Z x Z x Z x Z
++    Multiplicative Abelian group isomorphic to Z x Z x Z x Z x Z
+     sage: AbelianGroup(5).order()
+     +Infinity
+diff --git a/module_list.py b/module_list.py
+--- a/module_list.py
++++ b/module_list.py
+@@ -488,6 +488,9 @@
+     Extension('sage.groups.group',
+               sources = ['sage/groups/group.pyx']),
+ 
++    Extension('sage.groups.old',
++              sources = ['sage/groups/old.pyx']),
++
+     Extension('sage.groups.perm_gps.permgroup_element',
+               sources = ['sage/groups/perm_gps/permgroup_element.pyx']),
+ 
+diff --git a/sage/algebras/group_algebra.py b/sage/algebras/group_algebra.py
+--- a/sage/algebras/group_algebra.py
++++ b/sage/algebras/group_algebra.py
+@@ -30,6 +30,7 @@
+ from sage.algebras.algebra_element import AlgebraElement
+ from sage.rings.all import IntegerRing
+ from sage.groups.group import Group
++from sage.groups.old import Group as OldGroup
+ from sage.structure.formal_sum import FormalSums, FormalSum
+ from sage.sets.set import Set
+ 
+@@ -67,7 +68,7 @@
+         if not base_ring.is_commutative():
+             raise NotImplementedError("Base ring must be commutative")
+         
+-        if not isinstance(group, Group):
++        if not isinstance(group, (Group, OldGroup)):
+             raise TypeError('"%s" is not a group' % group)
+ 
+         ParentWithGens.__init__(self, base_ring, category = GroupAlgebras(base_ring))
+@@ -251,7 +252,7 @@
+             sage: ZG(1) == ZG(G(1))
+             True
+             sage: ZG(FormalSum([(1,f), (2, f**2)]))
+-            2*f^2 + f
++            f + 2*f^2
+             sage: G = GL(2,7)
+             sage: OG = GroupAlgebra(G, ZZ[sqrt(5)])
+             sage: OG(2)
+diff --git a/sage/algebras/group_algebra_new.py b/sage/algebras/group_algebra_new.py
+--- a/sage/algebras/group_algebra_new.py
++++ b/sage/algebras/group_algebra_new.py
+@@ -256,10 +256,11 @@
+             Group algebra of group "General Linear Group of degree 3 over Finite Field of size 7" over base ring Integer Ring
+         """
+         from sage.groups.group import Group
++        from sage.groups.old import Group as OldGroup
+         if not base_ring.is_commutative():
+             raise NotImplementedError("Base ring must be commutative")
+ 
+-        if not isinstance(group, Group):
++        if not isinstance(group, (Group, OldGroup)):
+             raise TypeError('"%s" is not a group' % group)
+ 
+         self._group = group
+@@ -680,7 +681,7 @@
+             True
+         """
+         from sage.rings.all import is_Ring
+-        from sage.groups.group import Group
++        from sage.groups.old import Group
+         k = self.base_ring()
+         G = self.group()
+         if isinstance(S, GroupAlgebra):
+@@ -732,7 +733,7 @@
+             [0 1]
+         """
+         from sage.rings.all import is_Ring
+-        from sage.groups.group import Group
++        from sage.groups.old import Group
+         from sage.structure.formal_sum import FormalSum
+         k = self.base_ring()
+         G = self.group()
+diff --git a/sage/all.py b/sage/all.py
+--- a/sage/all.py
++++ b/sage/all.py
+@@ -42,6 +42,7 @@
+ exit = quit
+ 
+ import os, sys
++import operator
+ 
+ if 'SAGE_ROOT' not in os.environ:
+     raise RuntimeError("To use the Sage libraries, set the environment variable SAGE_ROOT to the Sage build directory and LD_LIBRARY_PATH to $SAGE_ROOT/local/lib")
+diff --git a/sage/categories/action.pyx b/sage/categories/action.pyx
+--- a/sage/categories/action.pyx
++++ b/sage/categories/action.pyx
+@@ -216,8 +216,9 @@
+     An action that acts as the inverse of the given action.
+ 
+     TESTS:
++
+     This illustrates a shortcoming in the current coercion model.
+-    See the comments in _call_ below. 
++    See the comments in _call_ below::
+     
+         sage: x = polygen(QQ,'x')
+         sage: a = 2*x^2+2; a
+@@ -231,10 +232,11 @@
+     def __init__(self, Action action):
+         G = action.G
+         try:
++            from sage.groups.old import Group as OldGroup
+             from sage.groups.group import Group
+             # We must be in the case that parent(~a) == parent(a)
+             # so we can invert in call_c code below.
+-            if (PY_TYPE_CHECK(G, Group) and G.is_multiplicative()) or G.is_field():
++            if (isinstance(G, (Group, OldGroup)) and G.is_multiplicative()) or G.is_field():
+                 Action.__init__(self, G, action.underlying_set(), action._is_left)
+                 self._action = action
+                 return
+diff --git a/sage/categories/category.py b/sage/categories/category.py
+--- a/sage/categories/category.py
++++ b/sage/categories/category.py
+@@ -23,7 +23,7 @@
+     sage: Sets()
+     Category of sets
+     sage: GSets(AbelianGroup([2,4,9]))
+-    Category of G-sets for Multiplicative Abelian Group isomorphic to C2 x C4 x C9
++    Category of G-sets for Multiplicative Abelian group isomorphic to C2 x C4 x C9
+     sage: Semigroups()
+     Category of semigroups
+     sage: VectorSpaces(FiniteField(11))
+diff --git a/sage/groups/abelian_gps/abelian_group.py b/sage/groups/abelian_gps/abelian_group.py
+--- a/sage/groups/abelian_gps/abelian_group.py
++++ b/sage/groups/abelian_gps/abelian_group.py
+@@ -1,31 +1,60 @@
+ r"""
+ Multiplicative Abelian Groups
+ 
+-AUTHORS:
++This module lets you compute with finitely generated Abelian groups of the form
+ 
+-- William Stein, David Joyner (2008-12): added (user requested) is_cyclic,
+-  fixed elementary_divisors.
++.. math::
+ 
+-- David Joyner (2006-03): (based on free abelian monoids by David
+-  Kohel)
++    G = \ZZ^r \oplus \ZZ_{k_1} \oplus \cdots \oplus \ZZ_{k_t}
+ 
+-- David Joyner (2006-05) several significant bug fixes
++It is customary to denote the infinite cyclic group `\ZZ` as having
++order `0`, so the data defining the Abelian group can be written as an
++integer vector
+ 
+-- David Joyner (2006-08) trivial changes to docs, added random, fixed
+-  bug in how invariants are recorded
++.. math::
+ 
+-- David Joyner (2006-10) added dual_group method
++    \vec{k} = (0, \dots, 0, k_1, \dots, k_t)
+ 
+-- David Joyner (2008-02) fixed serious bug in word_problem
++where there are `r` zeroes and `t` non-zero values. To construct this
++Abelian group in Sage, you can either specify all entries of `\vec{k}`
++or only the non-zero entries together with the total number of
++generators::
+ 
+-- David Joyner (2008-03) fixed bug in trivial group case
++    sage: AbelianGroup([0,0,0,2,3])
++    Multiplicative Abelian group isomorphic to Z x Z x Z x C2 x C3
++    sage: AbelianGroup(5, [2,3])
++    Multiplicative Abelian group isomorphic to Z x Z x Z x C2 x C3
+ 
+-- David Loeffler (2009-05) added subgroups method
++It is also legal to specify `1` as the order. The corresponding
++generator will be the neutral element, but it will still take up an
++index in the labelling of the generators::
+ 
++    sage: G = AbelianGroup([2,1,3], names='g')
++    sage: G.gens()
++    (g0, 1, g2)
+ 
+-TODO: 
++Note that this presentation is not unique, for example `\ZZ_6 = \ZZ_2
++\times \ZZ_3`. The orders of the generators
++`\vec{k}=(0,\dots,0,k_1,\dots, k_t)` has previously been called
++invariants in Sage, even though they are not necessarily the (unique)
++invariant factors of the group. You should now use
++:meth:`~AbelianGroup_class.gens_orders` instead::
+ 
+-- additive abelian groups should also be supported
++   sage: J = AbelianGroup([2,0,3,2,4]);  J
++   Multiplicative Abelian group isomorphic to C2 x Z x C3 x C2 x C4
++   sage: J.gens_orders()            # use this instead
++   (2, 0, 3, 2, 4)
++   sage: J.invariants()             # deprecated
++   (2, 0, 3, 2, 4)
++   sage: J.elementary_divisors()    # these are the "invariant factors"
++   (2, 2, 12, 0)
++   sage: for i in range(J.ngens()):
++   ...       print i, J.gen(i), J.gen(i).order()     # or use this form
++   0 f0 2
++   1 f1 +Infinity
++   2 f2 3
++   3 f3 2
++   4 f4 4
+ 
+ Background on invariant factors and the Smith normal form 
+ (according to section 4.1 of [C1]): An abelian group is a
+@@ -37,7 +66,7 @@
+ 
+ .. math::
+ 
+-   A = \langle a_1\rangle \times \dots \times  \langle a_\ell\rangle ,
++    A = \langle a_1\rangle \times \dots \times  \langle a_\ell\rangle ,
+ 
+ where `ord(a_i)=p_i^{c_i}`, for some primes `p_i` and some
+ positive integers `c_i`, `i=1,...,\ell`. GAP calls the 
+@@ -59,15 +88,15 @@
+ 
+ .. math::
+ 
+-   F=
+-   \left(
+-   \begin{array}{cccc}
+-   f_{11} & f_{12} & \dots & f_{1m}\\
+-   f_{21} & f_{22} & \dots & f_{2m}\\
+-   \vdots &        & \ddots & \vdots \\
+-   f_{\ell,1} & f_{\ell,2} & \dots & f_{\ell,m}
+-   \end{array}
+-   \right),
++    F=
++    \left(
++    \begin{array}{cccc}
++    f_{11} & f_{12} & \dots & f_{1m}\\
++    f_{21} & f_{22} & \dots & f_{2m}\\
++    \vdots &        & \ddots & \vdots \\
++    f_{\ell,1} & f_{\ell,2} & \dots & f_{\ell,m}
++    \end{array}
++    \right),
+ 
+ regarded as a map 
+ `\ZZ^m\rightarrow (\ZZ/p_1^{c_1}\ZZ)\times ...\times (\ZZ/p_\ell^{c_\ell}\ZZ)`.
+@@ -80,41 +109,37 @@
+ 
+ .. math::
+ 
+-  s_1, s_2/s_1, s_3/s_2, ... s_r/s_{r-1}.
+-
+-
++    s_1, s_2/s_1, s_3/s_2, ... s_r/s_{r-1}.
+ 
+ Sage supports multiplicative abelian groups on any prescribed finite
+-number `n \geq 0` of generators.  Use the ``AbelianGroup`` function
+-to create an abelian group, and the ``gen`` and ``gens``
+-functions to obtain the corresponding generators.  You can print the
+-generators as arbitrary strings using the optional ``names``
+-argument to the ``AbelianGroup`` function.
++number `n \geq 0` of generators.  Use the :func:`AbelianGroup`
++function to create an abelian group, and the
++:meth:`~AbelianGroup_class.gen` and :meth:`~AbelianGroup_class.gens`
++methods to obtain the corresponding generators.  You can print the
++generators as arbitrary strings using the optional ``names`` argument
++to the :func:`AbelianGroup` function.
+ 
+ EXAMPLE 1:
+ 
+ We create an abelian group in zero or more variables; the syntax T(1)
+-creates the identity element even in the rank zero case.
+-
+-::
++creates the identity element even in the rank zero case::
+ 
+     sage: T = AbelianGroup(0,[])
+     sage: T
+-    Trivial Abelian Group
++    Trivial Abelian group
+     sage: T.gens()
+     ()
+     sage: T(1)
+     1
+ 
+-EXAMPLE 2: An abelian group uses a multiplicative representation of
+-elements, but the underlying representation is lists of integer
+-exponents.
++EXAMPLE 2:
+ 
+-::
++An Abelian group uses a multiplicative representation of elements, but
++the underlying representation is lists of integer exponents::
+ 
+     sage: F = AbelianGroup(5,[3,4,5,5,7],names = list("abcde"))
+     sage: F
+-    Multiplicative Abelian Group isomorphic to C3 x C4 x C5 x C5 x C7
++    Multiplicative Abelian group isomorphic to C3 x C4 x C5 x C5 x C7
+     sage: (a,b,c,d,e) = F.gens()
+     sage: a*b^2*e*d
+     a*b^2*d*e
+@@ -139,36 +164,59 @@
+ 
+    Many basic properties for infinite abelian groups are not
+    implemented.
++
++
++AUTHORS:
++
++- William Stein, David Joyner (2008-12): added (user requested) is_cyclic,
++  fixed elementary_divisors.
++
++- David Joyner (2006-03): (based on free abelian monoids by David
++  Kohel)
++
++- David Joyner (2006-05) several significant bug fixes
++
++- David Joyner (2006-08) trivial changes to docs, added random, fixed
++  bug in how invariants are recorded
++
++- David Joyner (2006-10) added dual_group method
++
++- David Joyner (2008-02) fixed serious bug in word_problem
++
++- David Joyner (2008-03) fixed bug in trivial group case
++
++- David Loeffler (2009-05) added subgroups method
++
++- Volker Braun (2012-11) port to new Parent base. Use tuples for
++  immutables. Rename invariants to gens_orders.
+ """
+ 
+ ##########################################################################
+-#  Copyright (C) 2006 David Joyner and William Stein
++#  Copyright (C) 2006 William Stein <wstein@gmail.com>
++#  Copyright (C) 2006 David Joyner  <wdjoyner@gmail.com>
++#  Copyright (C) 2012 Volker Braun  <vbraun.name@gmail.com>
+ #
+ #  Distributed under the terms of the GNU General Public License (GPL):
+ #
+ #                  http://www.gnu.org/licenses/
+ ##########################################################################
+ 
+-# TODO: change the "invariants" terminology everywhere to elementary_divisors
+-
+-
+ 
+ from sage.rings.integer import Integer
+-
++from sage.rings.integer_ring import ZZ
++from sage.structure.unique_representation import UniqueRepresentation
+ from sage.rings.infinity import infinity
+ from sage.rings.arith import divisors, gcd
+-from abelian_group_element import AbelianGroupElement
++from sage.groups.abelian_gps.abelian_group_element import AbelianGroupElement
++from sage.misc.cachefunc import cached_method
+ from sage.misc.misc import prod
+ from sage.misc.mrange import mrange, cartesian_product_iterator
+-import sage.groups.group as group
+-from sage.rings.integer_ring import IntegerRing
+ from sage.rings.arith import lcm
+-ZZ = IntegerRing()
++from sage.groups.group import AbelianGroup as AbelianGroupBase
++
+ 
+ # TODO: this uses perm groups - the AbelianGroupElement instance method
+ # uses a different implementation.
+-  
+-
+ def word_problem(words, g, verbose = False):
+     r"""
+     G and H are abelian, g in G, H is a subgroup of G generated by a
+@@ -202,7 +250,7 @@
+     EXAMPLE::
+     
+         sage: G.<a,b,c> = AbelianGroup(3,[2,3,4]); G
+-        Multiplicative Abelian Group isomorphic to C2 x C3 x C4
++        Multiplicative Abelian group isomorphic to C2 x C3 x C4
+         sage: w = word_problem([a*b,a*c], b*c); w #random 
+         [[a*b, 1], [a*c, 1]]  
+         sage: prod([x^i for x,i in w]) == b*c
+@@ -242,13 +290,12 @@
+           AbelianGroupElement is implemented differently (wrapping
+           GAP's 'EpimorphismFromFreeGroup' and
+           'PreImagesRepresentative') and may be faster.
+-       
+     """
+     from sage.interfaces.all import gap
+     G = g.parent()
+-    invs = G.invariants()
++    invs = map(str, G.gens_orders())
+     gap.eval("l:=One(Rationals)")
+-    s1 = 'A:=AbelianGroup(%s)'%invs
++    s1 = 'A:=AbelianGroup([' + ','.join(invs) + '])'
+     gap.eval(s1)
+     s2 = 'phi:=IsomorphismPermGroup(A)'
+     gap.eval(s2)
+@@ -279,32 +326,90 @@
+     return [[words[l3[2*i]-1],LL[i]] for i in range(len(LL))]
+ 
+ 
+-def AbelianGroup(n, invfac=None, names="f"):
++def _normalize(n, gens_orders=None, names="f"):
++    """
++    Helper function for :func:`AbelianGroup`. Beat some sense into the
++    arguments.
++
++    This function is also used by some descendents of
++    :class:`AbelianGroup_class`.
++
++    INPUT:
++
++    See :func:`AbelianGroup`
++
++    OUTPUT:
++
++    Unique data for defining a :class:`AbelianGroup_class`. Raises an
++    exception if the input is invalid.
++
++    EXAMPLES::
++
++        sage: from sage.groups.abelian_gps.abelian_group import _normalize
++        sage: _normalize(5, [2,1,0], names='abc')
++        ((0, 0, 2, 1, 0), 'abc')
++        sage: _normalize(5, (2.0, 1.0, 0/1), names=list('abc'))
++        ((0, 0, 2, 1, 0), ('a', 'b', 'c'))
++        sage: _normalize([0,2,1,0], names='a')
++        ((0, 2, 1, 0), 'a')
++
++    TESTS::
++
++        sage: _normalize(5, (2.0, 1.5, 0/1), names=list('abc'))
++        Traceback (most recent call last):
++        ...
++        TypeError: Attempt to coerce non-integral RealNumber to Integer
++        sage: _normalize('1', '[2]', names='a')
++        Traceback (most recent call last):
++        ...
++        TypeError: unable to convert x (=[) to an integer
++        sage: _normalize(3, 'str', names='a')
++        Traceback (most recent call last):
++        ...
++        TypeError: unable to convert x (=s) to an integer
++   """
++    if gens_orders is None:
++        if isinstance(n, (list, tuple)):
++            gens_orders = n
++            n = len(n)
++        else:
++            gens_orders = []
++    n = ZZ(n)
++    if len(gens_orders) < n:
++        gens_orders = [0] * (n - len(gens_orders)) + list(gens_orders)
++    gens_orders = tuple(ZZ(i) for i in gens_orders)
++    if len(gens_orders) > n:
++        raise ValueError('gens_orders (='+str(gens_orders)+') must have length n (='+str(n)+')')
++    if isinstance(names, list):
++        names = tuple(names)
++    return (gens_orders, names)
++
++def AbelianGroup(n, gens_orders=None, names="f"):
+     r"""
+     Create the multiplicative abelian group in `n` generators
+-    with given invariants (which need not be prime powers).
++    with given orders of generators (which need not be prime powers).
+     
+     INPUT:
+     
++    - ``n`` -- integer (optional). If not specified, will be derived
++       from ``gens_orders``.
+     
+-    -  ``n`` - integer
++    - ``gens_orders`` -- a list of non-negative integers in the form
++       `[a_0, a_1, \dots, a_{n-1}]`, typically written in increasing
++       order. This list is padded with zeros if it has length less
++       than n. The orders of the commuting generators, with `0`
++       denoting an infinite cyclic factor.
+     
+-    -  ``invfac`` - (the"invariant factors") a list of
+-       non-negative integers in the form [a0, a1,...,a(n-1)], typically
+-       written in increasing order. This list is padded with zeros if it
+-       has length less than n.
++    -  ``names`` -- (optional) names of generators
+     
+-    -  ``names`` - (optional) names of generators
++    Alternatively, you can also give input in the form
++    ``AbelianGroup(gens_orders, names="f")``, where the names keyword
++    argument must be explicitly named.
+     
+-       Alternatively, you can also give input in the following form:
+-    
+-       ``AbelianGroup(invfac, names="f")``,
+-    
+-       where names must be explicitly named.
+-    
+-    
+-    OUTPUT: Abelian group with generators and invariant type. The
+-    default name for generator A.i is fi, as in GAP.
++    OUTPUT:
++
++    Abelian group with generators and invariant type. The default name
++    for generator ``A.i`` is ``fi``, as in GAP.
+     
+     EXAMPLES::
+     
+@@ -317,52 +422,41 @@
+         sage: d * b**2 * c**3 
+         b^2*c^3*d
+         sage: F = AbelianGroup(3,[2]*3); F
+-        Multiplicative Abelian Group isomorphic to C2 x C2 x C2
++        Multiplicative Abelian group isomorphic to C2 x C2 x C2
+         sage: H = AbelianGroup([2,3], names="xy"); H
+-        Multiplicative Abelian Group isomorphic to C2 x C3
++        Multiplicative Abelian group isomorphic to C2 x C3
+         sage: AbelianGroup(5)
+-        Multiplicative Abelian Group isomorphic to Z x Z x Z x Z x Z
++        Multiplicative Abelian group isomorphic to Z x Z x Z x Z x Z
+         sage: AbelianGroup(5).order()
+         +Infinity
+     
+-    Notice how `0`'s are padded on.
++    Notice that `0`'s are prepended if necessary::
+     
+-    ::
+-    
+-        sage: AbelianGroup(5, [2,3,4])
+-        Multiplicative Abelian Group isomorphic to Z x Z x C2 x C3 x C4
+-    
+-    The invariant list can't be longer than the number of generators.
+-    
+-    ::
++        sage: G = AbelianGroup(5, [2,3,4]);  G
++        Multiplicative Abelian group isomorphic to Z x Z x C2 x C3 x C4
++        sage: G.gens_orders()
++        (0, 0, 2, 3, 4)
++
++    The invariant list must not be longer than the number of generators::
+     
+         sage: AbelianGroup(2, [2,3,4])
+         Traceback (most recent call last):
+         ...
+-        ValueError: invfac (=[2, 3, 4]) must have length n (=2)
++        ValueError: gens_orders (=(2, 3, 4)) must have length n (=2)
+     """
+-    if invfac is None:
+-        if isinstance(n, (list, tuple)):
+-            invfac = n
+-            n = len(n)
+-        else:
+-            invfac = []
+-    if len(invfac) < n:
+-        invfac = [0] * (n - len(invfac)) + invfac 
+-    elif len(invfac) > n:
+-        raise ValueError, "invfac (=%s) must have length n (=%s)"%(invfac, n)
+-    M = AbelianGroup_class(n, invfac, names)
++    gens_orders, names = _normalize(n, gens_orders, names)
++    M = AbelianGroup_class(gens_orders, names)
+     return M
+ 
+ def is_AbelianGroup(x):
+     """
+-    Return True if `x` is an abelian group.
++    Return True if ``x`` is an Abelian group.
+     
+     EXAMPLES::
+     
+         sage: from sage.groups.abelian_gps.abelian_group import is_AbelianGroup
+         sage: F = AbelianGroup(5,[5,5,7,8,9],names = list("abcde")); F
+-        Multiplicative Abelian Group isomorphic to C5 x C5 x C7 x C8 x C9
++        Multiplicative Abelian group isomorphic to C5 x C5 x C7 x C8 x C9
+         sage: is_AbelianGroup(F)
+         True
+         sage: is_AbelianGroup(AbelianGroup(7, [3]*7))
+@@ -371,142 +465,165 @@
+     return isinstance(x, AbelianGroup_class)
+ 
+ 
+-class AbelianGroup_class(group.AbelianGroup):
++class AbelianGroup_class(UniqueRepresentation, AbelianGroupBase):
+     """
+-    Abelian group on `n` generators. The invariant factors
+-    `[a_1,a_2,...,a_k]` need not be prime powers.
+-    divisors will be).
++    The parent for Abelian groups with chosen generator orders.
++
++    .. warning::
++
++        You should use :func:`AbelianGroup` to construct Abelian
++        groups and not instantiate this class directly.
++    
++    INPUT:
++
++    - ``generator_orders`` -- list of integers. The orders of the
++      (commuting) generators. Zero denotes an infinite cyclic
++      generator.
++
++    - ``names`` -- names of the group generators (optional).
+     
+     EXAMPLES::
+     
++        sage: Z2xZ3 = AbelianGroup([2,3])
++        sage: Z6 = AbelianGroup([6])
++        sage: Z2xZ3 is Z2xZ3, Z6 is Z6
++        (True, True)
++        sage: Z2xZ3 is Z6
++        False
++        sage: Z2xZ3 == Z6
++        True
++
+         sage: F = AbelianGroup(5,[5,5,7,8,9],names = list("abcde")); F
+-        Multiplicative Abelian Group isomorphic to C5 x C5 x C7 x C8 x C9
++        Multiplicative Abelian group isomorphic to C5 x C5 x C7 x C8 x C9
+         sage: F = AbelianGroup(5,[2, 4, 12, 24, 120],names = list("abcde")); F
+-        Multiplicative Abelian Group isomorphic to C2 x C4 x C12 x C24 x C120
++        Multiplicative Abelian group isomorphic to C2 x C4 x C12 x C24 x C120
+         sage: F.elementary_divisors()
+-        [2, 4, 12, 24, 120]
+-
+-    The entry 1 in the list of invariants is ignored::
+-
+-        sage: F = AbelianGroup(3,[1,2,3],names='a')
+-        sage: F.invariants()
+-        [2, 3]
+-        sage: F.gens()
+-        (a0, a1)
+-        sage: F.ngens()
+-        2
+-        sage: (F.0).order()
+-        2
+-        sage: (F.1).order()
+-        3
+-        sage: AbelianGroup(1, [1], names='e')
+-        Multiplicative Abelian Group isomorphic to C1
+-        sage: AbelianGroup(1, [1], names='e').gens()
+-        (e,)
+-        sage: AbelianGroup(1, [1], names='e').list()
+-        [1]
+-        sage: AbelianGroup(3, [2, 1, 2], names=list('abc')).list()
+-        [1, b, a, a*b]
++        (2, 4, 12, 24, 120)
+ 
+         sage: F.category()
+         Category of groups
+ 
++    TESTS::
++
++        sage: AbelianGroup([]).gens_orders()
++        ()
++        sage: AbelianGroup([1]).gens_orders()
++        (1,)
++        sage: AbelianGroup([1,1]).gens_orders()
++        (1, 1)
++        sage: AbelianGroup(0).gens_orders()
++        ()
+     """
+-    def __init__(self, n, invfac, names="f"):
+-        #invfac.sort()
+-        n = Integer(n)
+-        # if necessary, remove 1 from invfac first
+-        if n != 1:
+-            while True:
+-                try:
+-                    i = invfac.index(1)
+-                except ValueError:
+-                    break
+-                else:
+-                    del invfac[i]
+-                    n = n-1
++    Element = AbelianGroupElement
+ 
+-        if n < 0:
+-            raise ValueError, "n (=%s) must be nonnegative."%n
++    def __init__(self, generator_orders, names):
++        """
++        The Python constructor
+ 
+-        self.__invariants = invfac
++        TESTS::
+ 
+-        # *now* define ngens
+-        self.__ngens = len(self.__invariants)
+-        self._assign_names(names[:n])
+-        from sage.categories.groups import Groups
+-        group.Group.__init__(self, category = Groups()) # should be CommutativeGroups()
++            sage: G = AbelianGroup([0,5,0,7],names = list("abcd")); G
++            Multiplicative Abelian group isomorphic to Z x C5 x Z x C7
++            sage: TestSuite(G).run()
++        """
++        assert isinstance(names, (basestring, tuple))
++        assert isinstance(generator_orders, tuple)
++        assert all(isinstance(order,Integer) for order in generator_orders)
++        self._gens_orders = generator_orders
++        n = ZZ(len(generator_orders))
++        names = self.normalize_names(n, names)
++        self._assign_names(names)
++        AbelianGroupBase.__init__(self) # TODO: category=CommutativeGroups()
+ 
+-    def __call__(self, x):
++    def is_isomorphic(left, right):
+         """
+-        Create an element of this abelian group from `x`.
++        Check whether ``left`` and ``right`` are isomorphic
+         
++        INPUT:
++
++        - ``right`` -- anything.
++
++        OUTPUT:
++
++        Boolean. Whether ``left`` and ``right`` are isomorphic as abelian groups.
++
+         EXAMPLES::
+-        
+-            sage: F = AbelianGroup(10, [2]*10)
+-            sage: F(F.2)
+-            f2
+-            sage: F(1)
+-            1
+-        """
+-        if isinstance(x, AbelianGroupElement) and x.parent() is self: 
+-            return x
+-        return AbelianGroupElement(self, x)
+-                                
+-    def __contains__(self, x):
+-        """
+-        Return True if `x` is an element of this abelian group.
+-        
+-        EXAMPLES::
+-        
+-            sage: F = AbelianGroup(10,[2]*10)
+-            sage: F.2 * F.3 in F
+-            True
+-        """
+-        return isinstance(x, AbelianGroupElement) and x.parent() == self
+ 
+-    def __eq__(self, right):
+-        """
+-        Compare self and right.
+-        
+-        The ordering is the ordering induced by that on the invariant
+-        factors lists.
+-        
+-        EXAMPLES::
+-        
+             sage: G1 = AbelianGroup([2,3,4,5])
+             sage: G2 = AbelianGroup([2,3,4,5,1])
+-            sage: G1 < G2
+-            False
+-            sage: G1 > G2
+-            False
+-            sage: G1 == G2
++            sage: G1.is_isomorphic(G2)
++            True
++            sage: G1 == G2    # syntactic sugar
+             True
+         """
+         if not is_AbelianGroup(right):
+             return False
+-        if set(self.variable_names()) != set(right.variable_names()):
+-            return False
+-        svars = list(self.variable_names())
+-        sinvs = self.invariants()
+-        rvars = list(right.variable_names())
+-        rinvs = right.invariants()
+-        for i in xrange(len(svars)):
+-            if rinvs[rvars.index(svars[i])] != sinvs[i]:
++        return left.elementary_divisors() == right.elementary_divisors()
++
++    __eq__ = is_isomorphic
++
++    def __ne__(left, right):
++        """
++        Check whether ``left`` and ``right`` are not isomorphic
++
++        OUTPUT:
++
++        Boolean.
++
++        EXAMPLES::
++
++            sage: G1 = AbelianGroup([2,3,4,5])
++            sage: G2 = AbelianGroup([2,3,4,5,1])
++            sage: G1 != G2
++            False
++            sage: G1.__ne__(G2)
++            False
++        """
++        return not left.is_isomorphic(right)
++
++    def is_subgroup(left, right):
++        """
++        Test whether ``left`` is a subgroup of ``right``.
++
++        EXAMPLES::
++
++            sage: G = AbelianGroup([2,3,4,5])
++            sage: G.is_subgroup(G)
++            True
++
++            sage: H = G.subgroup([G.1])
++            sage: H.is_subgroup(G)
++            True
++
++            sage: G.<a, b> = AbelianGroup(2)
++            sage: H.<c> = AbelianGroup(1)
++            sage: H < G
++            False
++       """
++        for l in left.gens():
++            if l not in right:
+                 return False
+         return True
++
++    __le__ = is_subgroup
++
++    def __ge__(left, right):
++        """
++        Test whether ``right`` is a subgroup of ``left``
++
++        EXAMPLE::
++
++            sage: G.<a, b> = AbelianGroup(2)
++            sage: H.<c> = AbelianGroup(1)
++            sage: G >= H
++            False
++        """
++        return right.__le__(left)
+     
+-    def __ne__(self, right):
+-        return not self == right
+-    
+-    def __ge__(self, right):
+-        for a in right.gens():
+-            if a not in self:
+-                return False
+-        return True
+-    
+-    def __lt__(self, right):
++    def __lt__(left, right):
+         """
++        Test whether ``left`` is a strict subgroup of ``right``
++
+         EXAMPLE::
+         
+             sage: G.<a, b> = AbelianGroup(2)
+@@ -514,16 +631,35 @@
+             sage: H < G
+             False
+         """
+-        return self <= right and self != right
++        return left <= right and left != right
+     
+-    def __gt__(self, right):
+-        return self >= right and self != right
++    def __gt__(left, right):
++        """
++        Test whether ``right`` is a strict subgroup of ``left``
++
++        EXAMPLE::
++
++            sage: G.<a, b> = AbelianGroup(2)
++            sage: H.<c> = AbelianGroup(1)
++            sage: G > H
++            False
++        """
++        return left >= right and left != right
+     
+-    def __le__(self, right):
+-        for a in self.gens():
+-            if a not in right:
+-                return False
+-        return True
++    def is_trivial(self):
++        """
++        Return whether the group is trivial
++
++        A group is trivial if it has precisely one element.
++
++        EXAMPLES::
++
++            sage: AbelianGroup([2, 3]).is_trivial()
++            False
++            sage: AbelianGroup([1, 1]).is_trivial()
++            True
++        """
++        return self.elementary_divisors() == ()
+ 
+     def __nonzero__(self):
+         """
+@@ -536,18 +672,62 @@
+             True
+             sage: bool(AbelianGroup([]))
+             False
++            sage: bool(AbelianGroup([1,1,1]))
++            False
+         """
+-        return len(self.invariants()) != 0
++        return not self.is_trivial()
+ 
+-    def dual_group(self):
++    @cached_method
++    def dual_group(self, names="X", base_ring=None):
+         """
+         Returns the dual group.
++
++        INPUT:
++
++        - ``names`` -- string or list of strings. The generator names
++          for the dual group.
++
++        - ``base_ring`` -- the base ring. If ``None`` (default), then
++          a suitable cyclotomic field is picked automatically.
+         
+-        EXAMPLES:
++        OUTPUT:
++
++        The
++        :class:`~sage.groups.abelian_gps.dual_abelian_group.DualAbelianGroup_class
++        <dual abelian group>`
++
++        EXAMPLES::
++
++            sage: G = AbelianGroup([2])
++            sage: G.dual_group()
++            Dual of Abelian Group isomorphic to Z/2Z over Cyclotomic Field of order 2 and degree 1
++            sage: G.dual_group().gens()
++            (X,)
++            sage: G.dual_group(names='Z').gens()
++            (Z,)
++
++            sage: G.dual_group(base_ring=QQ)
++            Dual of Abelian Group isomorphic to Z/2Z over Rational Field
++
++
++        TESTS::
++
++            sage: H = AbelianGroup(1)
++            sage: H.dual_group()
++            Traceback (most recent call last):
++            ...
++            ValueError: the group must be finite
+         """
+-        from sage.groups.abelian_gps.dual_abelian_group import DualAbelianGroup
+-        return DualAbelianGroup(self)
++        from sage.groups.abelian_gps.dual_abelian_group import DualAbelianGroup_class
++        if not self.is_finite():
++            raise ValueError('the group must be finite')
++        if base_ring is None:
++            from sage.rings.number_field.number_field import CyclotomicField
++            from sage.rings.arith import LCM
++            base_ring = CyclotomicField(LCM(self.gens_orders()))
++        return DualAbelianGroup_class(self, names=names, base_ring=base_ring)
+                   
++    @cached_method
+     def elementary_divisors(self):
+         r"""
+         This returns the elementary divisors of the group, using Pari.
+@@ -566,34 +746,39 @@
+             on these ""smaller invariants"" to compute `d_{i-1}`, and so on.
+             (Thanks to Robert Miller for communicating this algorithm.)
+ 
++        OUTPUT:
++
++        A tuple of integers.
++
+         EXAMPLES::
+         
+             sage: G = AbelianGroup(2,[2,3])
+             sage: G.elementary_divisors()
+-            [6]
++            (6,)
+             sage: G = AbelianGroup(1, [6])
+             sage: G.elementary_divisors()
+-            [6]
++            (6,)
+             sage: G = AbelianGroup(2,[2,6])
+             sage: G
+-            Multiplicative Abelian Group isomorphic to C2 x C6
+-            sage: G.invariants()
+-            [2, 6]
++            Multiplicative Abelian group isomorphic to C2 x C6
++            sage: G.gens_orders()
++            (2, 6)
+             sage: G.elementary_divisors()
+-            [2, 6]
++            (2, 6)
+             sage: J = AbelianGroup([1,3,5,12])
+             sage: J.elementary_divisors()
+-            [3, 60]
++            (3, 60)
+             sage: G = AbelianGroup(2,[0,6])
+             sage: G.elementary_divisors()
+-            [6, 0]
++            (6, 0)
+             sage: AbelianGroup([3,4,5]).elementary_divisors()
+-            [60]
++            (60,)
+         """
+         from sage.matrix.constructor import diagonal_matrix
+-        ed = diagonal_matrix(ZZ, self.invariants()).elementary_divisors()
+-        return [d for d in ed if d!=1]
++        ed = diagonal_matrix(ZZ, self.gens_orders()).elementary_divisors()
++        return tuple(d for d in ed if d!=1)
+ 
++    @cached_method
+     def exponent(self):
+         """
+         Return the exponent of this abelian group.
+@@ -601,20 +786,15 @@
+         EXAMPLES::
+         
+             sage: G = AbelianGroup([2,3,7]); G
+-            Multiplicative Abelian Group isomorphic to C2 x C3 x C7
++            Multiplicative Abelian group isomorphic to C2 x C3 x C7
+             sage: G.exponent()
+             42
+             sage: G = AbelianGroup([2,4,6]); G
+-            Multiplicative Abelian Group isomorphic to C2 x C4 x C6
++            Multiplicative Abelian group isomorphic to C2 x C4 x C6
+             sage: G.exponent()
+             12
+         """
+-        try:
+-            return self.__exponent
+-        except AttributeError:
+-            self.__exponent = e = lcm(self.invariants())
+-            return e
+-            
++        return lcm(self.gens_orders())
+     
+     def identity(self):
+         r"""
+@@ -633,9 +813,25 @@
+             f0
+         """
+         return self(1)
+-
+     
+     def _group_notation(self, eldv):
++        """
++        Return abstract group notation for generator orders ``eldv``
++
++        INPUT:
++
++        - ``eldv`` -- iterable of integers.
++
++        OUTPUT:
++
++        String.
++
++        EXAMPLES::
++
++            sage: G = AbelianGroup([2,2])
++            sage: G._group_notation([0,1,2,3])
++            'Z x C1 x C2 x C3'
++        """
+         v = []
+         for x in eldv:
+             if x:
+@@ -652,9 +848,9 @@
+         
+             sage: F = AbelianGroup(10, [2]*10)
+             sage: F._latex_()
+-            '$\\mathrm{AbelianGroup}( 10, [2, 2, 2, 2, 2, 2, 2, 2, 2, 2] )$'
++            '$\\mathrm{AbelianGroup}( 10, (2, 2, 2, 2, 2, 2, 2, 2, 2, 2) )$'
+         """
+-        s = "$\mathrm{AbelianGroup}( %s, %s )$"%(len(self.invariants()), self.invariants())
++        s = "$\mathrm{AbelianGroup}( %s, %s )$"%(self.ngens(), self.gens_orders())
+         return s
+ 
+     def _gap_init_(self):
+@@ -669,12 +865,10 @@
+             sage: gap(G)
+             Group( [ f1, f2, f3 ] )
+         
+-        Only works for finite groups.
+-        
+-        ::
++        Only works for finite groups::
+         
+             sage: G = AbelianGroup(3,[0,3,4],names="abc"); G
+-            Multiplicative Abelian Group isomorphic to Z x C3 x C4
++            Multiplicative Abelian group isomorphic to Z x C3 x C4
+             sage: G._gap_init_()
+             Traceback (most recent call last):
+             ...
+@@ -683,11 +877,10 @@
+         # TODO: Use the package polycyclic has AbelianPcpGroup, which can handle
+         # the infinite case but it is a GAP package not GPL'd.
+         # Use this when the group is infinite...
+-        if (False and prod(self.invariants())==0):   # if False for now...
+-            return 'AbelianPcpGroup(%s)'%self.invariants()
++        # return 'AbelianPcpGroup(%s)'%list(self.invariants())
+         if not self.is_finite():
+-            raise TypeError, "abelian groups in GAP are finite, but self is infinite"
+-        return 'AbelianGroup(%s)'%self.invariants()
++            raise TypeError('abelian groups in GAP are finite, but self is infinite')
++        return 'AbelianGroup(%s)'%list(self.gens_orders())
+ 
+     def gen(self, i=0):
+         """
+@@ -700,36 +893,129 @@
+             a0
+             sage: F.2
+             a2
+-            sage: F.invariants()
+-            [0, 0, 0, 0, 0]
++            sage: F.gens_orders()
++            (0, 0, 0, 0, 0)
++
++            sage: G = AbelianGroup([2,1,3])
++            sage: G.gens()
++            (f0, 1, f2)
+         """
+-        n = self.__ngens
++        n = self.ngens()
+         if i < 0 or i >= n: 
+             raise IndexError, "Argument i (= %s) must be between 0 and %s."%(i, n-1)
+-        x = [0]*int(n)
+-        x[int(i)] = 1 
+-        return AbelianGroupElement(self, x)
++        x = [0]*n
++        if self._gens_orders[i] != 1:
++            x[i] = 1
++        return self.element_class(x, self)
++
++    def gens(self):
++        """
++        Return the generators of the group.
++
++        OUTPUT:
++
++        A tuple of group elements. The generators according to the
++        chosen :meth:`gens_orders`.
++
++        EXAMPLES::
++
++            sage: F = AbelianGroup(5,[3,2],names='abcde')
++            sage: F.gens()
++            (a, b, c, d, e)
++            sage: [ g.order() for g in F.gens() ]
++            [+Infinity, +Infinity, +Infinity, 3, 2]
++        """
++        return tuple( self.gen(i) for i in range(self.ngens()) )
++
++    def gens_orders(self):
++        """
++        Return the orders of the cyclic factors that this group has
++        been defined with.
++
++        Use :meth:`elementary_divisors` if you are looking for an
++        invariant of the group.
++
++        OUTPUT:
++
++        A tuple of integers.
++
++        EXAMPLES::
++
++            sage: Z2xZ3 = AbelianGroup([2,3])
++            sage: Z2xZ3.gens_orders()
++            (2, 3)
++            sage: Z2xZ3.elementary_divisors()
++            (6,)
++
++            sage: Z6 = AbelianGroup([6])
++            sage: Z6.gens_orders()
++            (6,)
++            sage: Z6.elementary_divisors()
++            (6,)
++
++            sage: Z2xZ3.is_isomorphic(Z6)
++            True
++            sage: Z2xZ3 is Z6
++            False
++
++        TESTS::
++
++            sage: F = AbelianGroup(3, [2], names='abc')
++            sage: map(type, F.gens_orders())
++            [<type 'sage.rings.integer.Integer'>,
++             <type 'sage.rings.integer.Integer'>,
++             <type 'sage.rings.integer.Integer'>]
++        """
++        return self._gens_orders
+ 
+     def invariants(self):
+         """
+-        Return a copy of the list of invariants of this group.
+-        
+-        It is safe to modify the returned list.
++        Return the orders of the cyclic factors that this group has
++        been defined with.
++
++        For historical reasons this has been called invariants in
++        Sage, even though they are not necessarily the invariant
++        factors of the group. Use :meth:`gens_orders` instead::
++
++            sage: J = AbelianGroup([2,0,3,2,4]);  J
++            Multiplicative Abelian group isomorphic to C2 x Z x C3 x C2 x C4
++            sage: J.invariants()    # deprecated
++            (2, 0, 3, 2, 4)
++            sage: J.gens_orders()   # use this instead
++            (2, 0, 3, 2, 4)
++            sage: for i in range(J.ngens()):
++            ...       print i, J.gen(i), J.gen(i).order()     # or use this
++            0 f0 2
++            1 f1 +Infinity
++            2 f2 3
++            3 f3 2
++            4 f4 4
++
++        Use :meth:`elementary_divisors` if you are looking for an
++        invariant of the group.
++
++        OUTPUT:
++
++        A tuple of integers. Zero means infinite cyclic factor.
+         
+         EXAMPLES::
+         
+             sage: J = AbelianGroup([2,3])
+             sage: J.invariants()
+-            [2, 3]
+-            sage: v = J.invariants(); v
+-            [2, 3]
+-            sage: v[0] = 5
+-            sage: J.invariants()
+-            [2, 3]
+-            sage: J.invariants() is J.invariants()
+-            False
++            (2, 3)
++            sage: J.elementary_divisors()
++            (6,)
++
++        TESTS::
++
++            sage: F = AbelianGroup(3, [2], names='abc')
++            sage: map(type, F.gens_orders())
++            [<type 'sage.rings.integer.Integer'>,
++             <type 'sage.rings.integer.Integer'>,
++             <type 'sage.rings.integer.Integer'>]
+         """
+-        return list(self.__invariants)
++        # TODO: deprecate
++        return self.gens_orders()
+ 
+     def is_cyclic(self):
+         """
+@@ -738,25 +1024,25 @@
+         EXAMPLES::
+         
+             sage: J = AbelianGroup([2,3])
+-            sage: J.invariants()
+-            [2, 3]
++            sage: J.gens_orders()
++            (2, 3)
+             sage: J.elementary_divisors()
+-            [6]
++            (6,)
+             sage: J.is_cyclic()
+             True
+             sage: G = AbelianGroup([6])
+-            sage: G.invariants()
+-            [6]
++            sage: G.gens_orders()
++            (6,)
+             sage: G.is_cyclic()
+             True
+             sage: H = AbelianGroup([2,2])
+-            sage: H.invariants()
+-            [2, 2]
++            sage: H.gens_orders()
++            (2, 2)
+             sage: H.is_cyclic()
+             False
+             sage: H = AbelianGroup([2,4])
+             sage: H.elementary_divisors()
+-            [2, 4]
++            (2, 4)
+             sage: H.is_cyclic()
+             False
+             sage: H.permutation_group().is_cyclic()
+@@ -765,7 +1051,7 @@
+             sage: T.is_cyclic()
+             True            
+             sage: T = AbelianGroup(1,[0]); T
+-            Multiplicative Abelian Group isomorphic to Z
++            Multiplicative Abelian group isomorphic to Z
+             sage: T.is_cyclic()
+             True
+             sage: B = AbelianGroup([3,4,5])
+@@ -774,6 +1060,7 @@
+         """
+         return len(self.elementary_divisors()) <= 1
+ 
++    @cached_method
+     def ngens(self):
+         """
+         The number of free generators of the abelian group.
+@@ -784,8 +1071,9 @@
+             sage: F.ngens()
+             10000
+         """
+-        return self.__ngens
++        return len(self.gens_orders())
+ 
++    @cached_method
+     def order(self):
+         """
+         Return the order of this group.
+@@ -799,18 +1087,12 @@
+             sage: G.order()
+             +Infinity
+         """
+-        try:
+-            return self.__len
+-        except AttributeError:
+-            from sage.rings.all import infinity, Integer
+-            if len(self.invariants()) < self.ngens():
+-                self.__len = infinity
+-            else:
+-                self.__len = Integer(prod(self.invariants()))
+-                if self.__len == 0:
+-                    self.__len = infinity
+-        return self.__len
+-    
++        from sage.rings.all import infinity
++        length = prod(self.gens_orders())
++        if length == 0:
++            return infinity
++        return length
++
+     def permutation_group(self):
+         r"""
+         Return the permutation group isomorphic to this abelian group.
+@@ -822,7 +1104,7 @@
+         EXAMPLES::
+         
+             sage: G = AbelianGroup(2,[2,3]); G
+-            Multiplicative Abelian Group isomorphic to C2 x C3
++            Multiplicative Abelian group isomorphic to C2 x C3
+             sage: G.permutation_group()
+             Permutation Group with generators [(3,4,5), (1,2)]
+         """
+@@ -830,7 +1112,6 @@
+         s = 'Image(IsomorphismPermGroup(%s))'%self._gap_init_()
+         return PermutationGroup(gap_group=s)
+ 
+-
+     def is_commutative(self):
+         """
+         Return True since this group is commutative.
+@@ -847,32 +1128,38 @@
+ 
+     def random_element(self):
+         """
+-        Return a random element of this group. (Renamed random to
+-        random_element.)
++        Return a random element of this group.
+         
+         EXAMPLES::
+         
+             sage: G = AbelianGroup([2,3,9])
+             sage: G.random_element()
+-            f0*f1^2*f2
++            f1^2
+         """
+         from sage.misc.prandom import randint
+-        if self.order() is infinity:
+-            NotImplementedError, "The group must be finite"
+-        gens = self.gens()
+-        g = gens[0]**0
+-        for i in range(len(gens)):
+-            g = g*gens[i]**(randint(1,gens[i].order()))
+-        return g
+-
++        result = self.one()
++        for g in self.gens():
++            order = g.order()
++            if order not in ZZ:
++                order = 42  # infinite order; randomly chosen maximum
++            result *= g**(randint(0,order))
++        return result
+ 
+     def _repr_(self):
+-        eldv = self.invariants()
++        """
++        Return a string representation of ``self``.
++
++        EXAMPLES::
++
++            sage: G = AbelianGroup([2,3,9])
++            sage: G._repr_()
++            'Multiplicative Abelian group isomorphic to C2 x C3 x C9'
++       """
++        eldv = self.gens_orders()
+         if len(eldv) == 0:
+-            return "Trivial Abelian Group"
++            return "Trivial Abelian group"
+         g = self._group_notation(eldv)
+-        return "Multiplicative Abelian Group isomorphic to " + g
+-
++        return "Multiplicative Abelian group isomorphic to " + g
+ 
+     def subgroup(self, gensH, names="f"):
+         """
+@@ -881,74 +1168,59 @@
+          
+          INPUT:
+          
+-         
+-         -  ``gensH`` - list of elements which are products of
+-            the generators of the ambient abelian group G = self
+-         
++         - ``gensH`` -- list of elements which are products of the
++            generators of the ambient abelian group G = self
+          
+          EXAMPLES::
+          
+              sage: G.<a,b,c> = AbelianGroup(3, [2,3,4]); G
+-             Multiplicative Abelian Group isomorphic to C2 x C3 x C4
++             Multiplicative Abelian group isomorphic to C2 x C3 x C4
+              sage: H = G.subgroup([a*b,a]); H
+-             Multiplicative Abelian Group isomorphic to C2 x C3, which is the subgroup of
+-             Multiplicative Abelian Group isomorphic to C2 x C3 x C4
+-             generated by [a*b, a]
++             Multiplicative Abelian subgroup isomorphic to C2 x C3 generated by {a*b, a}
+              sage: H < G
+              True
+              sage: F = G.subgroup([a,b^2])
+              sage: F
+-             Multiplicative Abelian Group isomorphic to C2 x C3, which is the subgroup of
+-             Multiplicative Abelian Group isomorphic to C2 x C3 x C4
+-             generated by [a, b^2]
++             Multiplicative Abelian subgroup isomorphic to C2 x C3 generated by {a, b^2}
+              sage: F.gens()
+-             [a, b^2]
++             (a, b^2)
+              sage: F = AbelianGroup(5,[30,64,729],names = list("abcde"))
+              sage: a,b,c,d,e = F.gens()
+              sage: F.subgroup([a,b])
+-             Multiplicative Abelian Group isomorphic to Z x Z, which is
+-             the subgroup of Multiplicative Abelian Group isomorphic to
+-             Z x Z x C30 x C64 x C729 generated by [a, b]
++             Multiplicative Abelian subgroup isomorphic to Z x Z generated by {a, b}
+              sage: F.subgroup([c,e])
+-             Multiplicative Abelian Group isomorphic to C2 x C3 x C5 x
+-             C729, which is the subgroup of Multiplicative Abelian
+-             Group isomorphic to Z x Z x C30 x C64 x C729 generated by
+-             [c, e]
++             Multiplicative Abelian subgroup isomorphic to C2 x C3 x C5 x C729 generated by {c, e}
+          """
+-        if not isinstance(gensH, (list, tuple)):
+-            raise TypeError, "gensH = (%s) must be a list or tuple"%(gensH)
+-         
+         G = self
+-        for i in range(len(gensH)):
+-            if not(gensH[i] in G):
+-                raise TypeError, "Subgroup generators must belong to the given group."
++        gensH = tuple(gensH)
++        if isinstance(names, list):
++            names = tuple(names)
++        for h in gensH:
++            if h not in G:
++                raise TypeError('Subgroup generators must belong to the given group.')
+         return AbelianGroup_subgroup(self, gensH, names)
+ 
++    @cached_method
+     def list(self):
+         """
+-        Return list of all elements of this group.
++        Return tuple of all elements of this group.
+         
+         EXAMPLES::
+         
+             sage: G = AbelianGroup([2,3], names = "ab")
+             sage: G.list()
+-            [1, b, b^2, a, a*b, a*b^2]
++            (1, b, b^2, a, a*b, a*b^2)
+         
+         ::
+         
+             sage: G = AbelianGroup([]); G
+-            Trivial Abelian Group
++            Trivial Abelian group
+             sage: G.list()
+-            [1]
++            (1,)
+         """
+-        try:
+-            return list(self.__list)
+-        except AttributeError:
+-            pass
+         if not(self.is_finite()):
+            raise NotImplementedError, "Group must be finite"
+-        self.__list = list(self.__iter__())
+-        return list(self.__list)
++        return tuple(self.__iter__())
+      
+     def __iter__(self):
+         """
+@@ -973,14 +1245,13 @@
+             [1]
+             sage: G = AbelianGroup([])
+             sage: G.list()
+-            [1]
++            (1,)
+             sage: list(G)
+             [1]
+         """
+-        invs = self.invariants()
++        invs = self.gens_orders()
+         for t in mrange(invs):
+-            yield AbelianGroupElement(self, t)
+-
++            yield self(t)
+ 
+     def subgroups(self, check=False):
+         r"""
+@@ -1010,35 +1281,28 @@
+         EXAMPLES::
+ 
+             sage: AbelianGroup([2,3]).subgroups()
+-            [Multiplicative Abelian Group isomorphic to C2 x C3, which is the subgroup of
+-            Multiplicative Abelian Group isomorphic to C2 x C3
+-            generated by [f0*f1^2],
+-             Multiplicative Abelian Group isomorphic to C2, which is the subgroup of
+-            Multiplicative Abelian Group isomorphic to C2 x C3
+-            generated by [f0],
+-             Multiplicative Abelian Group isomorphic to C3, which is the subgroup of
+-            Multiplicative Abelian Group isomorphic to C2 x C3
+-            generated by [f1],
+-             Trivial Abelian Group, which is the subgroup of
+-            Multiplicative Abelian Group isomorphic to C2 x C3
+-            generated by []]
+-
++            [Multiplicative Abelian subgroup isomorphic to C2 x C3 generated by {f0*f1^2},
++             Multiplicative Abelian subgroup isomorphic to C2 generated by {f0},
++             Multiplicative Abelian subgroup isomorphic to C3 generated by {f1},
++             Trivial Abelian subgroup]
+             sage: len(AbelianGroup([2,4,8]).subgroups())
+             81
+ 
++        TESTS::
++
++            sage: AbelianGroup([]).subgroups()
++            [Trivial Abelian group]
+         """
+         if not self.is_finite(): raise ValueError, "Group must be finite"
+         from sage.misc.misc import verbose
+ 
+-        v = self.invariants()
+-        
+-        
+-        if len(v) <= 1:
+-            if v == [] or v[0] == 1:
+-                return [self]
+-            else:
+-                return [ self.subgroup([self.gen(0)**i]) for i in divisors(v[0])[:-1]] + [self.subgroup([])]
++        if self.is_trivial():
++            return [self]
++        if self.ngens() == 1:
++            n = self.gen(0).order()
++            return [ self.subgroup([self.gen(0)**i]) for i in divisors(n) ]
+ 
++        v = self.gens_orders()
+         A = AbelianGroup(v[:-1])
+         x = v[-1]
+ 
+@@ -1046,8 +1310,8 @@
+ 
+         subgps = []
+         for G in Wsubs:
+-            verbose("G = subgp generated by %s" % G.gens())
+-            verbose("invariants are:", [t.order() for t in G.gens()]) # G.invariants() doesn't work
++            verbose("G = subgp generated by %s" % list(G.gens()))
++            verbose("invariants are:", [t.order() for t in G.gens()])
+             for H in divisors(x):
+                 # H = the subgroup of *index* H.
+                 its = [xrange(0, H, H/gcd(H, G.gen(i).order())) for i in xrange(len(G.gens()))] 
+@@ -1085,13 +1349,11 @@
+ 
+             sage: G = AbelianGroup([4,4])
+             sage: G.subgroup( [ G([1,0]), G([1,2]) ])
+-            Multiplicative Abelian Group isomorphic to C2 x C4, which is the subgroup of
+-            Multiplicative Abelian Group isomorphic to C4 x C4
+-            generated by [f0, f0*f1^2]
++            Multiplicative Abelian subgroup isomorphic to C2 x C4
++            generated by {f0, f0*f1^2}
+             sage: AbelianGroup([4,4]).subgroup_reduced( [ [1,0], [1,2] ])
+-            Multiplicative Abelian Group isomorphic to C2 x C4, which is the subgroup of
+-            Multiplicative Abelian Group isomorphic to C4 x C4
+-            generated by [f1^2, f0]
++            Multiplicative Abelian subgroup isomorphic to C2 x C4
++            generated by {f1^2, f0}
+         """
+         from sage.matrix.constructor import matrix
+         d = self.ngens()
+@@ -1102,12 +1364,13 @@
+             # can't happen?
+             print "Vectors not LI: ", elts
+             raise e
+-        rel_lattice = X.span([X.gen(i) * self.invariants()[i] for i in xrange(d)])
++        rel_lattice = X.span([X.gen(i) * self.gens_orders()[i] for i in xrange(d)])
+         isect = elt_lattice.intersection(rel_lattice)
+         mat = matrix([elt_lattice.coordinate_vector(x) for x in isect.gens()]).change_ring(ZZ)
+         D,U,V = mat.smith_form()
+         new_basis = [(elt_lattice.linear_combination_of_basis((~V).row(i)).list(), D[i,i]) for i in xrange(U.ncols())]
+-        return self.subgroup([self([x[0][i] % self.invariants()[i] for i in xrange(d)]) for x in new_basis if x[1] != 1])
++        return self.subgroup([self([x[0][i] % self.gens_orders()[i]
++                                    for i in xrange(d)]) for x in new_basis if x[1] != 1])
+ 
+ class AbelianGroup_subgroup(AbelianGroup_class):
+     """
+@@ -1126,61 +1389,21 @@
+             sage: F = AbelianGroup(5,[30,64,729],names = list("abcde"))
+             sage: a,b,c,d,e = F.gens()
+             sage: F.subgroup([a^3,b])
+-            Multiplicative Abelian Group isomorphic to Z x Z, which is the subgroup of
+-            Multiplicative Abelian Group isomorphic to Z x Z x C30 x C64 x C729
+-            generated by [a^3, b]            
+-        
+-        ::
+-        
++            Multiplicative Abelian subgroup isomorphic to Z x Z generated by {a^3, b}
+             sage: F.subgroup([c])
+-            Multiplicative Abelian Group isomorphic to C2 x C3 x C5, which is the subgroup of
+-            Multiplicative Abelian Group isomorphic to Z x Z x C30 x C64 x C729
+-            generated by [c]
+-        
+-        ::
+-        
+-            sage: F.subgroup([a,c])
+-            Multiplicative Abelian Group isomorphic to C2 x C3 x C5 x
+-            Z, which is the subgroup of Multiplicative Abelian Group
+-            isomorphic to Z x Z x C30 x C64 x C729 generated by [a, c]
+-        
+-        ::
+-        
+-            sage: F.subgroup([a,b*c])
+-            Multiplicative Abelian Group isomorphic to Z x Z, which is
+-            the subgroup of Multiplicative Abelian Group isomorphic to
+-            Z x Z x C30 x C64 x C729 generated by [a, b*c]
+-        
+-        ::
+-        
+-            sage: F.subgroup([b*c,d])
+-            Multiplicative Abelian Group isomorphic to C64 x Z, which
+-            is the subgroup of Multiplicative Abelian Group isomorphic
+-            to Z x Z x C30 x C64 x C729 generated by [b*c, d]
+-        
+-        ::
+-        
+-            sage: F.subgroup([a*b,c^6,d],names = list("xyz"))
+-            Multiplicative Abelian Group isomorphic to C5 x C64 x Z,
+-            which is the subgroup of Multiplicative Abelian Group
+-            isomorphic to Z x Z x C30 x C64 x C729 generated by [a*b,
+-            c^6, d]
+-        
+-        ::
+-        
++            Multiplicative Abelian subgroup isomorphic to C2 x C3 x C5 generated by {c}
++            sage: F.subgroup([a, c])
++            Multiplicative Abelian subgroup isomorphic to C2 x C3 x C5 x Z generated by {a, c}
++            sage: F.subgroup([a, b*c])
++            Multiplicative Abelian subgroup isomorphic to Z x Z generated by {a, b*c}
++            sage: F.subgroup([b*c, d])
++            Multiplicative Abelian subgroup isomorphic to C64 x Z generated by {b*c, d}
++            sage: F.subgroup([a*b, c^6, d],names=list("xyz"))
++            Multiplicative Abelian subgroup isomorphic to C5 x C64 x Z generated by {a*b, c^6, d}
+             sage: H.<x,y,z> = F.subgroup([a*b, c^6, d]); H
+-            Multiplicative Abelian Group isomorphic to C5 x C64 x Z,
+-            which is the subgroup of Multiplicative Abelian Group
+-            isomorphic to Z x Z x C30 x C64 x C729 generated by [a*b,
+-            c^6, d]
+-        
+-        ::
+-        
+-            sage: G = F.subgroup([a*b,c^6,d],names = list("xyz")); G
+-            Multiplicative Abelian Group isomorphic to C5 x C64 x Z,
+-            which is the subgroup of Multiplicative Abelian Group
+-            isomorphic to Z x Z x C30 x C64 x C729 generated by [a*b,
+-            c^6, d]
++            Multiplicative Abelian subgroup isomorphic to C5 x C64 x Z generated by {a*b, c^6, d}
++            sage: G = F.subgroup([a*b, c^6, d],names = list("xyz")); G
++            Multiplicative Abelian subgroup isomorphic to C5 x C64 x Z generated by {a*b, c^6, d}
+             sage: x,y,z = G.gens()
+             sage: x.order()
+             +Infinity
+@@ -1191,28 +1414,20 @@
+             sage: A = AbelianGroup(5,[3, 5, 5, 7, 8], names = "abcde")
+             sage: a,b,c,d,e = A.gens()
+             sage: A.subgroup([a,b])
+-            Multiplicative Abelian Group isomorphic to C3 x C5, which is the subgroup of
+-            Multiplicative Abelian Group isomorphic to C3 x C5 x C5 x C7 x C8
+-            generated by [a, b]
++            Multiplicative Abelian subgroup isomorphic to C3 x C5 generated by {a, b}
+             sage: A.subgroup([a,b,c,d^2,e])
+-            Multiplicative Abelian Group isomorphic to C3 x C5 x C5 x C7 x C8, which is the subgroup of
+-            Multiplicative Abelian Group isomorphic to C3 x C5 x C5 x C7 x C8
+-            generated by [a, b, c, d^2, e]
+-            sage: A.subgroup([a,b,c,d^2,e^2])
+-            Multiplicative Abelian Group isomorphic to C3 x C4 x C5 x C5 x C7, which is the subgroup of
+-            Multiplicative Abelian Group isomorphic to C3 x C5 x C5 x C7 x C8
+-            generated by [a, b, c, d^2, e^2]
+-            sage: B = A.subgroup([a^3,b,c,d,e^2]); B
+-            Multiplicative Abelian Group isomorphic to C4 x C5 x C5 x C7, which is the subgroup of
+-            Multiplicative Abelian Group isomorphic to C3 x C5 x C5 x C7 x C8
+-            generated by [b, c, d, e^2]
+-            sage: B.invariants()
+-            [4, 5, 5, 7]
++            Multiplicative Abelian subgroup isomorphic to C3 x C5 x C5 x C7 x C8 generated by {a, b, c, d^2, e}
++            sage: A.subgroup([a, b, c, d^2, e^2])
++            Multiplicative Abelian subgroup isomorphic to C3 x C4 x C5 x C5 x C7 generated by {a, b, c, d^2, e^2}
++            sage: B = A.subgroup([a^3, b, c, d, e^2]); B
++            Multiplicative Abelian subgroup isomorphic to C4 x C5 x C5 x C7 generated by {b, c, d, e^2}
++            sage: B.gens_orders()
++            (4, 5, 5, 7)
+             sage: A = AbelianGroup(4,[1009, 2003, 3001, 4001], names = "abcd")
+             sage: a,b,c,d = A.gens()
+             sage: B = A.subgroup([a^3,b,c,d])
+-            sage: B.invariants()
+-            [1009, 2003, 3001, 4001]
++            sage: B.gens_orders()
++            (1009, 2003, 3001, 4001)
+             sage: A.order()
+             24266473210027
+             sage: B.order()
+@@ -1220,41 +1435,35 @@
+             sage: A = AbelianGroup(4,[1008, 2003, 3001, 4001], names = "abcd")
+             sage: a,b,c,d = A.gens()
+             sage: B = A.subgroup([a^3,b,c,d]); B
+-            Multiplicative Abelian Group isomorphic to C3 x C7 x C16 x
+-            C2003 x C3001 x C4001, which is the subgroup of
+-            Multiplicative Abelian Group isomorphic to C1008 x C2003 x
+-            C3001 x C4001 generated by [a^3, b, c, d]
++            Multiplicative Abelian subgroup isomorphic
++            to C3 x C7 x C16 x C2003 x C3001 x C4001 generated by {a^3, b, c, d}
+         
+         Infinite groups can also be handled::
+         
+             sage: G = AbelianGroup([3,4,0], names = "abc")
+             sage: a,b,c = G.gens()
+-            sage: F = G.subgroup([a,b^2,c]); F
+-            Multiplicative Abelian Group isomorphic to C2 x C3 x Z,
+-            which is the subgroup of Multiplicative Abelian Group
+-            isomorphic to C3 x C4 x Z generated by [a, b^2, c]
++            sage: F = G.subgroup([a, b^2, c]); F
++            Multiplicative Abelian subgroup isomorphic to C2 x C3 x Z generated by {a, b^2, c}
+         
+-        ::
+-        
+-            sage: F.invariants()
+-            [2, 3, 0]
++            sage: F.gens_orders()
++            (2, 3, 0)
+             sage: F.gens()
+-            [a, b^2, c]
++            (a, b^2, c)
+             sage: F.order()
+             +Infinity
+         """
+         from sage.interfaces.all import gap
+         if not isinstance(ambient, AbelianGroup_class):
+             raise TypeError, "ambient (=%s) must be an abelian group."%ambient
+-        if not isinstance(gens, list):
+-            raise TypeError, "gens (=%s) must be a list"%gens
++        if not isinstance(gens, tuple):
++            raise TypeError, "gens (=%s) must be a tuple"%gens
+             
+-        self.__ambient_group = ambient
+-        Hgens = [x for x in gens if x != ambient(1)]  ## in case someone puts 1 in the list of generators
+-        self.__gens = Hgens
++        self._ambient_group = ambient
++        Hgens = tuple(x for x in gens if x != ambient.one())  ## in case someone puts 1 in the list of generators
++        self._gens = Hgens
+         m = len(gens)
+         ell = len(ambient.gens())
+-        ambient_invs = ambient.invariants()
++        ambient_invs = ambient.gens_orders()
+         invsf = [x for x in ambient_invs if x > 0]    ## fixes the problem with 
+         invs0 = [x for x in ambient_invs if x == 0]   ## the infinite parts
+         Ggens = list(ambient.variable_names())
+@@ -1264,7 +1473,7 @@
+             Ggens0 = [x for x in ambient.variable_names() if
+                         ambient_invs[Ggens.index(x)] == 0]
+             ##     ^^ only look at "finite" names 
+-            Gf = AbelianGroup_class(len(invsf), invsf, names = Gfgens)
++            Gf = AbelianGroup(invsf, names=Gfgens)
+             s1 = "G:= %s; gens := GeneratorsOfGroup(G)"%Gf._gap_init_()
+             gap.eval(s1)
+             Hgensf = [x for x in Hgens if len(set(Ggens0).intersection(set(list(str(x)))))==0]
+@@ -1274,35 +1483,35 @@
+             for i in range(len(Gfgens)):
+                cmd = ("%s := gens["+str(i+1)+"]")%Gfgens[i]
+                gap.eval(cmd)
+-        if invs0==[]:
+-           Hgensf = Hgens
+-           Hgens0 = []  # added for consistency
+-           G = ambient
+-           s1 = "G:= %s; gens := GeneratorsOfGroup(G)"%G._gap_init_()
+-           gap.eval(s1)
+-           for i in range(len(Ggens)):
+-               cmd = '%s := gens[%s]'%(Ggens[i], i+1)
+-               #print i,"  \n",cmd
+-               gap.eval(cmd)
+-        s2 = "gensH:=%s"%Hgensf #### remove from this the ones <--> 0 invar
++        else:  # invs0==[]:
++            Hgensf = Hgens
++            Hgens0 = []  # added for consistency
++            G = ambient
++            s1 = "G:= %s; gens := GeneratorsOfGroup(G)"%G._gap_init_()
++            gap.eval(s1)
++            for i in range(len(Ggens)):
++                cmd = '%s := gens[%s]'%(Ggens[i], i+1)
++                #print i,"  \n",cmd
++                gap.eval(cmd)
++        s2 = "gensH:=%s"%list(Hgensf) #### remove from this the ones <--> 0 invar
+         gap.eval(s2)
+         s3 = 'H:=Subgroup(G,gensH)'
+         gap.eval(s3)
+         # a GAP command which returns the "invariants" of the
+         # subgroup as an AbelianPcpGroup, RelativeOrdersOfPcp(Pcp(G)),
+         # works if G is the subgroup declared as a AbelianPcpGroup
+-        self.__abinvs = eval(gap.eval("AbelianInvariants(H)"))
+-        invs = self.__abinvs
++        self._abinvs = eval(gap.eval("AbelianInvariants(H)"))
++        invs = self._abinvs
+         #print s3, invs
+         if Hgens0 != []:
+             for x in Hgens0:
+                invs.append(0)
+-        #print Hgensf, invs, invs0
+-        AbelianGroup_class.__init__(self, len(invs), invs, names)
++        invs = tuple(ZZ(i) for i in invs)
++        AbelianGroup_class.__init__(self, invs, names)
+ 
+     def __contains__(self, x):
+         """
+-        Return True if `x` is an element of this subgroup.
++        Test whether ``x`` is an element of this subgroup.
+         
+         EXAMPLES::
+         
+@@ -1318,10 +1527,10 @@
+         if x.parent() is self:
+             return True
+         elif x in self.ambient_group():
+-            amb_inv = self.ambient_group().invariants()
++            amb_inv = self.ambient_group().gens_orders()
+             for a in xrange(len(amb_inv)):
+                 if amb_inv[a] == 0 and x.list()[a] != 0:
+-                    for g in self.__gens:
++                    for g in self._gens:
+                         if g.list()[a] == 0:
+                             continue
+                         if abs(x.list()[a]%g.list()[a]) < abs(x.list()[a]):
+@@ -1332,7 +1541,7 @@
+                         if x.list()[a] == 0:
+                             break
+                 elif x.list()[a] != 0:
+-                    for g in self.__gens:
++                    for g in self._gens:
+                         if g.list()[a] == 0:
+                             continue
+                         if abs(x.list()[a]%g.list()[a])%abs(amb_inv[a]) < x.list()[a]%abs(amb_inv[a]):
+@@ -1344,61 +1553,111 @@
+     def ambient_group(self):
+         """
+         Return the ambient group related to self.
++
++        OUTPUT:
++
++        A multiplicative Abelian group.
++
++        EXAMPLES::
++
++            sage: G.<a,b,c> = AbelianGroup([2,3,4])
++            sage: H = G.subgroup([a, b^2])
++            sage: H.ambient_group() is G
++            True
+         """
+-        return self.__ambient_group
++        return self._ambient_group
+ 
+-    def __eq__(self, right):
++    def equals(left, right):
+         """
+-        ::
++        Check whether ``left`` and ``right`` are the same (sub)group.
++
++        INPUT:
++
++        - ``right`` -- anything.
++
++        OUTPUT:
++
++        Boolean. If ``right`` is a subgroup, test whether ``left`` and
++        ``right`` are the same subset of the ambient group. If
++        ``right`` is not a subgroup, test whether they are isomorphic
++        groups, see :meth:`~AbelianGroup_class.is_isomorphic`.
++
++        EXAMPLES::
+         
+             sage: G = AbelianGroup(3, [2,3,4], names="abc"); G
+-            Multiplicative Abelian Group isomorphic to C2 x C3 x C4
++            Multiplicative Abelian group isomorphic to C2 x C3 x C4
+             sage: a,b,c = G.gens()
+-            sage: F=G.subgroup([a,b^2]); F
+-            Multiplicative Abelian Group isomorphic to C2 x C3, which is the subgroup of
+-            Multiplicative Abelian Group isomorphic to C2 x C3 x C4
+-            generated by [a, b^2]
++            sage: F = G.subgroup([a,b^2]); F
++            Multiplicative Abelian subgroup isomorphic to C2 x C3 generated by {a, b^2}
+             sage: F<G
+             True
+         
+-        ::
+-        
+             sage: A = AbelianGroup(1, [6])
+             sage: A.subgroup(list(A.gens())) == A
+             True
+         
+-        ::
+-        
+             sage: G.<a,b> = AbelianGroup(2)
+             sage: A = G.subgroup([a])
+             sage: B = G.subgroup([b])
+-            sage: A == B
++            sage: A.equals(B)
+             False
++            sage: A == B        # sames as A.equals(B)
++            False
++            sage: A.is_isomorphic(B)
++            True
+         """
+-        if not is_AbelianGroup(right):
+-            return -1
+-        return self <= right and right <= self
+-    
++        left_ambient = left.ambient_group()
++        try:
++            right_ambient = right.ambient_group()
++        except AttributeError:
++            # right is not a subgroup
++            return left.is_isomorphic(right)
++        if left_ambient is not right_ambient:
++            return False
++        return left <= right and right <= left
++
++    __eq__ = equals
++
+     def _repr_(self):
+-        return '%s, which is the subgroup of\n%s\ngenerated by %s'%(
+-            AbelianGroup_class._repr_(self),
+-            self.ambient_group(),
+-            self.gens())
++        """
++        Return a string representation
++
++        EXAMPLES::
++
++            sage: G.<a,b> = AbelianGroup(2)
++            sage: G._repr_()
++            'Multiplicative Abelian group isomorphic to Z x Z'
++            sage: A = G.subgroup([a])
++            sage: A._repr_()
++            'Multiplicative Abelian subgroup isomorphic to Z generated by {a}'
++       """