Beispiel #1
0
    def _reduce_cusp(self, c):
        r"""
        Compute a minimal representative for the given cusp c.
        Returns a pair (c', t), where c' is the minimal representative
        for the given cusp, and t is either 1 or -1, as explained
        below. Largely for internal use.

        The minimal representative for a cusp is the element in `P^1(Q)`
        in lowest terms with minimal positive denominator, and minimal
        positive numerator for that denominator.

        Two cusps `u1/v1` and `u2/v2` are equivalent modulo `\Gamma_H(N)`
        if and only if
            `v1 =  h*v2 (mod N)` and `u1 =  h^(-1)*u2 (mod gcd(v1,N))`
        or
            `v1 = -h*v2 (mod N)` and `u1 = -h^(-1)*u2 (mod gcd(v1,N))`
        for some `h \in H`. Then t is 1 or -1 as c and c' fall into
        the first or second case, respectively.

        EXAMPLES::

            sage: GammaH(6,[5])._reduce_cusp(Cusp(5,3))
            (1/3, -1)
            sage: GammaH(12,[5])._reduce_cusp(Cusp(8,9))
            (1/3, -1)
            sage: GammaH(12,[5])._reduce_cusp(Cusp(5,12))
            (Infinity, 1)
            sage: GammaH(12,[])._reduce_cusp(Cusp(5,12))
            (5/12, 1)
            sage: GammaH(21,[5])._reduce_cusp(Cusp(-9/14))
            (1/7, 1)
        """
        c = Cusp(c)
        N = int(self.level())
        Cusps = c.parent()
        v = int(c.denominator() % N)
        H = self._list_of_elements_in_H()

        # First, if N | v, take care of this case. If u is in \pm H,
        # then we return Infinity. If not, let u_0 be the minimum
        # of \{ h*u | h \in \pm H \}. Then return u_0/N.
        if not v:
            u = c.numerator() % N
            if u in H:
                return Cusps((1, 0)), 1
            if (N - u) in H:
                return Cusps((1, 0)), -1
            ls = [(u * h) % N for h in H]
            m1 = min(ls)
            m2 = N - max(ls)
            if m1 < m2:
                return Cusps((m1, N)), 1
            else:
                return Cusps((m2, N)), -1

        u = int(c.numerator() % v)
        gcd = get_gcd(N)
        d = gcd(v, N)

        # If (N,v) == 1, let v_0 be the minimal element
        # in \{ v * h | h \in \pm H \}. Then we either return
        # Infinity or 1/v_0, as v is or is not in \pm H,
        # respectively.
        if d == 1:
            if v in H:
                return Cusps((0, 1)), 1
            if (N - v) in H:
                return Cusps((0, 1)), -1
            ls = [(v * h) % N for h in H]
            m1 = min(ls)
            m2 = N - max(ls)
            if m1 < m2:
                return Cusps((1, m1)), 1
            else:
                return Cusps((1, m2)), -1

        val_min = v
        inv_mod = get_inverse_mod(N)

        # Now we're in the case (N,v) > 1. So we have to do several
        # steps: first, compute v_0 as above. While computing this
        # minimum, keep track of *all* pairs of (h,s) which give this
        # value of v_0.
        hs_ls = [(1, 1)]
        for h in H:
            tmp = (v * h) % N

            if tmp < val_min:
                val_min = tmp
                hs_ls = [(inv_mod(h, N), 1)]
            elif tmp == val_min:
                hs_ls.append((inv_mod(h, N), 1))

            if (N - tmp) < val_min:
                val_min = N - tmp
                hs_ls = [(inv_mod(h, N), -1)]
            elif (N - tmp) == val_min:
                hs_ls.append((inv_mod(h, N), -1))

        # Finally, we find our minimal numerator. Let u_1 be the
        # minimum of s*h^-1*u mod d as (h,s) ranges over the elements
        # of hs_ls. We must find the smallest integer u_0 which is
        # smaller than v_0, congruent to u_1 mod d, and coprime to
        # v_0. Then u_0/v_0 is our minimal representative.
        u_min = val_min
        sign = None
        for h_inv, s in hs_ls:
            tmp = (h_inv * s * u) % d
            while gcd(tmp, val_min) > 1 and tmp < u_min:
                tmp += d
            if tmp < u_min:
                u_min = tmp
                sign = s

        return Cusps((u_min, val_min)), sign
Beispiel #2
0
    def _reduce_cusp(self, c):
        r"""
        Compute a minimal representative for the given cusp c.
        Returns a pair (c', t), where c' is the minimal representative
        for the given cusp, and t is either 1 or -1, as explained
        below. Largely for internal use.

        The minimal representative for a cusp is the element in `P^1(Q)`
        in lowest terms with minimal positive denominator, and minimal
        positive numerator for that denominator.

        Two cusps `u1/v1` and `u2/v2` are equivalent modulo `\Gamma_H(N)`
        if and only if
            `v1 =  h*v2 (mod N)` and `u1 =  h^(-1)*u2 (mod gcd(v1,N))`
        or
            `v1 = -h*v2 (mod N)` and `u1 = -h^(-1)*u2 (mod gcd(v1,N))`
        for some `h \in H`. Then t is 1 or -1 as c and c' fall into
        the first or second case, respectively.

        EXAMPLES::

            sage: GammaH(6,[5])._reduce_cusp(Cusp(5,3))
            (1/3, -1)
            sage: GammaH(12,[5])._reduce_cusp(Cusp(8,9))
            (1/3, -1)
            sage: GammaH(12,[5])._reduce_cusp(Cusp(5,12))
            (Infinity, 1)
            sage: GammaH(12,[])._reduce_cusp(Cusp(5,12))
            (5/12, 1)
            sage: GammaH(21,[5])._reduce_cusp(Cusp(-9/14))
            (1/7, 1)
        """
        c = Cusp(c)
        N = int(self.level())
        Cusps = c.parent()
        v = int(c.denominator() % N)
        H = self._list_of_elements_in_H()

        # First, if N | v, take care of this case. If u is in \pm H,
        # then we return Infinity. If not, let u_0 be the minimum
        # of \{ h*u | h \in \pm H \}. Then return u_0/N.
        if not v:
            u = c.numerator() % N
            if u in H:
                return Cusps((1,0)), 1
            if (N-u) in H:
                return Cusps((1,0)), -1
            ls = [ (u*h)%N for h in H ]
            m1 = min(ls)
            m2 = N-max(ls)
            if m1 < m2:
                return Cusps((m1,N)), 1
            else:
                return Cusps((m2,N)), -1

        u = int(c.numerator() % v)
        gcd = get_gcd(N)
        d = gcd(v,N)

        # If (N,v) == 1, let v_0 be the minimal element
        # in \{ v * h | h \in \pm H \}. Then we either return
        # Infinity or 1/v_0, as v is or is not in \pm H,
        # respectively.
        if d == 1:
            if v in H:
                return Cusps((0,1)), 1
            if (N-v) in H:
                return Cusps((0,1)), -1
            ls = [ (v*h)%N for h in H ]
            m1 = min(ls)
            m2 = N-max(ls)
            if m1 < m2:
                return Cusps((1,m1)), 1
            else:
                return Cusps((1,m2)), -1

        val_min = v
        inv_mod = get_inverse_mod(N)

        # Now we're in the case (N,v) > 1. So we have to do several
        # steps: first, compute v_0 as above. While computing this
        # minimum, keep track of *all* pairs of (h,s) which give this
        # value of v_0.
        hs_ls = [(1,1)]
        for h in H:
            tmp = (v*h)%N

            if tmp < val_min:
                val_min = tmp
                hs_ls = [(inv_mod(h,N), 1)]
            elif tmp == val_min:
                hs_ls.append((inv_mod(h,N), 1))

            if (N-tmp) < val_min:
                val_min = N - tmp
                hs_ls = [(inv_mod(h,N), -1)]
            elif (N-tmp) == val_min:
                hs_ls.append((inv_mod(h,N), -1))

        # Finally, we find our minimal numerator. Let u_1 be the
        # minimum of s*h^-1*u mod d as (h,s) ranges over the elements
        # of hs_ls. We must find the smallest integer u_0 which is
        # smaller than v_0, congruent to u_1 mod d, and coprime to
        # v_0. Then u_0/v_0 is our minimal representative.
        u_min = val_min
        sign = None
        for h_inv,s in hs_ls:
            tmp = (h_inv * s * u)%d
            while gcd(tmp, val_min) > 1 and tmp < u_min:
                tmp += d
            if tmp < u_min:
                u_min = tmp
                sign = s

        return Cusps((u_min, val_min)), sign
Beispiel #3
0
    def _coset_reduction_data_first_coord(G):
        """
        Compute data used for determining the canonical coset
        representative of an element of SL_2(Z) modulo G. This
        function specifically returns data needed for the first part
        of the reduction step (the first coordinate).

        INPUT:
            G -- a congruence subgroup Gamma_0(N), Gamma_1(N), or Gamma_H(N).

        OUTPUT:
            A list v such that
                v[u] = (min(u*h: h in H),
                        gcd(u,N) ,
                        an h such that h*u = min(u*h: h in H)).

        EXAMPLES::

            sage: G = Gamma0(12)
            sage: sage.modular.arithgroup.congroup_gammaH.GammaH_class._coset_reduction_data_first_coord(G)
            [(0, 12, 0), (1, 1, 1), (2, 2, 1), (3, 3, 1), (4, 4, 1), (1, 1, 5), (6, 6, 1),
            (1, 1, 7), (4, 4, 5), (3, 3, 7), (2, 2, 5), (1, 1, 11)]
        """
        H = [int(x) for x in G._list_of_elements_in_H()]
        N = int(G.level())

        # Get some useful fast functions for inverse and gcd
        inverse_mod = get_inverse_mod(N)  # optimal inverse function
        gcd = get_gcd(N)  # optimal gcd function

        # We will be filling this list in below.
        reduct_data = [0] * N

        # We can fill in 0 and all elements of H immediately
        reduct_data[0] = (0, N, 0)
        for u in H:
            reduct_data[u] = (1, 1, inverse_mod(u, N))

        # Make a table of the reduction of H (mod N/d), one for each
        # divisor d.
        repr_H_mod_N_over_d = {}
        for d in divisors(N):
            # We special-case N == d because in this case,
            # 1 % N_over_d is 0
            if N == d:
                repr_H_mod_N_over_d[d] = [1]
                break
            N_over_d = N // d
            # For each element of H, we look at its image mod
            # N_over_d. If we haven't yet seen it, add it on to
            # the end of z.
            w = [0] * N_over_d
            z = [1]
            for x in H:
                val = x % N_over_d
                if not w[val]:
                    w[val] = 1
                    z.append(x)
            repr_H_mod_N_over_d[d] = z

        # Compute the rest of the tuples. The values left to process
        # are those where reduct_data has a 0. Note that several of
        # these values are processed on each loop below, so re-index
        # each time.
        while True:
            try:
                u = reduct_data.index(0)
            except ValueError:
                break
            d = gcd(u, N)
            for x in repr_H_mod_N_over_d[d]:
                reduct_data[(u * x) % N] = (u, d, inverse_mod(x, N))

        return reduct_data
Beispiel #4
0
    def _coset_reduction_data_first_coord(G):
        """
        Compute data used for determining the canonical coset
        representative of an element of SL_2(Z) modulo G. This
        function specifically returns data needed for the first part
        of the reduction step (the first coordinate).

        INPUT:
            G -- a congruence subgroup Gamma_0(N), Gamma_1(N), or Gamma_H(N).

        OUTPUT:
            A list v such that
                v[u] = (min(u*h: h in H),
                        gcd(u,N) ,
                        an h such that h*u = min(u*h: h in H)).

        EXAMPLES::

            sage: G = Gamma0(12)
            sage: sage.modular.arithgroup.congroup_gammaH.GammaH_class._coset_reduction_data_first_coord(G)
            [(0, 12, 0), (1, 1, 1), (2, 2, 1), (3, 3, 1), (4, 4, 1), (1, 1, 5), (6, 6, 1),
            (1, 1, 7), (4, 4, 5), (3, 3, 7), (2, 2, 5), (1, 1, 11)]
        """
        H = [ int(x) for x in G._list_of_elements_in_H() ]
        N = int(G.level())

        # Get some useful fast functions for inverse and gcd
        inverse_mod = get_inverse_mod(N)   # optimal inverse function
        gcd = get_gcd(N)   # optimal gcd function

        # We will be filling this list in below.
        reduct_data = [0] * N

        # We can fill in 0 and all elements of H immediately
        reduct_data[0] = (0,N,0)
        for u in H:
            reduct_data[u] = (1, 1, inverse_mod(u, N))

        # Make a table of the reduction of H (mod N/d), one for each
        # divisor d.
        repr_H_mod_N_over_d = {}
        for d in divisors(N):
            # We special-case N == d because in this case,
            # 1 % N_over_d is 0
            if N == d:
                repr_H_mod_N_over_d[d] = [1]
                break
            N_over_d = N//d
            # For each element of H, we look at its image mod
            # N_over_d. If we haven't yet seen it, add it on to
            # the end of z.
            w = [0] * N_over_d
            z = [1]
            for x in H:
                val = x%N_over_d
                if not w[val]:
                    w[val] = 1
                    z.append(x)
            repr_H_mod_N_over_d[d] = z

        # Compute the rest of the tuples. The values left to process
        # are those where reduct_data has a 0. Note that several of
        # these values are processed on each loop below, so re-index
        # each time.
        while True:
            try:
                u = reduct_data.index(0)
            except ValueError:
                break
            d = gcd(u, N)
            for x in repr_H_mod_N_over_d[d]:
                reduct_data[(u*x)%N] = (u, d, inverse_mod(x,N))

        return reduct_data