@@ -117,6 +117,83 @@ def quadratic_order_class_number(disc):
117
117
h = pari .qfbclassno (disc )
118
118
return ZZ (h )
119
119
120
+ def quadratic_order_approximate_class_number (disc , * , bound = 10 ** 4 ):
121
+ r"""
122
+ Return *an approximation of* the class number of
123
+ the quadratic order of given discriminant.
124
+
125
+ Currently only implemented for maximal orders
126
+ in imaginary-quadratic fields.
127
+
128
+ EXAMPLES::
129
+
130
+ sage: from sage.rings.number_field.order import quadratic_order_approximate_class_number
131
+ sage: QuadraticField(-419).class_number()
132
+ 9
133
+ sage: quadratic_order_approximate_class_number(-419)
134
+ 9.0...
135
+
136
+ ::
137
+
138
+ sage: from sage.rings.number_field.order import quadratic_order_approximate_class_number
139
+ sage: d = 100000000000031
140
+ sage: QuadraticField(-d).class_number(proof=False)
141
+ 14414435
142
+ sage: round(quadratic_order_approximate_class_number(-d))
143
+ 144...
144
+ sage: round(quadratic_order_approximate_class_number(-d, bound=10**6))
145
+ 1441...
146
+
147
+ ::
148
+
149
+ sage: from sage.rings.number_field.order import quadratic_order_approximate_class_number
150
+ sage: # Test it against the exact class number computed for the CSIDH-512 prime
151
+ sage: # Source: https://eprint.iacr.org/2019/498.pdf
152
+ sage: p = 4 * prod(primes(3,374)) * 587 - 1
153
+ sage: hreal = 84884147409828091725676728670213067387206838101828807864190286991865870575397
154
+ sage: assert not hreal * BQFClassGroup(-p).random_element()
155
+ sage: h = round(quadratic_order_approximate_class_number(-p, bound=10**3)); h
156
+ 8...
157
+ sage: RR(h / hreal)
158
+ 1.00...
159
+ sage: h = round(quadratic_order_approximate_class_number(-p, bound=10**4)); h
160
+ 84...
161
+ sage: RR(h / hreal)
162
+ 0.99...
163
+ sage: h = round(quadratic_order_approximate_class_number(-p, bound=10**5)); h
164
+ 848...
165
+ sage: RR(h / hreal)
166
+ 0.999...
167
+ sage: h = round(quadratic_order_approximate_class_number(-p, bound=10**6)); h # long time -- 2s
168
+ 84884...
169
+ sage: RR(h / hreal) # long time -- 2s
170
+ 1.00000...
171
+
172
+ ALGORITHM: Finite approximation of the infinite product given by
173
+ the analytic class number formula, using primes up to ``bound``.
174
+ """
175
+ disc = ZZ (disc )
176
+ if disc >= 0 :
177
+ raise NotImplementedError ('only imaginary-quadratic fields supported' )
178
+ if not disc .is_fundamental_discriminant ():
179
+ raise NotImplementedError ('only fundamental discriminants supported' )
180
+
181
+ from sage .rings .real_mpfr import RealField
182
+ from sage .arith .misc import primes , kronecker_symbol
183
+ from sage .symbolic .constants import pi
184
+
185
+ w = 6 if disc == - 3 else 4 if disc == - 4 else 2
186
+ RR = RealField (max (53 , disc .bit_length ())) # wild guess!
187
+
188
+ # compute numerator and denominator separately for numerical stability
189
+ L1 = L2 = RR (1 )
190
+ for ell in primes (bound ):
191
+ L1 *= ell
192
+ L2 *= ell - kronecker_symbol (disc , ell )
193
+ L = L1 / L2
194
+
195
+ return RR (w * abs (disc ).sqrt () * L / (2 * pi ))
196
+
120
197
121
198
class OrderFactory (UniqueFactory ):
122
199
r"""
0 commit comments