xref: /netbsd-src/external/lgpl3/gmp/dist/demos/qcn.c (revision ce54336801cf28877c3414aa2fcb251dddd543a2)
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