Ejemplo n.º 1
0
    def has_fundamental_discriminant(self):
        """
        Checks if the discriminant D of this form is a fundamental
        discriminant (i.e. D is the smallest element of its
        squareclass with D = 0 or 1 mod 4).

        EXAMPLES::

            sage: Q = BinaryQF([1,0,1])
            sage: Q.discriminant()
            -4
            sage: Q.has_fundamental_discriminant()
            True

            sage: Q = BinaryQF([2,0,2])
            sage: Q.discriminant()
            -16
            sage: Q.has_fundamental_discriminant()
            False
        """
        return is_fundamental_discriminant(self.discriminant())
Ejemplo n.º 2
0
    def has_fundamental_discriminant(self):
        """
        Return if the discriminant `D` of this form is a fundamental
        discriminant (i.e. `D` is the smallest element of its
        squareclass with `D = 0` or `1` modulo `4`).

        EXAMPLES::

            sage: Q = BinaryQF([1, 0, 1])
            sage: Q.discriminant()
            -4
            sage: Q.has_fundamental_discriminant()
            True

            sage: Q = BinaryQF([2, 0, 2])
            sage: Q.discriminant()
            -16
            sage: Q.has_fundamental_discriminant()
            False
        """
        return is_fundamental_discriminant(self.discriminant())
Ejemplo n.º 3
0
    def has_fundamental_discriminant(self):
        """
        Checks if the discriminant D of this form is a fundamental
        discriminant (i.e. D is the smallest element of its
        squareclass with D = 0 or 1 mod 4).

        EXAMPLES::

            sage: Q = BinaryQF([1,0,1])
            sage: Q.discriminant()
            -4
            sage: Q.has_fundamental_discriminant()
            True

            sage: Q = BinaryQF([2,0,2])
            sage: Q.discriminant()
            -16
            sage: Q.has_fundamental_discriminant()
            False
        """
        return is_fundamental_discriminant(self.discriminant())
Ejemplo n.º 4
0
def discriminants_with_bounded_class_number(hmax, B=None, proof=None):
    r"""
    Return dictionary with keys class numbers `h\le hmax` and values the
    list of all pairs `(D, f)`, with `D<0` a fundamental discriminant such
    that `Df^2` has class number `h`.  If the optional bound `B` is given,
    return only those pairs with fundamental `|D| \le B`, though `f` can
    still be arbitrarily large.

    INPUT:

    - ``hmax`` -- integer
    - `B` -- integer or None; if None returns all pairs
    - ``proof`` -- this code calls the PARI function ``qfbclassno``, so it
      could give wrong answers when ``proof``==``False``.  The default is
      whatever ``proof.number_field()`` is.  If ``proof==False`` and `B` is
      ``None``, at least the number of discriminants is correct, since it
      is double checked with Watkins's table.

    OUTPUT:

    - dictionary

    In case `B` is not given, we use Mark Watkins's: "Class numbers of
    imaginary quadratic fields" to compute a `B` that captures all `h`
    up to `hmax` (only available for `hmax\le100`).

    EXAMPLES::

        sage: v = sage.schemes.elliptic_curves.cm.discriminants_with_bounded_class_number(3)
        sage: sorted(v)
        [1, 2, 3]
        sage: v[1]
        [(-3, 3), (-3, 2), (-3, 1), (-4, 2), (-4, 1), (-7, 2), (-7, 1), (-8, 1), (-11, 1), (-19, 1), (-43, 1), (-67, 1), (-163, 1)]
        sage: v[2]
        [(-3, 7), (-3, 5), (-3, 4), (-4, 5), (-4, 4), (-4, 3), (-7, 4), (-8, 3), (-8, 2), (-11, 3), (-15, 2), (-15, 1), (-20, 1), (-24, 1), (-35, 1), (-40, 1), (-51, 1), (-52, 1), (-88, 1), (-91, 1), (-115, 1), (-123, 1), (-148, 1), (-187, 1), (-232, 1), (-235, 1), (-267, 1), (-403, 1), (-427, 1)]
        sage: v[3]
        [(-3, 9), (-3, 6), (-11, 2), (-19, 2), (-23, 2), (-23, 1), (-31, 2), (-31, 1), (-43, 2), (-59, 1), (-67, 2), (-83, 1), (-107, 1), (-139, 1), (-163, 2), (-211, 1), (-283, 1), (-307, 1), (-331, 1), (-379, 1), (-499, 1), (-547, 1), (-643, 1), (-883, 1), (-907, 1)]
        sage: v = sage.schemes.elliptic_curves.cm.discriminants_with_bounded_class_number(8, proof=False)
        sage: sorted(len(v[h]) for h in v)
        [13, 25, 29, 29, 38, 84, 101, 208]

    Find all class numbers for discriminant up to 50::

        sage: sage.schemes.elliptic_curves.cm.discriminants_with_bounded_class_number(hmax=5, B=50)
        {1: [(-3, 3), (-3, 2), (-3, 1), (-4, 2), (-4, 1), (-7, 2), (-7, 1), (-8, 1), (-11, 1), (-19, 1), (-43, 1)], 2: [(-3, 7), (-3, 5), (-3, 4), (-4, 5), (-4, 4), (-4, 3), (-7, 4), (-8, 3), (-8, 2), (-11, 3), (-15, 2), (-15, 1), (-20, 1), (-24, 1), (-35, 1), (-40, 1)], 3: [(-3, 9), (-3, 6), (-11, 2), (-19, 2), (-23, 2), (-23, 1), (-31, 2), (-31, 1), (-43, 2)], 4: [(-3, 13), (-3, 11), (-3, 8), (-4, 10), (-4, 8), (-4, 7), (-4, 6), (-7, 8), (-7, 6), (-7, 3), (-8, 6), (-8, 4), (-11, 5), (-15, 4), (-19, 5), (-19, 3), (-20, 3), (-20, 2), (-24, 2), (-35, 3), (-39, 2), (-39, 1), (-40, 2), (-43, 3)], 5: [(-47, 2), (-47, 1)]}
    """
    # imports that are needed only for this function
    from sage.structure.proof.proof import get_flag

    # deal with input defaults and type checking
    proof = get_flag(proof, 'number_field')
    hmax = Integer(hmax)

    # T stores the output
    T = {}

    # Easy case -- instead of giving error, give meaningful output
    if hmax < 1:
        return T

    if B is None:
        # Determine how far we have to go by applying Watkins's theorem.
        v = [
            largest_fundamental_disc_with_class_number(h)
            for h in range(1, hmax + 1)
        ]
        B = max([b for b, _ in v])
        fund_count = [0] + [cnt for _, cnt in v]
    else:
        # Nothing to do -- set to None so we can use this later to know not
        # to do a double check about how many we find.
        fund_count = None
        B = Integer(B)

    if B <= 2:
        # This is an easy special case, since there are no fundamental discriminants
        # this small.
        return T

    # This lower bound gets used in an inner loop below.
    from math import log

    def lb(f):
        """Lower bound on euler_phi."""
        # 1.79 > e^gamma = 1.7810724...
        if f <= 1: return 0  # don't do log(log(1)) = log(0)
        return f / (1.79 * log(log(f)) + 3.0 / log(log(f)))

    for D in range(-B, -2):
        D = Integer(D)
        if is_fundamental_discriminant(D):
            h_D = D.class_number(proof)
            # For each fundamental discriminant D, loop through the f's such
            # that h(D*f^2) could possibly be <= hmax.  As explained to me by Cremona,
            # we have h(D*f^2) >= (1/c)*h(D)*phi_D(f) >= (1/c)*h(D)*euler_phi(f), where
            # phi_D(f) is like euler_phi(f) but the factor (1-1/p) is replaced
            # by a factor of (1-kr(D,p)*p), where kr(D/p) is the Kronecker symbol.
            # The factor c is 1 unless D=-4 and f>1 (when c=2) or D=-3 and f>1 (when c=3).
            # Since (1-1/p) <= 1 and (1-1/p) <= (1+1/p), we see that
            #     euler_phi(f) <= phi_D(f).
            #
            # We have the following analytic lower bound on euler_phi:
            #
            #     euler_phi(f) >= lb(f) = f / (exp(euler_gamma)*log(log(n)) + 3/log(log(n))).
            #
            # See Theorem 8 of Peter Clark's
            #   http://math.uga.edu/~pete/4400arithmeticorders.pdf
            # which is a consequence of Theorem 15 of
            # [Rosser and Schoenfeld, 1962].
            #
            # By Calculus, we see that the lb(f) is an increasing function of f >= 2.
            #
            # NOTE: You can visibly "see" that it is a lower bound in Sage with
            #   lb(n) = n/(exp(euler_gamma)*log(log(n)) + 3/log(log(n)))
            #   plot(lb, (n, 1, 10^4), color='red') + plot(lambda x: euler_phi(int(x)), 1, 10^4).show()
            #
            # So we consider f=1,2,..., until the first f with lb(f)*h_D > c*h_max.
            # (Note that lb(f) is <= 0 for f=1,2, so nothing special is needed there.)
            #
            # TODO: Maybe we could do better using a bound for phi_D(f).
            #
            f = Integer(1)
            chmax = hmax
            if D == -3:
                chmax *= 3
            else:
                if D == -4:
                    chmax *= 2
            while lb(f) * h_D <= chmax:
                if f == 1:
                    h = h_D
                else:
                    h = (D * f * f).class_number(proof)
                # If the class number of this order is within the range, then
                # use it.  (NOTE: In some cases there is a simple relation between
                # the class number for D and D*f^2, and this could be used to
                # optimize this inner loop a little.)
                if h <= hmax:
                    z = (D, f)
                    if h in T:
                        T[h].append(z)
                    else:
                        T[h] = [z]
                f += 1

    for h in T:
        T[h] = list(reversed(T[h]))

    if fund_count is not None:
        # Double check that we found the right number of fundamental
        # discriminants; we might as well, since Watkins provides this
        # data.
        for h in T:
            if len([DD for DD, ff in T[h] if ff == 1]) != fund_count[h]:
                raise RuntimeError(
                    "number of discriminants inconsistent with Watkins's table"
                )

    return T
Ejemplo n.º 5
0
Archivo: cm.py Proyecto: mcognetta/sage
def discriminants_with_bounded_class_number(hmax, B=None, proof=None):
    """
    Return dictionary with keys class numbers `h\le hmax` and values the
    list of all pairs `(D, f)`, with `D<0` a fundamental discriminant such
    that `Df^2` has class number `h`.  If the optional bound `B` is given,
    return only those pairs with fundamental `|D| \le B`, though `f` can
    still be arbitrarily large.

    INPUT:

    - ``hmax`` -- integer
    - `B` -- integer or None; if None returns all pairs
    - ``proof`` -- this code calls the PARI function ``qfbclassno``, so it
      could give wrong answers when ``proof``==``False``.  The default is
      whatever ``proof.number_field()`` is.  If ``proof==False`` and `B` is
      ``None``, at least the number of discriminants is correct, since it
      is double checked with Watkins's table.

    OUTPUT:

    - dictionary

    In case `B` is not given, we use Mark Watkins's: "Class numbers of
    imaginary quadratic fields" to compute a `B` that captures all `h`
    up to `hmax` (only available for `hmax\le100`).

    EXAMPLES::

        sage: v = sage.schemes.elliptic_curves.cm.discriminants_with_bounded_class_number(3)
        sage: list(v)
        [1, 2, 3]
        sage: v[1]
        [(-3, 3), (-3, 2), (-3, 1), (-4, 2), (-4, 1), (-7, 2), (-7, 1), (-8, 1), (-11, 1), (-19, 1), (-43, 1), (-67, 1), (-163, 1)]
        sage: v[2]
        [(-3, 7), (-3, 5), (-3, 4), (-4, 5), (-4, 4), (-4, 3), (-7, 4), (-8, 3), (-8, 2), (-11, 3), (-15, 2), (-15, 1), (-20, 1), (-24, 1), (-35, 1), (-40, 1), (-51, 1), (-52, 1), (-88, 1), (-91, 1), (-115, 1), (-123, 1), (-148, 1), (-187, 1), (-232, 1), (-235, 1), (-267, 1), (-403, 1), (-427, 1)]
        sage: v[3]
        [(-3, 9), (-3, 6), (-11, 2), (-19, 2), (-23, 2), (-23, 1), (-31, 2), (-31, 1), (-43, 2), (-59, 1), (-67, 2), (-83, 1), (-107, 1), (-139, 1), (-163, 2), (-211, 1), (-283, 1), (-307, 1), (-331, 1), (-379, 1), (-499, 1), (-547, 1), (-643, 1), (-883, 1), (-907, 1)]
        sage: v = sage.schemes.elliptic_curves.cm.discriminants_with_bounded_class_number(8, proof=False)
        sage: [len(v[h]) for h in v]
        [13, 29, 25, 84, 29, 101, 38, 208]

    Find all class numbers for discriminant up to 50::

        sage: sage.schemes.elliptic_curves.cm.discriminants_with_bounded_class_number(hmax=5, B=50)
        {1: [(-3, 3), (-3, 2), (-3, 1), (-4, 2), (-4, 1), (-7, 2), (-7, 1), (-8, 1), (-11, 1), (-19, 1), (-43, 1)], 2: [(-3, 7), (-3, 5), (-3, 4), (-4, 5), (-4, 4), (-4, 3), (-7, 4), (-8, 3), (-8, 2), (-11, 3), (-15, 2), (-15, 1), (-20, 1), (-24, 1), (-35, 1), (-40, 1)], 3: [(-3, 9), (-3, 6), (-11, 2), (-19, 2), (-23, 2), (-23, 1), (-31, 2), (-31, 1), (-43, 2)], 4: [(-3, 13), (-3, 11), (-3, 8), (-4, 10), (-4, 8), (-4, 7), (-4, 6), (-7, 8), (-7, 6), (-7, 3), (-8, 6), (-8, 4), (-11, 5), (-15, 4), (-19, 5), (-19, 3), (-20, 3), (-20, 2), (-24, 2), (-35, 3), (-39, 2), (-39, 1), (-40, 2), (-43, 3)], 5: [(-47, 2), (-47, 1)]}
    """
    # imports that are needed only for this function
    from sage.structure.proof.proof import get_flag
    import math
    from sage.misc.functional import round

    # deal with input defaults and type checking
    proof = get_flag(proof, 'number_field')
    hmax = Integer(hmax)

    # T stores the output
    T = {}

    # Easy case -- instead of giving error, give meaningful output
    if hmax < 1:
        return T

    if B is None:
        # Determine how far we have to go by applying Watkins's theorem.
        v = [largest_fundamental_disc_with_class_number(h) for h in range(1, hmax+1)]
        B = max([b for b,_ in v])
        fund_count = [0] + [cnt for _,cnt in v]
    else:
        # Nothing to do -- set to None so we can use this later to know not
        # to do a double check about how many we find.
        fund_count = None
        B = Integer(B)

    if B <= 2:
        # This is an easy special case, since there are no fundamental discriminants
        # this small.
        return T

    # This lower bound gets used in an inner loop below.
    from math import log
    def lb(f):
        """Lower bound on euler_phi."""
        # 1.79 > e^gamma = 1.7810724...
        if f <= 1: return 0  # don't do log(log(1)) = log(0)
        return f/(1.79*log(log(f)) + 3.0/log(log(f)))

    for D in range(-B, -2):
        D = Integer(D)
        if is_fundamental_discriminant(D):
            h_D = D.class_number(proof)
            # For each fundamental discriminant D, loop through the f's such
            # that h(D*f^2) could possibly be <= hmax.  As explained to me by Cremona,
            # we have h(D*f^2) >= (1/c)*h(D)*phi_D(f) >= (1/c)*h(D)*euler_phi(f), where
            # phi_D(f) is like euler_phi(f) but the factor (1-1/p) is replaced
            # by a factor of (1-kr(D,p)*p), where kr(D/p) is the Kronecker symbol.
            # The factor c is 1 unless D=-4 and f>1 (when c=2) or D=-3 and f>1 (when c=3).
            # Since (1-1/p) <= 1 and (1-1/p) <= (1+1/p), we see that
            #     euler_phi(f) <= phi_D(f).
            #
            # We have the following analytic lower bound on euler_phi:
            #
            #     euler_phi(f) >= lb(f) = f / (exp(euler_gamma)*log(log(n)) + 3/log(log(n))).
            #
            # See Theorem 8 of Peter Clark's
            #   http://math.uga.edu/~pete/4400arithmeticorders.pdf
            # which is a consequence of Theorem 15 of
            # [Rosser and Schoenfeld, 1962].
            #
            # By Calculus, we see that the lb(f) is an increasing function of f >= 2.
            #
            # NOTE: You can visibly "see" that it is a lower bound in Sage with
            #   lb(n) = n/(exp(euler_gamma)*log(log(n)) + 3/log(log(n)))
            #   plot(lb, (n, 1, 10^4), color='red') + plot(lambda x: euler_phi(int(x)), 1, 10^4).show()
            #
            # So we consider f=1,2,..., until the first f with lb(f)*h_D > c*h_max.
            # (Note that lb(f) is <= 0 for f=1,2, so nothing special is needed there.)
            #
            # TODO: Maybe we could do better using a bound for for phi_D(f).
            #
            f = Integer(1)
            chmax=hmax
            if D==-3:
                chmax*=3
            else:
                if D==-4:
                    chmax*=2
            while lb(f)*h_D <= chmax:
                if f == 1:
                    h = h_D
                else:
                    h = (D*f*f).class_number(proof)
                # If the class number of this order is within the range, then
                # use it.  (NOTE: In some cases there is a simple relation between
                # the class number for D and D*f^2, and this could be used to
                # optimize this inner loop a little.)
                if h <= hmax:
                    z = (D, f)
                    if h in T:
                        T[h].append(z)
                    else:
                        T[h] = [z]
                f += 1

    for h in T:
        T[h] = list(reversed(T[h]))

    if fund_count is not None:
        # Double check that we found the right number of fundamental
        # discriminants; we might as well, since Watkins provides this
        # data.
        for h in T:
            if len([D for D,f in T[h] if f==1]) != fund_count[h]:
                raise RuntimeError("number of discriminants inconsistent with Watkins's table")

    return T
Ejemplo n.º 6
0
def BinaryQF_reduced_representatives(D, primitive_only=False):
    r"""
    Returns a list of inequivalent reduced representatives for the
    equivalence classes of positive definite binary forms of
    discriminant D.

    INPUT:

    - `D` -- (integer) A negative discriminant.

    - ``primitive_only`` -- (bool, default False) flag controlling whether only
      primitive forms are included.

    OUTPUT:

    (list) A lexicographically-ordered list of inequivalent reduced
    representatives for the equivalence classes of positive definite binary
    forms of discriminant `D`.  If ``primitive_only`` is ``True`` then
    imprimitive forms (which only exist when `D` is not fundamental) are
    omitted; otherwise they are included.

    EXAMPLES::

        sage: BinaryQF_reduced_representatives(-4)
        [x^2 + y^2]

        sage: BinaryQF_reduced_representatives(-163)
        [x^2 + x*y + 41*y^2]

        sage: BinaryQF_reduced_representatives(-12)
        [x^2 + 3*y^2, 2*x^2 + 2*x*y + 2*y^2]

        sage: BinaryQF_reduced_representatives(-16)
        [x^2 + 4*y^2, 2*x^2 + 2*y^2]

        sage: BinaryQF_reduced_representatives(-63)
        [x^2 + x*y + 16*y^2, 2*x^2 - x*y + 8*y^2, 2*x^2 + x*y + 8*y^2, 3*x^2 + 3*x*y + 6*y^2, 4*x^2 + x*y + 4*y^2]

    The number of inequivalent reduced binary forms with a fixed negative
    fundamental discriminant D is the class number of the quadratic field
    `Q(\sqrt{D})`::

        sage: len(BinaryQF_reduced_representatives(-13*4))
        2
        sage: QuadraticField(-13*4, 'a').class_number()
        2
        sage: p=next_prime(2^20); p
        1048583
        sage: len(BinaryQF_reduced_representatives(-p))
        689
        sage: QuadraticField(-p, 'a').class_number()
        689

        sage: BinaryQF_reduced_representatives(-23*9)
        [x^2 + x*y + 52*y^2,
        2*x^2 - x*y + 26*y^2,
        2*x^2 + x*y + 26*y^2,
        3*x^2 + 3*x*y + 18*y^2,
        4*x^2 - x*y + 13*y^2,
        4*x^2 + x*y + 13*y^2,
        6*x^2 - 3*x*y + 9*y^2,
        6*x^2 + 3*x*y + 9*y^2,
        8*x^2 + 7*x*y + 8*y^2]
        sage: BinaryQF_reduced_representatives(-23*9, primitive_only=True)
        [x^2 + x*y + 52*y^2,
        2*x^2 - x*y + 26*y^2,
        2*x^2 + x*y + 26*y^2,
        4*x^2 - x*y + 13*y^2,
        4*x^2 + x*y + 13*y^2,
        8*x^2 + 7*x*y + 8*y^2]

    TESTS::

        sage: BinaryQF_reduced_representatives(5)
        Traceback (most recent call last):
        ...
        ValueError: discriminant must be negative and congruent to 0 or 1 modulo 4
    """
    D = ZZ(D)
    if not ( D < 0 and (D % 4 in [0,1])):
        raise ValueError, "discriminant must be negative and congruent to 0 or 1 modulo 4"

    # For a fundamental discriminant all forms are primitive so we need not check:
    if primitive_only:
        primitive_only = not is_fundamental_discriminant(D)

    form_list = []

    from sage.misc.all import xsrange
    from sage.rings.arith import gcd

    # Only iterate over positive a and over b of the same
    # parity as D such that 4a^2 + D <= b^2 <= a^2
    for a in xsrange(1,1+((-D)//3).isqrt()):
        a4 = 4*a
        s = D + a*a4
        w = 1+(s-1).isqrt() if s > 0 else 0
        if w%2 != D%2: w += 1
        for b in xsrange(w,a+1,2):
            t = b*b-D
            if t % a4 == 0:
                c = t // a4
                if (not primitive_only) or gcd([a,b,c])==1:
                    if b>0 and a>b and c>a:
                        form_list.append(BinaryQF([a,-b,c]))
                    form_list.append(BinaryQF([a,b,c]))

    form_list.sort()
    return form_list
Ejemplo n.º 7
0
def BinaryQF_reduced_representatives(D, primitive_only=False):
    r"""
    Return representatives for the classes of binary quadratic forms
    of discriminant `D`.

    INPUT:

    - ``D`` -- (integer) a discriminant

    - ``primitive_only`` -- (boolean, default True): if True, only
      return primitive forms.

    OUTPUT:

    (list) A lexicographically-ordered list of inequivalent reduced
    representatives for the equivalence classes of binary quadratic
    forms of discriminant `D`.  If ``primitive_only`` is ``True`` then
    imprimitive forms (which only exist when `D` is not fundamental) are
    omitted; otherwise they are included.

    EXAMPLES::

        sage: BinaryQF_reduced_representatives(-4)
        [x^2 + y^2]

        sage: BinaryQF_reduced_representatives(-163)
        [x^2 + x*y + 41*y^2]

        sage: BinaryQF_reduced_representatives(-12)
        [x^2 + 3*y^2, 2*x^2 + 2*x*y + 2*y^2]

        sage: BinaryQF_reduced_representatives(-16)
        [x^2 + 4*y^2, 2*x^2 + 2*y^2]

        sage: BinaryQF_reduced_representatives(-63)
        [x^2 + x*y + 16*y^2, 2*x^2 - x*y + 8*y^2, 2*x^2 + x*y + 8*y^2, 3*x^2 + 3*x*y + 6*y^2, 4*x^2 + x*y + 4*y^2]

    The number of inequivalent reduced binary forms with a fixed negative
    fundamental discriminant D is the class number of the quadratic field
    `\QQ(\sqrt{D})`::

        sage: len(BinaryQF_reduced_representatives(-13*4))
        2
        sage: QuadraticField(-13*4, 'a').class_number()
        2
        sage: p=next_prime(2^20); p
        1048583
        sage: len(BinaryQF_reduced_representatives(-p))
        689
        sage: QuadraticField(-p, 'a').class_number()
        689

        sage: BinaryQF_reduced_representatives(-23*9)
        [x^2 + x*y + 52*y^2,
        2*x^2 - x*y + 26*y^2,
        2*x^2 + x*y + 26*y^2,
        3*x^2 + 3*x*y + 18*y^2,
        4*x^2 - x*y + 13*y^2,
        4*x^2 + x*y + 13*y^2,
        6*x^2 - 3*x*y + 9*y^2,
        6*x^2 + 3*x*y + 9*y^2,
        8*x^2 + 7*x*y + 8*y^2]
        sage: BinaryQF_reduced_representatives(-23*9, primitive_only=True)
        [x^2 + x*y + 52*y^2,
        2*x^2 - x*y + 26*y^2,
        2*x^2 + x*y + 26*y^2,
        4*x^2 - x*y + 13*y^2,
        4*x^2 + x*y + 13*y^2,
        8*x^2 + 7*x*y + 8*y^2]

    TESTS::

        sage: BinaryQF_reduced_representatives(73)
        [-6*x^2 + 5*x*y + 2*y^2,
        -6*x^2 + 7*x*y + y^2,
        -4*x^2 + 3*x*y + 4*y^2,
        -4*x^2 + 3*x*y + 4*y^2,
        -4*x^2 + 5*x*y + 3*y^2,
        -3*x^2 + 5*x*y + 4*y^2,
        -3*x^2 + 7*x*y + 2*y^2,
        -2*x^2 + 5*x*y + 6*y^2,
        -2*x^2 + 7*x*y + 3*y^2,
        -x^2 + 7*x*y + 6*y^2,
        x^2 + 7*x*y - 6*y^2,
        2*x^2 + 5*x*y - 6*y^2,
        2*x^2 + 7*x*y - 3*y^2,
        3*x^2 + 5*x*y - 4*y^2,
        3*x^2 + 7*x*y - 2*y^2,
        4*x^2 + 3*x*y - 4*y^2,
        4*x^2 + 3*x*y - 4*y^2,
        4*x^2 + 5*x*y - 3*y^2,
        6*x^2 + 5*x*y - 2*y^2,
        6*x^2 + 7*x*y - y^2]
        sage: BinaryQF_reduced_representatives(76, primitive_only=True)
        [-5*x^2 + 4*x*y + 3*y^2,
        -5*x^2 + 6*x*y + 2*y^2,
         -3*x^2 + 4*x*y + 5*y^2,
         -3*x^2 + 8*x*y + y^2,
         -2*x^2 + 6*x*y + 5*y^2,
         -x^2 + 8*x*y + 3*y^2,
         x^2 + 8*x*y - 3*y^2,
         2*x^2 + 6*x*y - 5*y^2,
         3*x^2 + 4*x*y - 5*y^2,
         3*x^2 + 8*x*y - y^2,
         5*x^2 + 4*x*y - 3*y^2,
         5*x^2 + 6*x*y - 2*y^2]

    Check that the primitive_only keyword does something::

        sage: BinaryQF_reduced_representatives(4*5, primitive_only=True)
        [-x^2 + 4*x*y + y^2,
        -x^2 + 4*x*y + y^2,
        x^2 + 4*x*y - y^2,
        x^2 + 4*x*y - y^2]
        sage: BinaryQF_reduced_representatives(4*5, primitive_only=False)
        [-2*x^2 + 2*x*y + 2*y^2,
        -2*x^2 + 2*x*y + 2*y^2,
        -x^2 + 4*x*y + y^2,
        -x^2 + 4*x*y + y^2,
        x^2 + 4*x*y - y^2,
        x^2 + 4*x*y - y^2,
        2*x^2 + 2*x*y - 2*y^2,
        2*x^2 + 2*x*y - 2*y^2]
    """
    D = ZZ(D)

    # For a fundamental discriminant all forms are primitive so we need not check:
    if primitive_only:
        primitive_only = not is_fundamental_discriminant(D)

    form_list = []

    from sage.arith.srange import xsrange

    D4 = D % 4
    if D4 == 2 or D4 == 3:
        raise ValueError("%s is not a discriminant" % D)
    if D > 0:  # Indefinite
        # We follow the description of Buchmann/Vollmer 6.7.1
        if D.is_square():
            # Buchmann/Vollmer 6.7.1. require D squarefree.
            raise ValueError("%s is a square" % D)
        sqrt_d = D.sqrt(prec=53)
        for b in xsrange(1, sqrt_d.floor() + 1):
            if (D - b) % 2 != 0:
                continue
            A = (D - b**2) / 4
            Low_a = ((sqrt_d - b) / 2).ceil()
            High_a = (A.sqrt(prec=53)).floor()
            for a in xsrange(Low_a, High_a + 1):
                if a == 0:
                    continue
                c = -A / a
                if c in ZZ:
                    if (not primitive_only) or gcd([a, b, c]) == 1:
                        Q = BinaryQF(a, b, c)
                        Q1 = BinaryQF(-a, b, -c)
                        Q2 = BinaryQF(c, b, a)
                        Q3 = BinaryQF(-c, b, -a)
                        form_list.append(Q)
                        form_list.append(Q1)
                        form_list.append(Q2)
                        form_list.append(Q3)
    else:  # Definite
        # Only iterate over positive a and over b of the same
        # parity as D such that 4a^2 + D <= b^2 <= a^2
        for a in xsrange(1, 1 + ((-D) // 3).isqrt()):
            a4 = 4 * a
            s = D + a * a4
            w = 1 + (s - 1).isqrt() if s > 0 else 0
            if w % 2 != D % 2: w += 1
            for b in xsrange(w, a + 1, 2):
                t = b * b - D
                if t % a4 == 0:
                    c = t // a4
                    if (not primitive_only) or gcd([a, b, c]) == 1:
                        if b > 0 and a > b and c > a:
                            form_list.append(BinaryQF([a, -b, c]))
                        form_list.append(BinaryQF([a, b, c]))

    form_list.sort()
    return form_list
Ejemplo n.º 8
0
def BinaryQF_reduced_representatives(D, primitive_only=False):
    r"""
    Returns a list of inequivalent reduced representatives for the
    equivalence classes of positive definite binary forms of
    discriminant D.

    INPUT:

    - `D` -- (integer) A negative discriminant.

    - ``primitive_only`` -- (bool, default False) flag controlling whether only
      primitive forms are included.

    OUTPUT:

    (list) A lexicographically-ordered list of inequivalent reduced
    representatives for the equivalence classes of positive definite binary
    forms of discriminant `D`.  If ``primitive_only`` is ``True`` then
    imprimitive forms (which only exist when `D` is not fundamental) are
    omitted; otherwise they are included.

    EXAMPLES::

        sage: BinaryQF_reduced_representatives(-4)
        [x^2 + y^2]

        sage: BinaryQF_reduced_representatives(-163)
        [x^2 + x*y + 41*y^2]

        sage: BinaryQF_reduced_representatives(-12)
        [x^2 + 3*y^2, 2*x^2 + 2*x*y + 2*y^2]

        sage: BinaryQF_reduced_representatives(-16)
        [x^2 + 4*y^2, 2*x^2 + 2*y^2]

        sage: BinaryQF_reduced_representatives(-63)
        [x^2 + x*y + 16*y^2, 2*x^2 - x*y + 8*y^2, 2*x^2 + x*y + 8*y^2, 3*x^2 + 3*x*y + 6*y^2, 4*x^2 + x*y + 4*y^2]

    The number of inequivalent reduced binary forms with a fixed negative
    fundamental discriminant D is the class number of the quadratic field
    `Q(\sqrt{D})`::

        sage: len(BinaryQF_reduced_representatives(-13*4))
        2
        sage: QuadraticField(-13*4, 'a').class_number()
        2
        sage: p=next_prime(2^20); p
        1048583
        sage: len(BinaryQF_reduced_representatives(-p))
        689
        sage: QuadraticField(-p, 'a').class_number()
        689

        sage: BinaryQF_reduced_representatives(-23*9)
        [x^2 + x*y + 52*y^2,
        2*x^2 - x*y + 26*y^2,
        2*x^2 + x*y + 26*y^2,
        3*x^2 + 3*x*y + 18*y^2,
        4*x^2 - x*y + 13*y^2,
        4*x^2 + x*y + 13*y^2,
        6*x^2 - 3*x*y + 9*y^2,
        6*x^2 + 3*x*y + 9*y^2,
        8*x^2 + 7*x*y + 8*y^2]
        sage: BinaryQF_reduced_representatives(-23*9, primitive_only=True)
        [x^2 + x*y + 52*y^2,
        2*x^2 - x*y + 26*y^2,
        2*x^2 + x*y + 26*y^2,
        4*x^2 - x*y + 13*y^2,
        4*x^2 + x*y + 13*y^2,
        8*x^2 + 7*x*y + 8*y^2]

    TESTS::

        sage: BinaryQF_reduced_representatives(5)
        Traceback (most recent call last):
        ...
        ValueError: discriminant must be negative and congruent to 0 or 1 modulo 4
    """
    D = ZZ(D)
    if not (D < 0 and (D % 4 in [0, 1])):
        raise ValueError, "discriminant must be negative and congruent to 0 or 1 modulo 4"

    # For a fundamental discriminant all forms are primitive so we need not check:
    if primitive_only:
        primitive_only = not is_fundamental_discriminant(D)

    form_list = []

    from sage.misc.all import xsrange
    from sage.rings.arith import gcd

    # Only iterate over positive a and over b of the same
    # parity as D such that 4a^2 + D <= b^2 <= a^2
    for a in xsrange(1, 1 + ((-D) // 3).isqrt()):
        a4 = 4 * a
        s = D + a * a4
        w = 1 + (s - 1).isqrt() if s > 0 else 0
        if w % 2 != D % 2: w += 1
        for b in xsrange(w, a + 1, 2):
            t = b * b - D
            if t % a4 == 0:
                c = t // a4
                if (not primitive_only) or gcd([a, b, c]) == 1:
                    if b > 0 and a > b and c > a:
                        form_list.append(BinaryQF([a, -b, c]))
                    form_list.append(BinaryQF([a, b, c]))

    form_list.sort()
    return form_list