install gmp memory functions that let libgc know about allocations
[bpt/guile.git] / libguile / numbers.c
index 59d8e74..e5b4ed9 100644 (file)
@@ -45,6 +45,8 @@
 #  include <config.h>
 #endif
 
+#include <verify.h>
+
 #include <math.h>
 #include <string.h>
 #include <unicase.h>
@@ -72,6 +74,9 @@
 #ifndef M_LOG10E
 #define M_LOG10E   0.43429448190325182765
 #endif
+#ifndef M_LN2
+#define M_LN2     0.69314718055994530942
+#endif
 #ifndef M_PI
 #define M_PI       3.14159265358979323846
 #endif
@@ -109,8 +114,13 @@ typedef scm_t_signed_bits scm_t_inum;
 /* the macro above will not work as is with fractions */
 
 
+/* Default to 1, because as we used to hard-code `free' as the
+   deallocator, we know that overriding these functions with
+   instrumented `malloc' / `free' is OK.  */
+int scm_install_gmp_memory_functions = 1;
 static SCM flo0;
 static SCM exactly_one_half;
+static SCM flo_log10e;
 
 #define SCM_SWAP(x, y) do { SCM __t = x; x = y; y = __t; } while (0)
 
@@ -130,9 +140,9 @@ static double acosh (double x) { return log (x + sqrt (x * x - 1)); }
 static double atanh (double x) { return 0.5 * log ((1 + x) / (1 - x)); }
 #endif
 
-/* mpz_cmp_d in gmp 4.1.3 doesn't recognise infinities, so xmpz_cmp_d uses
-   an explicit check.  In some future gmp (don't know what version number),
-   mpz_cmp_d is supposed to do this itself.  */
+/* mpz_cmp_d in GMP before 4.2 didn't recognise infinities, so
+   xmpz_cmp_d uses an explicit check.  Starting with GMP 4.2 (released
+   in March 2006), mpz_cmp_d now handles infinities properly.  */
 #if 1
 #define xmpz_cmp_d(z, d)                                \
   (isinf (d) ? (d < 0.0 ? 1 : -1) : mpz_cmp_d (z, d))
@@ -142,7 +152,7 @@ static double atanh (double x) { return 0.5 * log ((1 + x) / (1 - x)); }
 
 
 #if defined (GUILE_I)
-#if HAVE_COMPLEX_DOUBLE
+#if defined HAVE_COMPLEX_DOUBLE
 
 /* For an SCM object Z which is a complex number (ie. satisfies
    SCM_COMPLEXP), return its value as a C level "complex double". */
@@ -166,6 +176,7 @@ scm_from_complex_double (complex double z)
 static mpz_t z_negative_one;
 
 \f
+
 /* Clear the `mpz_t' embedded in bignum PTR.  */
 static void
 finalize_bignum (GC_PTR ptr, GC_PTR data)
@@ -176,6 +187,31 @@ finalize_bignum (GC_PTR ptr, GC_PTR data)
   mpz_clear (SCM_I_BIG_MPZ (bignum));
 }
 
+/* The next three functions (custom_libgmp_*) are passed to
+   mp_set_memory_functions (in GMP) so that memory used by the digits
+   themselves is known to the garbage collector.  This is needed so
+   that GC will be run at appropriate times.  Otherwise, a program which
+   creates many large bignums would malloc a huge amount of memory
+   before the GC runs. */
+static void *
+custom_gmp_malloc (size_t alloc_size)
+{
+  return scm_malloc (alloc_size);
+}
+
+static void *
+custom_gmp_realloc (void *old_ptr, size_t old_size, size_t new_size)
+{
+  return scm_realloc (old_ptr, new_size);
+}
+
+static void
+custom_gmp_free (void *ptr, size_t size)
+{
+  free (ptr);
+}
+
+
 /* Return a new uninitialized bignum.  */
 static inline SCM
 make_bignum (void)
@@ -312,16 +348,15 @@ scm_i_dbl2num (double u)
    we need to use mpz_getlimbn.  mpz_tstbit is not right, it treats
    negatives as twos complement.
 
-   In current gmp 4.1.3, mpz_get_d rounding is unspecified.  It ends up
-   following the hardware rounding mode, but applied to the absolute value
-   of the mpz_t operand.  This is not what we want so we put the high
-   DBL_MANT_DIG bits into a temporary.  In some future gmp, don't know when,
-   mpz_get_d is supposed to always truncate towards zero.
+   In GMP before 4.2, mpz_get_d rounding was unspecified.  It ended up
+   following the hardware rounding mode, but applied to the absolute
+   value of the mpz_t operand.  This is not what we want so we put the
+   high DBL_MANT_DIG bits into a temporary.  Starting with GMP 4.2
+   (released in March 2006) mpz_get_d now always truncates towards zero.
 
-   ENHANCE-ME: The temporary init+clear to force the rounding in gmp 4.1.3
-   is a slowdown.  It'd be faster to pick out the relevant high bits with
-   mpz_getlimbn if we could be bothered coding that, and if the new
-   truncating gmp doesn't come out.  */
+   ENHANCE-ME: The temporary init+clear to force the rounding in GMP
+   before 4.2 is a slowdown.  It'd be faster to pick out the relevant
+   high bits with mpz_getlimbn.  */
 
 double
 scm_i_big2dbl (SCM b)
@@ -333,7 +368,12 @@ scm_i_big2dbl (SCM b)
 
 #if 1
   {
-    /* Current GMP, eg. 4.1.3, force truncation towards zero */
+    /* For GMP earlier than 4.2, force truncation towards zero */
+
+    /* FIXME: DBL_MANT_DIG is the number of base-`FLT_RADIX' digits,
+       _not_ the number of bits, so this code will break badly on a
+       system with non-binary doubles.  */
+
     mpz_t  tmp;
     if (bits > DBL_MANT_DIG)
       {
@@ -349,7 +389,7 @@ scm_i_big2dbl (SCM b)
       }
   }
 #else
-  /* Future GMP */
+  /* GMP 4.2 or later */
   result = mpz_get_d (SCM_I_BIG_MPZ (b));
 #endif
 
@@ -526,6 +566,11 @@ SCM_PRIMITIVE_GENERIC (scm_exact_p, "exact?", 1, 0, 0,
 }
 #undef FUNC_NAME
 
+int
+scm_is_exact (SCM val)
+{
+  return scm_is_true (scm_exact_p (val));
+}
 
 SCM_PRIMITIVE_GENERIC (scm_inexact_p, "inexact?", 1, 0, 0,
             (SCM x),
@@ -542,6 +587,11 @@ SCM_PRIMITIVE_GENERIC (scm_inexact_p, "inexact?", 1, 0, 0,
 }
 #undef FUNC_NAME
 
+int
+scm_is_inexact (SCM val)
+{
+  return scm_is_true (scm_inexact_p (val));
+}
 
 SCM_PRIMITIVE_GENERIC (scm_odd_p, "odd?", 1, 0, 0, 
             (SCM n),
@@ -788,9 +838,9 @@ SCM_PRIMITIVE_GENERIC (scm_quotient, "quotient", 2, 0, 0,
        "Return the quotient of the numbers @var{x} and @var{y}.")
 #define FUNC_NAME s_scm_quotient
 {
-  if (SCM_LIKELY (SCM_I_INUMP (x)) || SCM_LIKELY (SCM_BIGP (x)))
+  if (SCM_LIKELY (scm_is_integer (x)))
     {
-      if (SCM_LIKELY (SCM_I_INUMP (y)) || SCM_LIKELY (SCM_BIGP (y)))
+      if (SCM_LIKELY (scm_is_integer (y)))
        return scm_truncate_quotient (x, y);
       else
        SCM_WTA_DISPATCH_2 (g_scm_quotient, x, y, SCM_ARG2, s_scm_quotient);
@@ -809,9 +859,9 @@ SCM_PRIMITIVE_GENERIC (scm_remainder, "remainder", 2, 0, 0,
        "@end lisp")
 #define FUNC_NAME s_scm_remainder
 {
-  if (SCM_LIKELY (SCM_I_INUMP (x)) || SCM_LIKELY (SCM_BIGP (x)))
+  if (SCM_LIKELY (scm_is_integer (x)))
     {
-      if (SCM_LIKELY (SCM_I_INUMP (y)) || SCM_LIKELY (SCM_BIGP (y)))
+      if (SCM_LIKELY (scm_is_integer (y)))
        return scm_truncate_remainder (x, y);
       else
        SCM_WTA_DISPATCH_2 (g_scm_remainder, x, y, SCM_ARG2, s_scm_remainder);
@@ -831,9 +881,9 @@ SCM_PRIMITIVE_GENERIC (scm_modulo, "modulo", 2, 0, 0,
        "@end lisp")
 #define FUNC_NAME s_scm_modulo
 {
-  if (SCM_LIKELY (SCM_I_INUMP (x)) || SCM_LIKELY (SCM_BIGP (x)))
+  if (SCM_LIKELY (scm_is_integer (x)))
     {
-      if (SCM_LIKELY (SCM_I_INUMP (y)) || SCM_LIKELY (SCM_BIGP (y)))
+      if (SCM_LIKELY (scm_is_integer (y)))
        return scm_floor_remainder (x, y);
       else
        SCM_WTA_DISPATCH_2 (g_scm_modulo, x, y, SCM_ARG2, s_scm_modulo);
@@ -1490,8 +1540,6 @@ SCM_PRIMITIVE_GENERIC (scm_ceiling_quotient, "ceiling-quotient", 2, 0, 0,
                  if (SCM_LIKELY (xx >= 0))
                    xx1 = xx + yy - 1;
                }
-             else if (SCM_UNLIKELY (yy == 0))
-               scm_num_overflow (s_scm_ceiling_quotient);
              else if (xx < 0)
                xx1 = xx + yy + 1;
              qq = xx1 / yy;
@@ -2450,11 +2498,8 @@ scm_i_inexact_truncate_divide (double x, double y, SCM *qp, SCM *rp)
     scm_num_overflow (s_scm_truncate_divide);  /* or return a NaN? */
   else
     {
-      double q, r, q1;
-      /* FIXME: Use trunc, after it has been imported from gnulib */
-      q1 = x / y;
-      q = (q1 >= 0) ? floor (q1) : ceil (q1);
-      r = x - q * y;
+      double q = trunc (x / y);
+      double r = x - q * y;
       *qp = scm_from_double (q);
       *rp = scm_from_double (r);
     }
@@ -4105,7 +4150,7 @@ SCM scm_logand (SCM n1, SCM n2)
       else if SCM_BIGP (n2)
        {
        intbig: 
-         if (n1 == 0)
+         if (nn1 == 0)
            return SCM_INUM0;
          {
            SCM result_z = scm_i_mkbig ();
@@ -5353,9 +5398,12 @@ int
 scm_bigprint (SCM exp, SCM port, scm_print_state *pstate SCM_UNUSED)
 {
   char *str = mpz_get_str (NULL, 10, SCM_I_BIG_MPZ (exp));
+  size_t len = strlen (str);
+  void (*freefunc) (void *, size_t);
+  mp_get_memory_functions (NULL, NULL, &freefunc);
   scm_remember_upto_here_1 (exp);
-  scm_lfwrite (str, (size_t) strlen (str), port);
-  free (str);
+  scm_lfwrite (str, len, port);
+  freefunc (str, len + 1);
   return !0;
 }
 /*** END nums->strs ***/
@@ -5440,6 +5488,9 @@ char_decimal_value (scm_t_uint32 c)
   return d;
 }
 
+/* Parse the substring of MEM starting at *P_IDX for an unsigned integer
+   in base RADIX.  Upon success, return the unsigned integer and update
+   *P_IDX and *P_EXACTNESS accordingly.  Return #f on failure.  */
 static SCM
 mem2uinteger (SCM mem, unsigned int *p_idx,
              unsigned int radix, enum t_exactness *p_exactness)
@@ -5663,7 +5714,7 @@ mem2decimal_from_point (SCM result, SCM mem,
          if (sign == 1)
            result = scm_product (result, e);
          else
-           result = scm_divide2real (result, e);
+           result = scm_divide (result, e);
 
          /* We've seen an exponent, thus the value is implicitly inexact. */
          x = INEXACT;
@@ -5711,7 +5762,16 @@ mem2ureal (SCM mem, unsigned int *p_idx,
       /* Cobble up the fractional part.  We might want to set the
         NaN's mantissa from it. */
       idx += 4;
-      mem2uinteger (mem, &idx, 10, &implicit_x);
+      if (!scm_is_eq (mem2uinteger (mem, &idx, 10, &implicit_x), SCM_INUM0))
+        {
+#if SCM_ENABLE_DEPRECATED == 1
+          scm_c_issue_deprecation_warning
+            ("Non-zero suffixes to `+nan.' are deprecated.  Use `+nan.0'.");
+#else
+          return SCM_BOOL_F;
+#endif
+        }
+          
       *p_idx = idx;
       return scm_nan ();
     }
@@ -9375,6 +9435,62 @@ scm_is_number (SCM z)
 }
 
 
+/* Returns log(x * 2^shift) */
+static SCM
+log_of_shifted_double (double x, long shift)
+{
+  double ans = log (fabs (x)) + shift * M_LN2;
+
+  if (x > 0.0 || double_is_non_negative_zero (x))
+    return scm_from_double (ans);
+  else
+    return scm_c_make_rectangular (ans, M_PI);
+}
+
+/* Returns log(n), for exact integer n of integer-length size */
+static SCM
+log_of_exact_integer_with_size (SCM n, long size)
+{
+  long shift = size - 2 * scm_dblprec[0];
+
+  if (shift > 0)
+    return log_of_shifted_double
+      (scm_to_double (scm_ash (n, scm_from_long(-shift))),
+       shift);
+  else
+    return log_of_shifted_double (scm_to_double (n), 0);
+}
+
+/* Returns log(n), for exact integer n */
+static SCM
+log_of_exact_integer (SCM n)
+{
+  return log_of_exact_integer_with_size
+    (n, scm_to_long (scm_integer_length (n)));
+}
+
+/* Returns log(n/d), for exact non-zero integers n and d */
+static SCM
+log_of_fraction (SCM n, SCM d)
+{
+  long n_size = scm_to_long (scm_integer_length (n));
+  long d_size = scm_to_long (scm_integer_length (d));
+
+  if (abs (n_size - d_size) > 1)
+    return (scm_difference (log_of_exact_integer_with_size (n, n_size),
+                           log_of_exact_integer_with_size (d, d_size)));
+  else if (scm_is_false (scm_negative_p (n)))
+    return scm_from_double
+      (log1p (scm_to_double (scm_divide2real (scm_difference (n, d), d))));
+  else
+    return scm_c_make_rectangular
+      (log1p (scm_to_double (scm_divide2real
+                            (scm_difference (scm_abs (n), d),
+                             d))),
+       M_PI);
+}
+
+
 /* In the following functions we dispatch to the real-arg funcs like log()
    when we know the arg is real, instead of just handing everything to
    clog() for instance.  This is in case clog() doesn't optimize for a
@@ -9388,7 +9504,8 @@ SCM_PRIMITIVE_GENERIC (scm_log, "log", 1, 0, 0,
 {
   if (SCM_COMPLEXP (z))
     {
-#if HAVE_COMPLEX_DOUBLE && HAVE_CLOG && defined (SCM_COMPLEX_VALUE)
+#if defined HAVE_COMPLEX_DOUBLE && defined HAVE_CLOG \
+  && defined (SCM_COMPLEX_VALUE)
       return scm_from_complex_double (clog (SCM_COMPLEX_VALUE (z)));
 #else
       double re = SCM_COMPLEX_REAL (z);
@@ -9397,17 +9514,21 @@ SCM_PRIMITIVE_GENERIC (scm_log, "log", 1, 0, 0,
                                      atan2 (im, re));
 #endif
     }
-  else if (SCM_NUMBERP (z))
+  else if (SCM_REALP (z))
+    return log_of_shifted_double (SCM_REAL_VALUE (z), 0);
+  else if (SCM_I_INUMP (z))
     {
-      /* ENHANCE-ME: When z is a bignum the logarithm will fit a double
-         although the value itself overflows.  */
-      double re = scm_to_double (z);
-      double l = log (fabs (re));
-      if (re >= 0.0)
-        return scm_from_double (l);
-      else
-        return scm_c_make_rectangular (l, M_PI);
+#ifndef ALLOW_DIVIDE_BY_EXACT_ZERO
+      if (scm_is_eq (z, SCM_INUM0))
+       scm_num_overflow (s_scm_log);
+#endif
+      return log_of_shifted_double (SCM_I_INUM (z), 0);
     }
+  else if (SCM_BIGP (z))
+    return log_of_exact_integer (z);
+  else if (SCM_FRACTIONP (z))
+    return log_of_fraction (SCM_FRACTION_NUMERATOR (z),
+                           SCM_FRACTION_DENOMINATOR (z));
   else
     SCM_WTA_DISPATCH_1 (g_scm_log, z, 1, s_scm_log);
 }
@@ -9434,17 +9555,27 @@ SCM_PRIMITIVE_GENERIC (scm_log10, "log10", 1, 0, 0,
                                      M_LOG10E * atan2 (im, re));
 #endif
     }
-  else if (SCM_NUMBERP (z))
+  else if (SCM_REALP (z) || SCM_I_INUMP (z))
     {
-      /* ENHANCE-ME: When z is a bignum the logarithm will fit a double
-         although the value itself overflows.  */
-      double re = scm_to_double (z);
-      double l = log10 (fabs (re));
-      if (re >= 0.0)
-        return scm_from_double (l);
-      else
-        return scm_c_make_rectangular (l, M_LOG10E * M_PI);
+#ifndef ALLOW_DIVIDE_BY_EXACT_ZERO
+      if (scm_is_eq (z, SCM_INUM0))
+       scm_num_overflow (s_scm_log10);
+#endif
+      {
+       double re = scm_to_double (z);
+       double l = log10 (fabs (re));
+       if (re > 0.0 || double_is_non_negative_zero (re))
+         return scm_from_double (l);
+       else
+         return scm_c_make_rectangular (l, M_LOG10E * M_PI);
+      }
     }
+  else if (SCM_BIGP (z))
+    return scm_product (flo_log10e, log_of_exact_integer (z));
+  else if (SCM_FRACTIONP (z))
+    return scm_product (flo_log10e,
+                       log_of_fraction (SCM_FRACTION_NUMERATOR (z),
+                                        SCM_FRACTION_DENOMINATOR (z)));
   else
     SCM_WTA_DISPATCH_1 (g_scm_log10, z, 1, s_scm_log10);
 }
@@ -9459,7 +9590,8 @@ SCM_PRIMITIVE_GENERIC (scm_exp, "exp", 1, 0, 0,
 {
   if (SCM_COMPLEXP (z))
     {
-#if HAVE_COMPLEX_DOUBLE && HAVE_CEXP && defined (SCM_COMPLEX_VALUE)
+#if defined HAVE_COMPLEX_DOUBLE && defined HAVE_CEXP \
+  && defined (SCM_COMPLEX_VALUE)
       return scm_from_complex_double (cexp (SCM_COMPLEX_VALUE (z)));
 #else
       return scm_c_make_polar (exp (SCM_COMPLEX_REAL (z)),
@@ -9478,6 +9610,70 @@ SCM_PRIMITIVE_GENERIC (scm_exp, "exp", 1, 0, 0,
 #undef FUNC_NAME
 
 
+SCM_DEFINE (scm_i_exact_integer_sqrt, "exact-integer-sqrt", 1, 0, 0,
+           (SCM k),
+           "Return two exact non-negative integers @var{s} and @var{r}\n"
+           "such that @math{@var{k} = @var{s}^2 + @var{r}} and\n"
+           "@math{@var{s}^2 <= @var{k} < (@var{s} + 1)^2}.\n"
+           "An error is raised if @var{k} is not an exact non-negative integer.\n"
+           "\n"
+           "@lisp\n"
+           "(exact-integer-sqrt 10) @result{} 3 and 1\n"
+           "@end lisp")
+#define FUNC_NAME s_scm_i_exact_integer_sqrt
+{
+  SCM s, r;
+
+  scm_exact_integer_sqrt (k, &s, &r);
+  return scm_values (scm_list_2 (s, r));
+}
+#undef FUNC_NAME
+
+void
+scm_exact_integer_sqrt (SCM k, SCM *sp, SCM *rp)
+{
+  if (SCM_LIKELY (SCM_I_INUMP (k)))
+    {
+      scm_t_inum kk = SCM_I_INUM (k);
+      scm_t_inum uu = kk;
+      scm_t_inum ss;
+
+      if (SCM_LIKELY (kk > 0))
+       {
+         do
+           {
+             ss = uu;
+             uu = (ss + kk/ss) / 2;
+           } while (uu < ss);
+         *sp = SCM_I_MAKINUM (ss);
+         *rp = SCM_I_MAKINUM (kk - ss*ss);
+       }
+      else if (SCM_LIKELY (kk == 0))
+       *sp = *rp = SCM_INUM0;
+      else
+       scm_wrong_type_arg_msg ("exact-integer-sqrt", SCM_ARG1, k,
+                               "exact non-negative integer");
+    }
+  else if (SCM_LIKELY (SCM_BIGP (k)))
+    {
+      SCM s, r;
+
+      if (mpz_sgn (SCM_I_BIG_MPZ (k)) < 0)
+       scm_wrong_type_arg_msg ("exact-integer-sqrt", SCM_ARG1, k,
+                               "exact non-negative integer");
+      s = scm_i_mkbig ();
+      r = scm_i_mkbig ();
+      mpz_sqrtrem (SCM_I_BIG_MPZ (s), SCM_I_BIG_MPZ (r), SCM_I_BIG_MPZ (k));
+      scm_remember_upto_here_1 (k);
+      *sp = scm_i_normbig (s);
+      *rp = scm_i_normbig (r);
+    }
+  else
+    scm_wrong_type_arg_msg ("exact-integer-sqrt", SCM_ARG1, k,
+                           "exact non-negative integer");
+}
+
+
 SCM_PRIMITIVE_GENERIC (scm_sqrt, "sqrt", 1, 0, 0,
                       (SCM z),
        "Return the square root of @var{z}.  Of the two possible roots\n"
@@ -9525,6 +9721,11 @@ scm_init_numbers ()
 {
   int i;
 
+  if (scm_install_gmp_memory_functions)
+    mp_set_memory_functions (custom_gmp_malloc,
+                             custom_gmp_realloc,
+                             custom_gmp_free);
+
   mpz_init_set_si (z_negative_one, -1);
 
   /* It may be possible to tune the performance of some algorithms by using
@@ -9539,6 +9740,7 @@ scm_init_numbers ()
   scm_add_feature ("complex");
   scm_add_feature ("inexact");
   flo0 = scm_from_double (0.0);
+  flo_log10e = scm_from_double (M_LOG10E);
 
   /* determine floating point precision */
   for (i=2; i <= SCM_MAX_DBL_RADIX; ++i)