def __unit_gens_primecase(self, p):
        """
        Assuming the modulus is prime, returns the smallest generator
        of the group of units.

        EXAMPLES::

            sage: Zmod(17)._IntegerModRing_generic__unit_gens_primecase(17)
            3
        """
        if p == 2:
            return integer_mod.Mod(1, p)
        P = prime_divisors(p - 1)
        ord = integer.Integer(p - 1)
        one = integer_mod.Mod(1, p)
        x = 2
        while x < p:
            generator = True
            z = integer_mod.Mod(x, p)
            for q in P:
                if z**(ord // q) == one:
                    generator = False
                    break
            if generator:
                return z
            x += 1
        #end for
        assert False, "didn't find primitive root for p=%s" % p
    def __unit_gens_primepowercase(self, p, r):
        r"""
        Find smallest generator for
        `(\ZZ/p^r\ZZ)^*`.

        EXAMPLES::

            sage: Zmod(27)._IntegerModRing_generic__unit_gens_primepowercase(3,3)
            [2]
        """
        if r == 1:
            return [self.__unit_gens_primecase(p)]
        elif p == 2:
            if r == 1:
                return []
            elif r == 2:
                return [integer_mod.Mod(-1, 2**r)]
            elif r >= 3:
                pr = 2**r
                a = integer_mod.Mod(5, pr)
                return [integer_mod.Mod(-1, pr), a]
            assert False, "p=2, r=%s should be >=1" % r
        else:  # odd prime
            pr = p**r
            R = IntegerModRing(pr)
            x = R(self.__unit_gens_primecase(p).lift())
            n = p**(r - 2) * (p - 1)
            one = integer_mod.Mod(1, pr)
            for b in range(0, p):
                z = x + R(b * p)
                if z**n != one:
                    a = integer_mod.Mod(z, pr)
                    return [a]
            assert False, "p=%s, r=%s, couldn't find generator" % (p, r)
def _unit_gens_primepowercase(p, r):
    r"""
    Return a list of generators for `(\ZZ/p^r\ZZ)^*` and their orders.

    EXAMPLES::

        sage: from sage.rings.finite_rings.integer_mod_ring import _unit_gens_primepowercase
        sage: _unit_gens_primepowercase(2, 3)
        [(7, 2), (5, 2)]
        sage: _unit_gens_primepowercase(17, 1)
        [(3, 16)]
        sage: _unit_gens_primepowercase(3, 3)
        [(2, 18)]
    """
    pr = p**r
    if p == 2:
        if r == 1:
            return []
        if r == 2:
            return [(integer_mod.Mod(3, 4), integer.Integer(2))]
        return [(integer_mod.Mod(-1, pr), integer.Integer(2)),
                (integer_mod.Mod(5, pr), integer.Integer(2**(r - 2)))]

    # odd prime
    return [(integer_mod.Mod(primitive_root(pr, check=False),
                             pr), integer.Integer(p**(r - 1) * (p - 1)))]
Beispiel #4
0
 def __unit_gens_primecase(self, p):
     if p == 2:
         return integer_mod.Mod(1, p)
     P = prime_divisors(p - 1)
     ord = integer.Integer(p - 1)
     one = integer_mod.Mod(1, p)
     x = 2
     while x < p:
         generator = True
         z = integer_mod.Mod(x, p)
         for q in P:
             if z**(ord // q) == one:
                 generator = False
                 break
         if generator:
             return z
         x += 1
     #end for
     assert False, "didn't find primitive root for p=%s" % p
    def __unit_gens_primepowercase(self, p, r):
        r"""
        Find smallest generator for
        `(\ZZ/p^r\ZZ)^*`.

        EXAMPLES::

            sage: Zmod(27)._IntegerModRing_generic__unit_gens_primepowercase(3,3)
            [2]
        """
        if r == 1:
            return [self.__unit_gens_primecase(p)]

        if p == 2:
            if r < 1:
                raise ValueError("p=2, r={} should be >=1".format(r))
            if r == 1:
                return []
            if r == 2:
                return [integer_mod.Mod(-1, 2**r)]

            pr = 2**r
            a = integer_mod.Mod(5, pr)
            return [integer_mod.Mod(-1, pr), a]

        # odd prime
        pr = p**r
        R = IntegerModRing(pr)
        x = R(self.__unit_gens_primecase(p).lift())
        n = p**(r - 2) * (p - 1)
        one = integer_mod.Mod(1, pr)
        for b in range(0, p):
            z = x + R(b * p)
            if z**n != one:
                a = integer_mod.Mod(z, pr)
                return [a]
        raise ValueError("p={}, r={}, couldn't find generator".format(p, r))
Beispiel #6
0
    def unit_gens(self):
        r"""
        Returns generators for the unit group `(\ZZ/N\ZZ)^*`.

        We compute the list of generators using a deterministic algorithm, so
        the generators list will always be the same. For each odd prime divisor
        of N there will be exactly one corresponding generator; if N is even
        there will be 0, 1 or 2 generators according to whether 2 divides N to
        order 1, 2 or `\ge 3`.

        OUTPUT:

        A tuple containing the units of ``self``.

        EXAMPLES::

            sage: R = IntegerModRing(18)
            sage: R.unit_gens()
            (11,)
            sage: R = IntegerModRing(17)
            sage: R.unit_gens()
            (3,)
            sage: IntegerModRing(next_prime(10^30)).unit_gens()
            (5,)

        TESTS::

            sage: IntegerModRing(2).unit_gens()
            ()
            sage: IntegerModRing(4).unit_gens()
            (3,)
            sage: IntegerModRing(8).unit_gens()
            (7, 5)
        """
        n = self.__order
        if n == 1:
            return (self(1),)
        unit_gens = []
        for p,r in self.factored_order():
            m = n/(p**r)
            for g in self.__unit_gens_primepowercase(p, r):
                x = g.crt(integer_mod.Mod(1,m))
                if x != 1:
                    unit_gens.append(x)
        return tuple(unit_gens)
 def unit_gens(self):
     r"""
     Returns generators for the unit group `(\ZZ/N\ZZ)^*`.
     
     We compute the list of generators using a deterministic algorithm, so
     the generators list will always be the same. For each odd prime divisor
     of N there will be exactly one corresponding generator; if N is even
     there will be 0, 1 or 2 generators according to whether 2 divides N to
     order 1, 2 or `\ge 3`. 
     
     INPUT: (none)
     
     OUTPUT:
     
     -  ``list`` - a list of elements of self
     
     EXAMPLES::
     
         sage: R = IntegerModRing(18)
         sage: R.unit_gens()
         [11]
         sage: R = IntegerModRing(17)
         sage: R.unit_gens()
         [3]
         sage: IntegerModRing(next_prime(10^30)).unit_gens()
         [5]
     """
     try:
         return self.__unit_gens
     except AttributeError:
         self.__unit_gens = []
     n = self.__order
     if n == 1:
         self.__unit_gens = [self(1)]
         return self.__unit_gens
     for p, r in self.factored_order():
         m = n / (p**r)
         for g in self.__unit_gens_primepowercase(p, r):
             x = g.crt(integer_mod.Mod(1, m))
             if x != 1:
                 self.__unit_gens.append(x)
     return self.__unit_gens
    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, 21, 11)

            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, 31, 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)