Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/quadratic_forms/binary_qf.py
4045 views
1
r"""
2
Binary Quadratic Forms with Integer Coefficients
3
4
This module provides a specialized class for working with a binary quadratic
5
form `a x^2 + b x y + c y^2`, stored as a triple of integers `(a, b, c)`.
6
7
EXAMPLES::
8
9
sage: Q = BinaryQF([1,2,3])
10
sage: Q
11
x^2 + 2*x*y + 3*y^2
12
sage: Q.discriminant()
13
-8
14
sage: Q.reduced_form()
15
x^2 + 2*y^2
16
sage: Q(1, 1)
17
6
18
19
TESTS::
20
21
sage: Q == loads(dumps(Q))
22
True
23
24
AUTHORS:
25
26
- Jon Hanke (2006-08-08):
27
28
- Appended to add the methods :func:`BinaryQF_reduced_representatives`,
29
:meth:`~BinaryQF.is_reduced`, and ``__add__`` on 8-3-2006 for Coding Sprint
30
#2.
31
- Added Documentation and :meth:`~BinaryQF.complex_point` method on 8-8-2006.
32
33
- Nick Alexander: add doctests and clean code for Doc Days 2
34
- William Stein (2009-08-05): composition; some ReSTification.
35
- William Stein (2009-09-18): make immutable.
36
"""
37
38
#*****************************************************************************
39
# Copyright (C) 2006--2009 William Stein and Jon Hanke
40
#
41
# Distributed under the terms of the GNU General Public License (GPL)
42
#
43
# This code is distributed in the hope that it will be useful,
44
# but WITHOUT ANY WARRANTY; without even the implied warranty of
45
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
46
# General Public License for more details.
47
#
48
# The full text of the GPL is available at:
49
#
50
# http://www.gnu.org/licenses/
51
#*****************************************************************************
52
53
from sage.libs.pari.all import pari
54
from sage.rings.all import (is_fundamental_discriminant, ZZ, divisors)
55
from sage.structure.sage_object import SageObject
56
from sage.misc.cachefunc import cached_method
57
58
class BinaryQF(SageObject):
59
"""
60
A binary quadratic form over `\ZZ`.
61
62
INPUT:
63
64
- `v` -- a list or tuple of 3 entries: [a,b,c], or a quadratic homogeneous
65
polynomial in two variables with integer coefficients
66
67
68
OUTPUT:
69
70
the binary quadratic form a*x^2 + b*x*y + c*y^2.
71
72
EXAMPLES::
73
74
sage: b = BinaryQF([1,2,3])
75
sage: b.discriminant()
76
-8
77
sage: R.<x, y> = ZZ[]
78
sage: BinaryQF(x^2 + 2*x*y + 3*y^2) == b
79
True
80
"""
81
# Initializes the form with a 3-element list
82
def __init__(self, abc):
83
r"""
84
Creates the binary quadratic form `ax^2 + bxy + cy^2` from the
85
triple [a,b,c] over `\ZZ` or from a polynomial.
86
87
INPUT:
88
89
- ``abc`` -- 3-tuple of integers, or a quadratic homogeneous polynomial
90
in two variables with integer coefficients
91
92
EXAMPLES::
93
94
sage: Q = BinaryQF([1,2,3]); Q
95
x^2 + 2*x*y + 3*y^2
96
sage: Q = BinaryQF([1,2])
97
Traceback (most recent call last):
98
...
99
TypeError: Binary quadratic form must be given by a list of three coefficients
100
101
sage: R.<x, y> = ZZ[]
102
sage: f = x^2 + 2*x*y + 3*y^2
103
sage: BinaryQF(f)
104
x^2 + 2*x*y + 3*y^2
105
sage: BinaryQF(f + x)
106
Traceback (most recent call last):
107
...
108
TypeError: Binary quadratic form must be given by a quadratic homogeneous bivariate integer polynomial
109
110
TESTS::
111
112
sage: BinaryQF(0)
113
0
114
"""
115
if isinstance(abc, (list, tuple)):
116
if len(abc) != 3:
117
# Check we have three coefficients
118
raise TypeError, "Binary quadratic form must be given by a list of three coefficients"
119
self._a, self._b, self._c = [ZZ(x) for x in abc]
120
else:
121
f = abc
122
from sage.rings.polynomial.multi_polynomial_element import is_MPolynomial
123
if f.is_zero():
124
self._a, self._b, self._c = [ZZ(0), ZZ(0), ZZ(0)]
125
elif (is_MPolynomial(f) and f.is_homogeneous() and f.base_ring() == ZZ
126
and f.degree() == 2 and f.parent().ngens() == 2):
127
x, y = f.parent().gens()
128
self._a, self._b, self._c = [f.monomial_coefficient(mon) for mon in [x**2, x*y, y**2]]
129
else:
130
raise TypeError, "Binary quadratic form must be given by a quadratic homogeneous bivariate integer polynomial"
131
132
def _pari_init_(self):
133
"""
134
Used to convert this quadratic form to Pari.
135
136
EXAMPLES::
137
138
sage: f = BinaryQF([2,3,4]); f
139
2*x^2 + 3*x*y + 4*y^2
140
sage: f._pari_init_()
141
'Qfb(2,3,4)'
142
sage: pari(f)
143
Qfb(2, 3, 4)
144
sage: type(pari(f))
145
<type 'sage.libs.pari.gen.gen'>
146
sage: gp(f)
147
Qfb(2, 3, 4)
148
sage: type(gp(f))
149
<class 'sage.interfaces.gp.GpElement'>
150
"""
151
return 'Qfb(%s,%s,%s)'%(self._a,self._b,self._c)
152
153
def __mul__(self, right):
154
"""
155
Gauss composition of binary quadratic forms. The result is
156
not reduced.
157
158
EXAMPLES:
159
160
We explicitly compute in the group of classes of positive
161
definite binary quadratic forms of discriminant -23.
162
163
::
164
165
sage: R = BinaryQF_reduced_representatives(-23); R
166
[x^2 + x*y + 6*y^2, 2*x^2 - x*y + 3*y^2, 2*x^2 + x*y + 3*y^2]
167
sage: R[0] * R[0]
168
x^2 + x*y + 6*y^2
169
sage: R[1] * R[1]
170
4*x^2 + 3*x*y + 2*y^2
171
sage: (R[1] * R[1]).reduced_form()
172
2*x^2 + x*y + 3*y^2
173
sage: (R[1] * R[1] * R[1]).reduced_form()
174
x^2 + x*y + 6*y^2
175
176
"""
177
if not isinstance(right, BinaryQF):
178
raise TypeError, "both self and right must be binary quadratic forms"
179
# There could be more elegant ways, but qfbcompraw isn't
180
# wrapped yet in the PARI C library. We may as well settle
181
# for the below, until somebody simply implements composition
182
# from scratch in Cython.
183
v = list(pari('qfbcompraw(%s,%s)'%(self._pari_init_(),
184
right._pari_init_())))
185
return BinaryQF(v)
186
187
def __getitem__(self, n):
188
"""
189
Return the n-th component of this quadratic form.
190
191
If this form is `a x^2 + b x y + c y^2`, the 0-th component is `a`,
192
the 1-st component is `b`, and `2`-nd component is `c`.
193
194
Indexing is like lists -- negative indices and slices are allowed.
195
196
EXAMPLES::
197
198
199
sage: Q = BinaryQF([2,3,4])
200
sage: Q[0]
201
2
202
sage: Q[2]
203
4
204
sage: Q[:2]
205
(2, 3)
206
sage: tuple(Q)
207
(2, 3, 4)
208
sage: list(Q)
209
[2, 3, 4]
210
"""
211
return (self._a, self._b, self._c)[n]
212
213
def __call__(self, *args):
214
r"""
215
Evaluate this quadratic form at a point.
216
217
INPUT:
218
219
- args -- x and y values, as a pair x, y or a list, tuple, or
220
vector
221
222
EXAMPLES::
223
224
225
sage: Q = BinaryQF([2, 3, 4])
226
sage: Q(1, 2)
227
24
228
229
TESTS::
230
231
sage: Q = BinaryQF([2, 3, 4])
232
sage: Q([1, 2])
233
24
234
sage: Q((1, 2))
235
24
236
sage: Q(vector([1, 2]))
237
24
238
"""
239
if len(args) == 1:
240
args = args[0]
241
x, y = args
242
return (self._a * x + self._b * y) * x + self._c * y**2
243
244
def __cmp__(self, right):
245
"""
246
Returns True if self and right are identical: the same coefficients.
247
248
EXAMPLES::
249
250
251
sage: P = BinaryQF([2,2,3])
252
sage: Q = BinaryQF([2,2,3])
253
sage: R = BinaryQF([1,2,3])
254
sage: P == Q # indirect doctest
255
True
256
sage: P == R # indirect doctest
257
False
258
259
TESTS::
260
261
sage: P == P
262
True
263
sage: Q == P
264
True
265
sage: R == P
266
False
267
sage: P == 2
268
False
269
"""
270
if not isinstance(right, BinaryQF):
271
return cmp(type(self), type(right))
272
return cmp((self._a,self._b,self._c), (right._a,right._b,right._c))
273
274
def __add__(self, Q):
275
"""
276
Returns the component-wise sum of two forms.
277
278
That is, given `a_1 x^2 + b_1 x y + c_1 y^2` and `a_2 x^2 + b_2 x y +
279
c_2 y^2`, returns the form
280
`(a_1 + a_2) x^2 + (b_1 + b_2) x y + (c_1 + c_2) y^2.`
281
282
EXAMPLES::
283
284
285
sage: P = BinaryQF([2,2,3]); P
286
2*x^2 + 2*x*y + 3*y^2
287
sage: Q = BinaryQF([-1,2,2]); Q
288
-x^2 + 2*x*y + 2*y^2
289
sage: P + Q
290
x^2 + 4*x*y + 5*y^2
291
sage: P + Q == BinaryQF([1,4,5]) # indirect doctest
292
True
293
294
TESTS::
295
296
sage: Q + P == BinaryQF([1,4,5]) # indirect doctest
297
True
298
"""
299
return BinaryQF([self._a + Q._a, self._b + Q._b, self._c + Q._c])
300
301
def __sub__(self, Q):
302
"""
303
Returns the component-wise difference of two forms.
304
305
That is, given `a_1 x^2 + b_1 x y + c_1 y^2` and `a_2 x^2 + b_2 x y +
306
c_2 y^2`, returns the form
307
`(a_1 - a_2) x^2 + (b_1 - b_2) x y + (c_1 - c_2) y^2.`
308
309
EXAMPLES::
310
311
312
sage: P = BinaryQF([2,2,3]); P
313
2*x^2 + 2*x*y + 3*y^2
314
sage: Q = BinaryQF([-1,2,2]); Q
315
-x^2 + 2*x*y + 2*y^2
316
sage: P - Q
317
3*x^2 + y^2
318
sage: P - Q == BinaryQF([3,0,1]) # indirect doctest
319
True
320
321
TESTS::
322
323
sage: Q - P == BinaryQF([3,0,1]) # indirect doctest
324
False
325
sage: Q - P != BinaryQF([3,0,1]) # indirect doctest
326
True
327
"""
328
return BinaryQF([self._a - Q._a, self._b - Q._b, self._c - Q._c])
329
330
def _repr_(self):
331
"""
332
Display the quadratic form.
333
334
EXAMPLES::
335
336
337
sage: Q = BinaryQF([1,2,3]); Q # indirect doctest
338
x^2 + 2*x*y + 3*y^2
339
340
sage: Q = BinaryQF([-1,2,3]); Q
341
-x^2 + 2*x*y + 3*y^2
342
343
sage: Q = BinaryQF([0,0,0]); Q
344
0
345
"""
346
return repr(self.polynomial())
347
348
def _latex_(self):
349
"""
350
Return latex representation of this binary quadratic form.
351
352
EXAMPLES::
353
354
sage: f = BinaryQF((778,1115,400)); f
355
778*x^2 + 1115*x*y + 400*y^2
356
sage: latex(f) # indirect doctest
357
778 x^{2} + 1115 x y + 400 y^{2}
358
"""
359
return self.polynomial()._latex_()
360
361
@cached_method
362
def polynomial(self):
363
"""
364
Returns the binary quadratic form as a homogeneous 2-variable
365
polynomial.
366
367
EXAMPLES::
368
369
sage: Q = BinaryQF([1,2,3])
370
sage: Q.polynomial()
371
x^2 + 2*x*y + 3*y^2
372
373
sage: Q = BinaryQF([-1,-2,3])
374
sage: Q.polynomial()
375
-x^2 - 2*x*y + 3*y^2
376
377
sage: Q = BinaryQF([0,0,0])
378
sage: Q.polynomial()
379
0
380
"""
381
return self(ZZ['x, y'].gens())
382
383
@cached_method
384
def discriminant(self):
385
"""
386
Returns the discriminant `b^2 - 4ac` of the binary
387
form `ax^2 + bxy + cy^2`.
388
389
EXAMPLES::
390
391
392
sage: Q = BinaryQF([1,2,3])
393
sage: Q.discriminant()
394
-8
395
"""
396
return self._b**2 - 4 * self._a * self._c
397
398
@cached_method
399
def has_fundamental_discriminant(self):
400
"""
401
Checks if the discriminant D of this form is a fundamental
402
discriminant (i.e. D is the smallest element of its
403
squareclass with D = 0 or 1 mod 4).
404
405
EXAMPLES::
406
407
sage: Q = BinaryQF([1,0,1])
408
sage: Q.discriminant()
409
-4
410
sage: Q.has_fundamental_discriminant()
411
True
412
413
sage: Q = BinaryQF([2,0,2])
414
sage: Q.discriminant()
415
-16
416
sage: Q.has_fundamental_discriminant()
417
False
418
"""
419
return is_fundamental_discriminant(self.discriminant())
420
421
@cached_method
422
def is_primitive(self):
423
"""
424
Checks if the form `ax^2 + bxy + cy^2` satisfies
425
`\gcd(a,b,c)=1`, i.e., is primitive.
426
427
EXAMPLES::
428
429
430
sage: Q = BinaryQF([6,3,9])
431
sage: Q.is_primitive()
432
False
433
434
sage: Q = BinaryQF([1,1,1])
435
sage: Q.is_primitive()
436
True
437
438
sage: Q = BinaryQF([2,2,2])
439
sage: Q.is_primitive()
440
False
441
442
sage: rqf = BinaryQF_reduced_representatives(-23*9)
443
sage: [qf.is_primitive() for qf in rqf]
444
[True, True, True, False, True, True, False, False, True]
445
sage: rqf
446
[x^2 + x*y + 52*y^2,
447
2*x^2 - x*y + 26*y^2,
448
2*x^2 + x*y + 26*y^2,
449
3*x^2 + 3*x*y + 18*y^2,
450
4*x^2 - x*y + 13*y^2,
451
4*x^2 + x*y + 13*y^2,
452
6*x^2 - 3*x*y + 9*y^2,
453
6*x^2 + 3*x*y + 9*y^2,
454
8*x^2 + 7*x*y + 8*y^2]
455
sage: [qf for qf in rqf if qf.is_primitive()]
456
[x^2 + x*y + 52*y^2,
457
2*x^2 - x*y + 26*y^2,
458
2*x^2 + x*y + 26*y^2,
459
4*x^2 - x*y + 13*y^2,
460
4*x^2 + x*y + 13*y^2,
461
8*x^2 + 7*x*y + 8*y^2]
462
"""
463
from sage.rings.arith import gcd
464
return gcd([self._a, self._b, self._c])==1
465
466
@cached_method
467
def is_weakly_reduced(self):
468
"""
469
Checks if the form `ax^2 + bxy + cy^2` satisfies
470
`|b| \leq a \leq c`, i.e., is weakly reduced.
471
472
EXAMPLES::
473
474
475
sage: Q = BinaryQF([1,2,3])
476
sage: Q.is_weakly_reduced()
477
False
478
479
sage: Q = BinaryQF([2,1,3])
480
sage: Q.is_weakly_reduced()
481
True
482
483
sage: Q = BinaryQF([1,-1,1])
484
sage: Q.is_weakly_reduced()
485
True
486
"""
487
if self.discriminant() >= 0:
488
raise NotImplementedError, "only implemented for negative discriminants"
489
return (abs(self._b) <= self._a) and (self._a <= self._c)
490
491
@cached_method
492
def reduced_form(self):
493
"""
494
Return the unique reduced form equivalent to ``self``. See also
495
:meth:`~is_reduced`.
496
497
EXAMPLES::
498
499
sage: a = BinaryQF([33,11,5])
500
sage: a.is_reduced()
501
False
502
sage: b = a.reduced_form(); b
503
5*x^2 - x*y + 27*y^2
504
sage: b.is_reduced()
505
True
506
507
sage: a = BinaryQF([15,0,15])
508
sage: a.is_reduced()
509
True
510
sage: b = a.reduced_form(); b
511
15*x^2 + 15*y^2
512
sage: b.is_reduced()
513
True
514
"""
515
if self.discriminant() >= 0 or self._a < 0:
516
raise NotImplementedError, "only implemented for positive definite forms"
517
if not self.is_reduced():
518
v = list(pari('Vec(qfbred(Qfb(%s,%s,%s)))'%(self._a,self._b,self._c)))
519
return BinaryQF(v)
520
else:
521
return self
522
523
def is_equivalent(self, right):
524
"""
525
Return true if self and right are equivalent, i.e., have the
526
same reduced form.
527
528
INPUT:
529
530
- ``right`` -- a binary quadratic form
531
532
EXAMPLES::
533
534
sage: a = BinaryQF([33,11,5])
535
sage: b = a.reduced_form(); b
536
5*x^2 - x*y + 27*y^2
537
sage: a.is_equivalent(b)
538
True
539
sage: a.is_equivalent(BinaryQF((3,4,5)))
540
False
541
"""
542
if not isinstance(right, BinaryQF):
543
raise TypeError, "right must be a binary quadratic form"
544
return self.reduced_form() == right.reduced_form()
545
546
@cached_method
547
def is_reduced(self):
548
"""
549
Checks if the quadratic form is reduced, i.e., if the form
550
`ax^2 + bxy + cy^2` satisfies `|b|\leq a \leq c`, and
551
that `b\geq 0` if either `a = b` or `a = c`.
552
553
EXAMPLES::
554
555
sage: Q = BinaryQF([1,2,3])
556
sage: Q.is_reduced()
557
False
558
559
sage: Q = BinaryQF([2,1,3])
560
sage: Q.is_reduced()
561
True
562
563
sage: Q = BinaryQF([1,-1,1])
564
sage: Q.is_reduced()
565
False
566
567
sage: Q = BinaryQF([1,1,1])
568
sage: Q.is_reduced()
569
True
570
"""
571
return (-self._a < self._b <= self._a < self._c) or \
572
(ZZ(0) <= self._b <= self._a == self._c)
573
574
def complex_point(self):
575
r"""
576
Returns the point in the complex upper half-plane associated
577
to this (positive definite) quadratic form.
578
579
For positive definite forms with negative discriminants, this is a
580
root `\tau` of `a x^2 + b x + c` with the imaginary part of `\tau`
581
greater than 0.
582
583
EXAMPLES::
584
585
sage: Q = BinaryQF([1,0,1])
586
sage: Q.complex_point()
587
1.00000000000000*I
588
"""
589
if self.discriminant() >= 0:
590
raise NotImplementedError, "only implemented for negative discriminant"
591
R = ZZ['x']
592
x = R.gen()
593
Q1 = R(self.polynomial()(x,1))
594
return [z for z in Q1.complex_roots() if z.imag() > 0][0]
595
596
def matrix_action_left(self, M):
597
r"""
598
Return the binary quadratic form resulting from the left action
599
of the 2-by-2 matrix ``M`` on the quadratic form ``self``.
600
601
Here the action of the matrix `M = \begin{pmatrix} a & b \\ c & d
602
\end{pmatrix}` on the form `Q(x, y)` produces the form `Q(ax+by,
603
cx+dy)`.
604
605
EXAMPLES::
606
607
sage: Q = BinaryQF([2, 1, 3]); Q
608
2*x^2 + x*y + 3*y^2
609
sage: M = matrix(ZZ, [[1, 2], [3, 5]])
610
sage: Q.matrix_action_left(M)
611
16*x^2 + 83*x*y + 108*y^2
612
"""
613
v, w = M.rows()
614
a1 = self(v)
615
c1 = self(w)
616
b1 = self(v + w) - a1 - c1
617
return BinaryQF([a1, b1, c1])
618
619
def matrix_action_right(self, M):
620
r"""
621
Return the binary quadratic form resulting from the right action
622
of the 2-by-2 matrix ``M`` on the quadratic form ``self``.
623
624
Here the action of the matrix `M = \begin{pmatrix} a & b \\ c & d
625
\end{pmatrix}` on the form `Q(x, y)` produces the form `Q(ax+cy,
626
bx+dy)`.
627
628
EXAMPLES::
629
630
sage: Q = BinaryQF([2, 1, 3]); Q
631
2*x^2 + x*y + 3*y^2
632
sage: M = matrix(ZZ, [[1, 2], [3, 5]])
633
sage: Q.matrix_action_right(M)
634
32*x^2 + 109*x*y + 93*y^2
635
"""
636
v, w = M.columns()
637
a1 = self(v)
638
c1 = self(w)
639
b1 = self(v + w) - a1 - c1
640
return BinaryQF([a1, b1, c1])
641
642
def BinaryQF_reduced_representatives(D, primitive_only=False):
643
r"""
644
Returns a list of inequivalent reduced representatives for the
645
equivalence classes of positive definite binary forms of
646
discriminant D.
647
648
INPUT:
649
650
- `D` -- (integer) A negative discriminant.
651
652
- ``primitive_only`` -- (bool, default False) flag controlling whether only
653
primitive forms are included.
654
655
OUTPUT:
656
657
(list) A lexicographically-ordered list of inequivalent reduced
658
representatives for the equivalence classes of positive definite binary
659
forms of discriminant `D`. If ``primitive_only`` is ``True`` then
660
imprimitive forms (which only exist when `D` is not fundamental) are
661
omitted; otherwise they are included.
662
663
EXAMPLES::
664
665
sage: BinaryQF_reduced_representatives(-4)
666
[x^2 + y^2]
667
668
sage: BinaryQF_reduced_representatives(-163)
669
[x^2 + x*y + 41*y^2]
670
671
sage: BinaryQF_reduced_representatives(-12)
672
[x^2 + 3*y^2, 2*x^2 + 2*x*y + 2*y^2]
673
674
sage: BinaryQF_reduced_representatives(-16)
675
[x^2 + 4*y^2, 2*x^2 + 2*y^2]
676
677
sage: BinaryQF_reduced_representatives(-63)
678
[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]
679
680
The number of inequivalent reduced binary forms with a fixed negative
681
fundamental discriminant D is the class number of the quadratic field
682
`Q(\sqrt{D})`::
683
684
sage: len(BinaryQF_reduced_representatives(-13*4))
685
2
686
sage: QuadraticField(-13*4, 'a').class_number()
687
2
688
sage: p=next_prime(2^20); p
689
1048583
690
sage: len(BinaryQF_reduced_representatives(-p))
691
689
692
sage: QuadraticField(-p, 'a').class_number()
693
689
694
695
sage: BinaryQF_reduced_representatives(-23*9)
696
[x^2 + x*y + 52*y^2,
697
2*x^2 - x*y + 26*y^2,
698
2*x^2 + x*y + 26*y^2,
699
3*x^2 + 3*x*y + 18*y^2,
700
4*x^2 - x*y + 13*y^2,
701
4*x^2 + x*y + 13*y^2,
702
6*x^2 - 3*x*y + 9*y^2,
703
6*x^2 + 3*x*y + 9*y^2,
704
8*x^2 + 7*x*y + 8*y^2]
705
sage: BinaryQF_reduced_representatives(-23*9, primitive_only=True)
706
[x^2 + x*y + 52*y^2,
707
2*x^2 - x*y + 26*y^2,
708
2*x^2 + x*y + 26*y^2,
709
4*x^2 - x*y + 13*y^2,
710
4*x^2 + x*y + 13*y^2,
711
8*x^2 + 7*x*y + 8*y^2]
712
713
TESTS::
714
715
sage: BinaryQF_reduced_representatives(5)
716
Traceback (most recent call last):
717
...
718
ValueError: discriminant must be negative and congruent to 0 or 1 modulo 4
719
"""
720
D = ZZ(D)
721
if not ( D < 0 and (D % 4 in [0,1])):
722
raise ValueError, "discriminant must be negative and congruent to 0 or 1 modulo 4"
723
724
# For a fundamental discriminant all forms are primitive so we need not check:
725
if primitive_only:
726
primitive_only = not is_fundamental_discriminant(D)
727
728
form_list = []
729
730
from sage.misc.all import xsrange
731
from sage.rings.arith import gcd
732
733
# Only iterate over positive a and over b of the same
734
# parity as D such that 4a^2 + D <= b^2 <= a^2
735
for a in xsrange(1,1+((-D)//3).isqrt()):
736
a4 = 4*a
737
s = D + a*a4
738
w = 1+(s-1).isqrt() if s > 0 else 0
739
if w%2 != D%2: w += 1
740
for b in xsrange(w,a+1,2):
741
t = b*b-D
742
if t % a4 == 0:
743
c = t // a4
744
if (not primitive_only) or gcd([a,b,c])==1:
745
if b>0 and a>b and c>a:
746
form_list.append(BinaryQF([a,-b,c]))
747
form_list.append(BinaryQF([a,b,c]))
748
749
form_list.sort()
750
return form_list
751
752