151c586b8Smrg /* Use mpz_kronecker_ui() to calculate an estimate for the quadratic
251c586b8Smrg class number h(d), for a given negative fundamental discriminant, using
351c586b8Smrg Dirichlet's analytic formula.
451c586b8Smrg
5*ce543368Smrg Copyright 1999-2002 Free Software Foundation, Inc.
651c586b8Smrg
751c586b8Smrg This file is part of the GNU MP Library.
851c586b8Smrg
951c586b8Smrg This program is free software; you can redistribute it and/or modify it
1051c586b8Smrg under the terms of the GNU General Public License as published by the Free
1151c586b8Smrg Software Foundation; either version 3 of the License, or (at your option)
1251c586b8Smrg any later version.
1351c586b8Smrg
1451c586b8Smrg This program is distributed in the hope that it will be useful, but WITHOUT
1551c586b8Smrg ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1651c586b8Smrg FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
1751c586b8Smrg more details.
1851c586b8Smrg
1951c586b8Smrg You should have received a copy of the GNU General Public License along with
20*ce543368Smrg this program. If not, see https://www.gnu.org/licenses/. */
2151c586b8Smrg
2251c586b8Smrg
2351c586b8Smrg /* Usage: qcn [-p limit] <discriminant>...
2451c586b8Smrg
2551c586b8Smrg A fundamental discriminant means one of the form D or 4*D with D
2651c586b8Smrg square-free. Each argument is checked to see it's congruent to 0 or 1
2751c586b8Smrg mod 4 (as all discriminants must be), and that it's negative, but there's
2851c586b8Smrg no check on D being square-free.
2951c586b8Smrg
3051c586b8Smrg This program is a bit of a toy, there are better methods for calculating
3151c586b8Smrg the class number and class group structure.
3251c586b8Smrg
3351c586b8Smrg Reference:
3451c586b8Smrg
3551c586b8Smrg Daniel Shanks, "Class Number, A Theory of Factorization, and Genera",
3651c586b8Smrg Proc. Symp. Pure Math., vol 20, 1970, pages 415-440.
3751c586b8Smrg
3851c586b8Smrg */
3951c586b8Smrg
4051c586b8Smrg #include <math.h>
4151c586b8Smrg #include <stdio.h>
4251c586b8Smrg #include <stdlib.h>
4351c586b8Smrg #include <string.h>
4451c586b8Smrg
4551c586b8Smrg #include "gmp.h"
4651c586b8Smrg
4751c586b8Smrg #ifndef M_PI
4851c586b8Smrg #define M_PI 3.14159265358979323846
4951c586b8Smrg #endif
5051c586b8Smrg
5151c586b8Smrg
5251c586b8Smrg /* A simple but slow primality test. */
5351c586b8Smrg int
prime_p(unsigned long n)5451c586b8Smrg prime_p (unsigned long n)
5551c586b8Smrg {
5651c586b8Smrg unsigned long i, limit;
5751c586b8Smrg
5851c586b8Smrg if (n == 2)
5951c586b8Smrg return 1;
6051c586b8Smrg if (n < 2 || !(n&1))
6151c586b8Smrg return 0;
6251c586b8Smrg
6351c586b8Smrg limit = (unsigned long) floor (sqrt ((double) n));
6451c586b8Smrg for (i = 3; i <= limit; i+=2)
6551c586b8Smrg if ((n % i) == 0)
6651c586b8Smrg return 0;
6751c586b8Smrg
6851c586b8Smrg return 1;
6951c586b8Smrg }
7051c586b8Smrg
7151c586b8Smrg
7251c586b8Smrg /* The formula is as follows, with d < 0.
7351c586b8Smrg
7451c586b8Smrg w * sqrt(-d) inf p
7551c586b8Smrg h(d) = ------------ * product --------
7651c586b8Smrg 2 * pi p=2 p - (d/p)
7751c586b8Smrg
7851c586b8Smrg
7951c586b8Smrg (d/p) is the Kronecker symbol and the product is over primes p. w is 6
8051c586b8Smrg when d=-3, 4 when d=-4, or 2 otherwise.
8151c586b8Smrg
8251c586b8Smrg Calculating the product up to p=infinity would take a long time, so for
8351c586b8Smrg the estimate primes up to 132,000 are used. Shanks found this giving an
8451c586b8Smrg accuracy of about 1 part in 1000, in normal cases. */
8551c586b8Smrg
8651c586b8Smrg unsigned long p_limit = 132000;
8751c586b8Smrg
8851c586b8Smrg double
qcn_estimate(mpz_t d)8951c586b8Smrg qcn_estimate (mpz_t d)
9051c586b8Smrg {
9151c586b8Smrg double h;
9251c586b8Smrg unsigned long p;
9351c586b8Smrg
9451c586b8Smrg /* p=2 */
9551c586b8Smrg h = sqrt (-mpz_get_d (d)) / M_PI
9651c586b8Smrg * 2.0 / (2.0 - mpz_kronecker_ui (d, 2));
9751c586b8Smrg
9851c586b8Smrg if (mpz_cmp_si (d, -3) == 0) h *= 3;
9951c586b8Smrg else if (mpz_cmp_si (d, -4) == 0) h *= 2;
10051c586b8Smrg
10151c586b8Smrg for (p = 3; p <= p_limit; p += 2)
10251c586b8Smrg if (prime_p (p))
10351c586b8Smrg h *= (double) p / (double) (p - mpz_kronecker_ui (d, p));
10451c586b8Smrg
10551c586b8Smrg return h;
10651c586b8Smrg }
10751c586b8Smrg
10851c586b8Smrg
10951c586b8Smrg void
qcn_str(char * num)11051c586b8Smrg qcn_str (char *num)
11151c586b8Smrg {
11251c586b8Smrg mpz_t z;
11351c586b8Smrg
11451c586b8Smrg mpz_init_set_str (z, num, 0);
11551c586b8Smrg
11651c586b8Smrg if (mpz_sgn (z) >= 0)
11751c586b8Smrg {
11851c586b8Smrg mpz_out_str (stdout, 0, z);
11951c586b8Smrg printf (" is not supported (negatives only)\n");
12051c586b8Smrg }
12151c586b8Smrg else if (mpz_fdiv_ui (z, 4) != 0 && mpz_fdiv_ui (z, 4) != 1)
12251c586b8Smrg {
12351c586b8Smrg mpz_out_str (stdout, 0, z);
12451c586b8Smrg printf (" is not a discriminant (must == 0 or 1 mod 4)\n");
12551c586b8Smrg }
12651c586b8Smrg else
12751c586b8Smrg {
12851c586b8Smrg printf ("h(");
12951c586b8Smrg mpz_out_str (stdout, 0, z);
13051c586b8Smrg printf (") approx %.1f\n", qcn_estimate (z));
13151c586b8Smrg }
13251c586b8Smrg mpz_clear (z);
13351c586b8Smrg }
13451c586b8Smrg
13551c586b8Smrg
13651c586b8Smrg int
main(int argc,char * argv[])13751c586b8Smrg main (int argc, char *argv[])
13851c586b8Smrg {
13951c586b8Smrg int i;
14051c586b8Smrg int saw_number = 0;
14151c586b8Smrg
14251c586b8Smrg for (i = 1; i < argc; i++)
14351c586b8Smrg {
14451c586b8Smrg if (strcmp (argv[i], "-p") == 0)
14551c586b8Smrg {
14651c586b8Smrg i++;
14751c586b8Smrg if (i >= argc)
14851c586b8Smrg {
14951c586b8Smrg fprintf (stderr, "Missing argument to -p\n");
15051c586b8Smrg exit (1);
15151c586b8Smrg }
15251c586b8Smrg p_limit = atoi (argv[i]);
15351c586b8Smrg }
15451c586b8Smrg else
15551c586b8Smrg {
15651c586b8Smrg qcn_str (argv[i]);
15751c586b8Smrg saw_number = 1;
15851c586b8Smrg }
15951c586b8Smrg }
16051c586b8Smrg
16151c586b8Smrg if (! saw_number)
16251c586b8Smrg {
16351c586b8Smrg /* some default output */
16451c586b8Smrg qcn_str ("-85702502803"); /* is 16259 */
16551c586b8Smrg qcn_str ("-328878692999"); /* is 1499699 */
16651c586b8Smrg qcn_str ("-928185925902146563"); /* is 52739552 */
16751c586b8Smrg qcn_str ("-84148631888752647283"); /* is 496652272 */
16851c586b8Smrg return 0;
16951c586b8Smrg }
17051c586b8Smrg
17151c586b8Smrg return 0;
17251c586b8Smrg }
173