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)))]
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))
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)