def multiplicative_subgroups(self): r""" Return generators for each subgroup of `(\ZZ/N\ZZ)^*`. EXAMPLES:: sage: Integers(5).multiplicative_subgroups() ((2,), (4,), ()) sage: Integers(15).multiplicative_subgroups() ((11, 7), (4, 11), (8,), (11,), (14,), (7,), (4,), ()) sage: Integers(2).multiplicative_subgroups() ((),) sage: len(Integers(341).multiplicative_subgroups()) 80 TESTS:: sage: IntegerModRing(1).multiplicative_subgroups() ((0,),) sage: IntegerModRing(2).multiplicative_subgroups() ((),) sage: IntegerModRing(3).multiplicative_subgroups() ((2,), ()) """ from sage.groups.abelian_gps.values import AbelianGroupWithValues U = self.unit_gens() G = AbelianGroupWithValues(U, [x.multiplicative_order() for x in U], values_group=self) mysubs = [] for Gsub in G.subgroups(): mysubs.append(tuple( g.value() for g in Gsub.gens() )) return tuple(mysubs)
def unit_group(self, algorithm='sage'): r""" Return the unit group of ``self``. INPUT: - ``self`` -- the ring `\ZZ/n\ZZ` for a positive integer `n` - ``algorithm`` -- either ``'sage'`` (default) or ``'pari'`` OUTPUT: The unit group of ``self``. This is a finite Abelian group equipped with a distinguished set of generators, which is computed using a deterministic algorithm depending on the ``algorithm`` parameter. - If ``algorithm == 'sage'``, the generators correspond to the prime factors `p \mid n` (one generator for each odd `p`; the number of generators for `p = 2` is 0, 1 or 2 depending on the order to which 2 divides `n`). - If ``algorithm == 'pari'``, the generators are chosen such that their orders form a decreasing sequence with respect to divisibility. EXAMPLES: The output of the algorithms ``'sage'`` and ``'pari'`` can differ in various ways. In the following example, the same cyclic factors are computed, but in a different order:: sage: A = Zmod(15) sage: G = A.unit_group(); G Multiplicative Abelian group isomorphic to C2 x C4 sage: G.gens_values() (11, 7) sage: H = A.unit_group(algorithm='pari'); H Multiplicative Abelian group isomorphic to C4 x C2 sage: H.gens_values() (7, 11) Here are two examples where the cyclic factors are isomorphic, but are ordered differently and have different generators:: sage: A = Zmod(40) sage: G = A.unit_group(); G Multiplicative Abelian group isomorphic to C2 x C2 x C4 sage: G.gens_values() (31, 21, 17) sage: H = A.unit_group(algorithm='pari'); H Multiplicative Abelian group isomorphic to C4 x C2 x C2 sage: H.gens_values() (17, 31, 21) sage: A = Zmod(192) sage: G = A.unit_group(); G Multiplicative Abelian group isomorphic to C2 x C16 x C2 sage: G.gens_values() (127, 133, 65) sage: H = A.unit_group(algorithm='pari'); H Multiplicative Abelian group isomorphic to C16 x C2 x C2 sage: H.gens_values() (133, 127, 65) In the following examples, the cyclic factors are not even isomorphic:: sage: A = Zmod(319) sage: A.unit_group() Multiplicative Abelian group isomorphic to C10 x C28 sage: A.unit_group(algorithm='pari') Multiplicative Abelian group isomorphic to C140 x C2 sage: A = Zmod(30.factorial()) sage: A.unit_group() Multiplicative Abelian group isomorphic to C2 x C16777216 x C3188646 x C62500 x C2058 x C110 x C156 x C16 x C18 x C22 x C28 sage: A.unit_group(algorithm='pari') Multiplicative Abelian group isomorphic to C20499647385305088000000 x C55440 x C12 x C12 x C4 x C2 x C2 x C2 x C2 x C2 x C2 TESTS: We test the cases where the unit group is trivial:: sage: A = Zmod(1) sage: A.unit_group() Trivial Abelian group sage: A.unit_group(algorithm='pari') Trivial Abelian group sage: A = Zmod(2) sage: A.unit_group() Trivial Abelian group sage: A.unit_group(algorithm='pari') Trivial Abelian group sage: Zmod(3).unit_group(algorithm='bogus') Traceback (most recent call last): ... ValueError: unknown algorithm 'bogus' for computing the unit group """ from sage.groups.abelian_gps.values import AbelianGroupWithValues if algorithm == 'sage': n = self.order() gens = [] orders = [] for p, r in self.factored_order(): m = n / (p**r) for g, o in _unit_gens_primepowercase(p, r): x = g.crt(integer_mod.Mod(1, m)) gens.append(x) orders.append(o) elif algorithm == 'pari': _, orders, gens = self.order()._pari_().znstar() gens = map(self, gens) orders = map(integer.Integer, orders) else: raise ValueError( 'unknown algorithm %r for computing the unit group' % algorithm) return AbelianGroupWithValues(gens, orders, values_group=self)