Andreas Enge on Tue, 26 Nov 2013 20:15:06 +0100


[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

Interfacing gmp and mpfr/mpc


With my PhD student, we wish to call library functions using floating point
numbers from the GNU MPFR and GNU MPC libraries inside Pari/GP.
Thanks to lots of help by Bill Allombert (who essentially dictated the
solution), we ended up with the attached code.

The functions mp{fr,c}_init_set_GEN take as argument an uninitialised
variable of type mp{fr,c}, allocate it on the pari stack and set its value
to that of the GEN given as second argument. The content of the variable
is copied, so they may be modified separately. Notice that unlike "normal"
mp{fr,c} variables, here they should not be initialised or cleared; they
may be modified, but their precision cannot be changed. If pari floating point
numbers are given as inputs, their pari precision (which is a multiple of
the bit length of long) is preserved; if integers are given, the current
pari default precision is used (thanks to additional parameters "long prec"
and the magic involved when installing C functions into the GP interpreter
with the type "p" of this variable).

Thanks to the GNU MPFR developers for providing the custom interface to make
this easily possible!

Since usually functions return mpfr and mpc results by side-effects, the
variables holding such results need to be allocated on the pari stack (instead
of by mp{fr,c}_init); this is done via the functions mp{fr,c}_pari_init.
As their argument prec is a pari precision (number of words of the signifi-
cand + 2), their mp{fr,c} precision is automatically a multiple of the word
length.

The functions mp{fr,c}_get_GEN return conversely a pari object of type GEN
from an mp{fr,c} number, the precision of which is assumed to be a multiple
of the word length (which is the case if they are obtained by the functions
explained above).

The function mul is given merely as an example of use. It takes two pari
floating point numbers and returns their product, computed by a call to
mpfr_mul or mpc_mul. To use it inside a GP session, execute the command
   install ("mul", "GGp", , "./mpfrc-pari.so");
The letters "GGp" stand for the types of the arguments (the return type GEN
being implicit, and the "p" being automatically replaced by the current
floating point precision, so that the function indeed takes only two
arguments). This assumes that the C file has been compiled into a shared
library by the command
   gcc mpfrc-pari.c -o mpfrc-pari.so -shared -fPIC -lmpc -lmpfr -lpari -I/usr/local/pari-dev/include -L/usr/local/pari-dev/lib
(with appropriately chosen include and library locations to the development
version of pari).

Comments and suggestions for improvement are welcome.

Andreas

#include <stdio.h>
#include <mpc.h>
#include <pari/pari.h>

/*
GP; install ("mul", "GGp", , "./mpfrc-pari.so");
*/

static void xmpn_mirrorcopy(mp_limb_t *z, mp_limb_t *x, long n)
{
  long i;
  for(i=0;i<n;i++)
    z[i]=x[n-1-i];
}


void mpfr_init_set_real (mpfr_ptr x, GEN r)
   /* creates space for x on the pari stack and sets it to r, which is
      supposed to be a t_REAL */
{
   mp_prec_t prec = realprec (r) - 2;
   GEN rc = new_chunk (prec);
   xmpn_mirrorcopy (rc, r+2, prec);
   mpfr_custom_init_set (x,
                         signe (r) ? signe (r) * MPFR_REGULAR_KIND : MPFR_ZERO_KIND,
                         expo (r) + 1,
                         BITS_IN_LONG * prec,
                         rc);
}


void mpfr_init_set_GEN (mpfr_ptr x, GEN r, long prec)
   /* creates space for x on the pari stack and sets it to r.
      If r is a t_REAL, its own precision is used;
      if r is a t_INT, prec is used as pari precision. */
{
   switch (typ (r)) {
      case t_REAL:
         mpfr_init_set_real (x, r);
         break;
      case t_INT:
         mpfr_init_set_real (x, itor (r, prec));
         break;
      default:
         pari_err_TYPE ("mpfr_init_set_GEN", r);
   }
}


void mpc_init_set_GEN (mpc_ptr z, GEN c, long prec)
   /* creates space for z on the pari stack and sets it to c.
      prec is passed through to mpfr_init_set_GEN. */
{
   if (typ (c) != t_COMPLEX) {
      mpfr_init_set_GEN (mpc_realref (z), c, prec);
      mpfr_init_set_GEN (mpc_imagref (z), gen_0, prec);
   }
   else {
      mpfr_init_set_GEN (mpc_realref (z), gel (c, 1), prec);
      mpfr_init_set_GEN (mpc_imagref (z), gel (c, 2), prec);
   }
}


/*
void
mpfr_print (GEN r, long prec)
{
   pari_sp av = avma;
   mpfr_t x;

   mpfr_init_set_GEN (x, r, prec);
   mpfr_out_str (stdout, 10, 0, x, MPFR_RNDN);
   printf ("\n");

   avma = av;
}


void mpc_print (GEN c, long prec)
{
   pari_sp av = avma;
   mpc_t z;

   mpc_init_set_GEN (z, c, prec);
   mpc_out_str (stdout, 10, 0, z, MPC_RNDNN);
   printf ("\n");

   avma = av;
}
*/


void mpfr_pari_init (mpfr_ptr x, long prec)
   /* initialises x on the pari stack with the pari precision prec */
{
   mpfr_init_set_GEN (x, gen_0, prec);
}

void mpc_pari_init (mpc_ptr z, long prec)
   /* initialises z on the pari stack with the pari precision prec */
{
   mpc_init_set_GEN (z, gen_I (), prec);
}


GEN mpfr_get_GEN (mpfr_srcptr x)
   /* returns x as a t_REAL */
{
   long prec = mpfr_get_prec (x) / BITS_IN_LONG + 2;
   if (mpfr_zero_p (x))
      return real_0 (prec);
   else {
      GEN r = cgetr (prec);
      xmpn_mirrorcopy (r+2, mpfr_custom_get_significand (x), prec - 2);
      setsigne (r, (mpfr_signbit (x) ? -1 : 1));
      setexpo (r, mpfr_get_exp (x) - 1);
      return r;
   }
}


GEN mpc_get_GEN (mpc_srcptr z)
   /* returns z as a t_COMPLEX */
{
   GEN c = cgetg (3, t_COMPLEX);
   gel (c, 1) = mpfr_get_GEN (mpc_realref (z));
   gel (c, 2) = mpfr_get_GEN (mpc_imagref (z));
   return c;
}


GEN mul (GEN c1, GEN c2, long prec)
   /* test function */
{
   if (typ (c1) != t_COMPLEX && typ (c2) != t_COMPLEX) {
      mpfr_t z, z1, z2;
      mpfr_pari_init (z, prec);
      mpfr_init_set_GEN (z1, c1, prec);
      mpfr_init_set_GEN (z2, c2, prec);
      mpfr_mul (z, z1, z2, MPC_RNDNN);
      return mpfr_get_GEN (z);
   }
   else {
      mpc_t z, z1, z2;
      mpc_pari_init (z, prec);
      mpc_init_set_GEN (z1, c1, prec);
      mpc_init_set_GEN (z2, c2, prec);
      mpc_mul (z, z1, z2, MPC_RNDNN);
      return mpc_get_GEN (z);
   }
}