def GammaH_constructor(level, H): r""" Return the congruence subgroup `\Gamma_H(N)`, which is the subgroup of `SL_2(\ZZ)` consisting of matrices of the form `\begin{pmatrix} a & b \\ c & d \end{pmatrix}` with `N | c` and `a, b \in H`, for `H` a specified subgroup of `(\ZZ/N\ZZ)^\times`. INPUT: - level -- an integer - H -- either 0, 1, or a list * If H is a list, return `\Gamma_H(N)`, where `H` is the subgroup of `(\ZZ/N\ZZ)^*` **generated** by the elements of the list. * If H = 0, returns `\Gamma_0(N)`. * If H = 1, returns `\Gamma_1(N)`. EXAMPLES:: sage: GammaH(11,0) # indirect doctest Congruence Subgroup Gamma0(11) sage: GammaH(11,1) Congruence Subgroup Gamma1(11) sage: GammaH(11,[10]) Congruence Subgroup Gamma_H(11) with H generated by [10] sage: GammaH(11,[10,1]) Congruence Subgroup Gamma_H(11) with H generated by [10] sage: GammaH(14,[10]) Traceback (most recent call last): ... ArithmeticError: The generators [10] must be units modulo 14 """ from all import Gamma0, Gamma1, SL2Z if level == 1: return SL2Z elif H == 0: return Gamma0(level) elif H == 1: return Gamma1(level) H = _normalize_H(H, level) if H == []: return Gamma1(level) Hlist = _list_subgroup(level, H) if len(Hlist) == euler_phi(level): return Gamma0(level) key = (level, tuple(H)) try: return _gammaH_cache[key] except KeyError: _gammaH_cache[key] = GammaH_class(level, H, Hlist) return _gammaH_cache[key]
def index(self): r""" Return the index of self in SL2Z. EXAMPLE:: sage: [G.index() for G in Gamma0(40).gamma_h_subgroups()] [72, 144, 144, 144, 144, 288, 288, 288, 288, 144, 288, 288, 576, 576, 144, 288, 288, 576, 576, 144, 288, 288, 576, 576, 288, 576, 1152] """ from all import Gamma1 return Gamma1(self.level()).index() / len(self._list_of_elements_in_H())
def _new_group_from_level(self, level): r""" Return a new group of the same type (Gamma0, Gamma1, or GammaH) as self of the given level. In the case that self is of type GammaH, we take the largest H inside `(\ZZ/ \text{level}\ZZ)^\times` which maps to H, namely its inverse image under the natural reduction map. EXAMPLES:: sage: G = Gamma0(20) sage: G._new_group_from_level(4) Congruence Subgroup Gamma0(4) sage: G._new_group_from_level(40) Congruence Subgroup Gamma0(40) sage: G = Gamma1(10) sage: G._new_group_from_level(6) Traceback (most recent call last): ... ValueError: one level must divide the other sage: G = GammaH(50,[7]); G Congruence Subgroup Gamma_H(50) with H generated by [7] sage: G._new_group_from_level(25) Congruence Subgroup Gamma_H(25) with H generated by [7] sage: G._new_group_from_level(10) Congruence Subgroup Gamma0(10) sage: G._new_group_from_level(100) Congruence Subgroup Gamma_H(100) with H generated by [7, 57] """ from congroup_gamma0 import is_Gamma0 from congroup_gamma1 import is_Gamma1 from congroup_gammaH import is_GammaH from all import Gamma0, Gamma1, GammaH N = self.level() if (level % N) and (N % level): raise ValueError, "one level must divide the other" if is_Gamma0(self): return Gamma0(level) elif is_Gamma1(self): return Gamma1(level) elif is_GammaH(self): H = self._generators_for_H() if level > N: d = level // N diffs = [N * i for i in range(d)] newH = [h + diff for h in H for diff in diffs] return GammaH(level, [x for x in newH if gcd(level, x) == 1]) else: return GammaH(level, [h % level for h in H]) else: raise NotImplementedError