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