From fe11a0d75811897f026c3b698b08f11d13950543 Mon Sep 17 00:00:00 2001
From: Andreas Enge <andreas.enge@inria.fr>
Date: Mon, 20 Feb 2023 19:14:12 +0100
Subject: [PATCH] Make checkpointing dependent on CM_ECPP_TMPDIR.

* lib/cm-impl.h (mpzx_oneroot_split_mod, cm_class_get_j_mod_p,
  cm_curve_and_point_stat, cm_ecpp_one_step2,
  cm_mpi_submit_ecpp_one_step2): Add tmpdir parameter.
* lib/curve.c (cm_curve_and_point_stat, cm_curve_and_point): Add tmpdir
  parameter and adapt function calls.
* lib/ecpp.c (ecpp2, cm_ecpp_one_step2, cm_ecpp): Add tmpdir parameter
  and adapt function calls.
* lib/jmodp.c (get_root_mod_p, get_tower_root_mod_p,
  get_quadratic_tower_root_mod_p, cm_class_get_j_mod_p): Add tmpdir
  parameter and adapt function calls.
* lib/mpi.c (cm_mpi_submit_ecpp_one_step2): Add tmpdir parameter and send
  it to workers.
  (mpi_worker): Receive tmpdir parameter.
* lib/pari.c (mpzx_oneroot_split_mod): Add and treat tmpdir parameter.
---
 lib/cm-impl.h | 15 ++++++++-------
 lib/curve.c   | 10 ++++++----
 lib/ecpp.c    | 23 +++++++++++++++--------
 lib/jmodp.c   | 37 +++++++++++++++++++------------------
 lib/mpi.c     | 25 ++++++++++++++++++++-----
 lib/pari.c    | 19 +++++++++++--------
 6 files changed, 79 insertions(+), 50 deletions(-)

diff --git a/lib/cm-impl.h b/lib/cm-impl.h
index 9dee7c3..5e153e4 100644
--- a/lib/cm-impl.h
+++ b/lib/cm-impl.h
@@ -120,7 +120,7 @@ extern void cm_modular_eta_series_fr (cm_modular_t m, ftype rop, ftype q_24);
 
 /* functions depending on PARI */
 extern void mpzx_oneroot_split_mod (mpz_ptr root, mpzx_srcptr f,
-   mpz_srcptr p, bool verbose, bool debug);
+   mpz_srcptr p, const char *tmpdir, bool verbose, bool debug);
 extern void cm_pari_oneroot (mpz_ptr root, mpzx_srcptr f, mpz_srcptr p);
 extern mpz_t* cm_pari_find_roots (int *no, mpzx_srcptr f, mpz_srcptr p);
 extern int cm_pari_classgroup (int_cl_t d, int_cl_t *ord, cm_form_t *gen);
@@ -218,15 +218,15 @@ extern void cm_modclass_atkinhecke_level_eval_quad (cm_modclass_t mc, ctype rop,
 extern bool cm_class_write (cm_class_srcptr c, cm_param_srcptr param);
 extern bool cm_class_read (cm_class_ptr c, cm_param_srcptr param);
 extern mpz_t* cm_class_get_j_mod_p (int *no, cm_param_srcptr param,
-   cm_class_srcptr c, mpz_srcptr p, const char* modpoldir,
-   bool verbose, bool debug);
+   cm_class_srcptr c, mpz_srcptr p, const char *modpoldir,
+   const char *tmpdir, bool verbose, bool debug);
 
 /* functions for computing parameters of a complex multiplication curve */
 extern void cm_curve_and_point_stat (mpz_ptr a, mpz_ptr b, mpz_ptr x,
    mpz_ptr y, cm_param_srcptr param, cm_class_srcptr c,
    mpz_srcptr p, mpz_srcptr l, mpz_srcptr co,
-   const char* modpoldir, bool print, bool verbose, bool debug,
-   cm_stat_t stat);
+   const char *modpoldir, const char *tmpdir,
+   bool print, bool verbose, bool debug, cm_stat_t stat);
 
 
 /* functions for ECPP */
@@ -240,7 +240,8 @@ extern mpz_t* cm_ecpp_compute_cardinalities (int *no_card,
    int_cl_t **card_d, int_cl_t *d, int no_d, mpz_srcptr N,
    long int *qstar, int no_qstar, mpz_t *qroot);
 extern void cm_ecpp_one_step2 (mpz_t *cert2, mpz_t *cert1, int i,
-   const char* modpoldir, bool verbose, bool debug, cm_stat_t stat);
+   const char* modpoldir,
+   const char *tmpdir, bool verbose, bool debug, cm_stat_t stat);
 extern bool cm_pari_ecpp_check (mpz_t **cert, int depth);
 
 
@@ -287,7 +288,7 @@ extern void cm_mpi_get_primorial (int rank, double *t);
 extern void cm_mpi_submit_tonelli (int rank, int job, const long int a);
 extern void cm_mpi_get_tonelli (mpz_ptr root, int rank, double *t);
 extern void cm_mpi_submit_ecpp_one_step2 (int rank, int job, mpz_t *cert1,
-   const char* modpoldir);
+   const char *modpoldir, const char *tmpdir);
 extern void cm_mpi_get_ecpp_one_step2 (mpz_t *cert2, int rank,
    cm_stat_ptr stat);
 extern void cm_mpi_submit_curve_cardinalities (int rank, int job,
diff --git a/lib/curve.c b/lib/curve.c
index 7f0192d..d1c8ab0 100644
--- a/lib/curve.c
+++ b/lib/curve.c
@@ -2,7 +2,7 @@
 
 curve.c - code for computing cm curves
 
-Copyright (C) 2009, 2010, 2021, 2022 Andreas Enge
+Copyright (C) 2009, 2010, 2021, 2022, 2023 Andreas Enge
 
 This file is part of CM.
 
@@ -692,7 +692,8 @@ void cm_curve_crypto_param (mpz_ptr p, mpz_ptr n, mpz_ptr l, mpz_ptr c,
 void cm_curve_and_point_stat (mpz_ptr a, mpz_ptr b, mpz_ptr x, mpz_ptr y,
    cm_param_srcptr param, cm_class_srcptr c,
    mpz_srcptr p, mpz_srcptr l, mpz_srcptr co,
-   const char* modpoldir, bool print, bool verbose, bool debug,
+   const char *modpoldir, const char *tmpdir,
+   bool print, bool verbose, bool debug,
    cm_stat_t stat)
    /* Given CM parameters param, a class polynomial or class field tower
       stored in c, and curve cardinality parameters p (>=5, the cardinality
@@ -754,7 +755,8 @@ void cm_curve_and_point_stat (mpz_ptr a, mpz_ptr b, mpz_ptr x, mpz_ptr y,
 
    if (stat != NULL)
       cm_timer_continue (stat->timer [2]);
-   j = cm_class_get_j_mod_p (&no_j, param, c, p, modpoldir, verbose, debug);
+   j = cm_class_get_j_mod_p (&no_j, param, c, p, modpoldir,
+      tmpdir, verbose, debug);
    if (stat != NULL)
       cm_timer_stop (stat->timer [2]);
 
@@ -867,7 +869,7 @@ void cm_curve_and_point (mpz_ptr a, mpz_ptr b, mpz_ptr x, mpz_ptr y,
    const char* modpoldir, bool print, bool verbose)
 {
    cm_curve_and_point_stat (a, b, x, y, param, c, p, l, co, modpoldir,
-      print, verbose, false, NULL);
+      NULL, print, verbose, false, NULL);
 }
 
 /*****************************************************************************/
diff --git a/lib/ecpp.c b/lib/ecpp.c
index f5448c1..527009b 100644
--- a/lib/ecpp.c
+++ b/lib/ecpp.c
@@ -68,7 +68,7 @@ static void ecpp_param_init (cm_param_ptr param, uint_cl_t d);
 static mpz_t** ecpp1 (int *depth, mpz_srcptr p, char *filename,
    char *tmpdir, bool onlyread, bool verbose, bool debug, cm_stat_ptr stat);
 static void ecpp2 (mpz_t **cert2, mpz_t **cert1, int depth,
-   char *filename, const char* modpoldir, bool verbose,
+   char *filename, const char* modpoldir, const char *tmpdir, bool verbose,
    bool debug, cm_stat_ptr stat);
 
 /*****************************************************************************/
@@ -1533,7 +1533,8 @@ static void ecpp_param_init (cm_param_ptr param, uint_cl_t d)
 /*****************************************************************************/
 
 void cm_ecpp_one_step2 (mpz_t *cert2, mpz_t *cert1, int i,
-   const char* modpoldir, bool verbose, bool debug, cm_stat_t stat)
+   const char *modpoldir,
+   const char *tmpdir, bool verbose, bool debug, cm_stat_t stat)
    /* The function takes the same parameters as ecpp2, except that cert1
       contains only one entry of the first ECPP step, and that only one
       step of the certificate is output in cert2, which needs be to a
@@ -1587,7 +1588,7 @@ void cm_ecpp_one_step2 (mpz_t *cert2, mpz_t *cert1, int i,
    cm_timer_stop (stat->timer [1]);
 
    cm_curve_and_point_stat (a, b, x, y, param, c, p, l, co,
-      modpoldir, false, verbose, debug, stat);
+      modpoldir, tmpdir, false, verbose, debug, stat);
    cm_class_clear (c);
    cm_timer_stop (clock);
 
@@ -1612,7 +1613,8 @@ void cm_ecpp_one_step2 (mpz_t *cert2, mpz_t *cert1, int i,
 /*****************************************************************************/
 
 static void ecpp2 (mpz_t **cert2, mpz_t **cert1, int depth, char *filename,
-   const char* modpoldir, bool verbose, bool debug, cm_stat_ptr stat)
+   const char* modpoldir, const char *tmpdir, bool verbose, bool debug,
+   cm_stat_ptr stat)
    /* Given the result of the ECPP down-run in cert1, an array of
       length depth as computed by ecpp1, execute the second step of
       the ECPP algorithm and compute the certificate proper in cert2,
@@ -1627,6 +1629,8 @@ static void ecpp2 (mpz_t **cert2, mpz_t **cert1, int depth, char *filename,
       modpoldir gives the directory where modular polynomials are stored;
       it is passed through to the function computing a curve from a root
       of the class polynomial.
+      If different from NULL, tmpdir indicates a directory in which files
+      with factors of polynomials can be stored for checkpointing.
       verbose indicates whether intermediate computations output progress
       information.
       debug indicates whether additional developer information (mainly
@@ -1671,7 +1675,7 @@ static void ecpp2 (mpz_t **cert2, mpz_t **cert1, int depth, char *filename,
    for (i = 0; i < depth; i++) {
       if (!mpz_cmp_ui (cert2 [i][0], 0)) {
          cm_ecpp_one_step2 (cert2 [i], cert1 [i], i, modpoldir,
-               verbose, debug, stat);
+            tmpdir, verbose, debug, stat);
          if (filename != NULL) {
             cm_timer_stop (stat->timer [0]);
             cm_write_ecpp_cert2_line (f, cert2 [i], i, stat);
@@ -1693,7 +1697,8 @@ static void ecpp2 (mpz_t **cert2, mpz_t **cert1, int depth, char *filename,
       while (next < depth && mpz_cmp_ui (cert2 [next][0], 0))
          next++;
       if (next < depth && (rank = cm_mpi_queue_pop ()) != -1) {
-         cm_mpi_submit_ecpp_one_step2 (rank, next, cert1 [next], modpoldir);
+         cm_mpi_submit_ecpp_one_step2 (rank, next, cert1 [next], modpoldir,
+            tmpdir);
          next++;
       }
       else {
@@ -1740,7 +1745,8 @@ bool cm_ecpp (mpz_srcptr N, const char* modpoldir,
       read from (partially) and written to temporary files.
       If tmpdir is different from NULL, it refers to a directory where
       files can be stored representing precomputations that are independent
-      of the number under consideration (class numbers, primorials).
+      of the number under consideration (class numbers, primorials),
+      or checkpoints (factors of polynomial).
       If trust is set to true, then N is trusted to be a probable prime;
       otherwise a quick primality test is run.
       The variable phases should be one of 0, 1 or 2. If set to 1, only
@@ -1809,7 +1815,8 @@ bool cm_ecpp (mpz_srcptr N, const char* modpoldir,
          for (j = 0; j < 6; j++)
             mpz_init (cert2 [i][j]);
       }
-      ecpp2 (cert2, cert1, depth, filename2, modpoldir, verbose, debug, stat2);
+      ecpp2 (cert2, cert1, depth, filename2, modpoldir,
+         tmpdir, verbose, debug, stat2);
 
       if (print)
          cm_file_write_ecpp_cert_pari (stdout, cert2, depth);
diff --git a/lib/jmodp.c b/lib/jmodp.c
index 1d54632..8cd4122 100644
--- a/lib/jmodp.c
+++ b/lib/jmodp.c
@@ -2,7 +2,7 @@
 
 jmodp.c - code for obtaining a j-invariant from a class polynomial
 
-Copyright (C) 2009, 2010, 2021, 2022 Andreas Enge
+Copyright (C) 2009, 2010, 2021, 2022, 2023 Andreas Enge
 
 This file is part of CM.
 
@@ -35,12 +35,13 @@ static void quadraticx_eval_mod_p (mpz_ptr val, mpzx_srcptr g,
 static void quadraticxx_eval_mod_p (mpzx_ptr val, mpzx_t *g, mpzx_t *h,
    int deg, mpz_srcptr y, mpz_srcptr omega, mpz_srcptr p);
 static void get_root_mod_p (cm_param_srcptr param, cm_class_srcptr c,
-   mpz_ptr root, mpz_srcptr p, bool verbose, bool debug);
+   mpz_ptr root, mpz_srcptr p,
+   const char *tmpdir, bool verbose, bool debug);
 static void get_tower_root_mod_p (mpz_ptr root, mpzx_tower_srcptr t,
-   mpz_srcptr p, bool verbose, bool debug);
+   mpz_srcptr p, const char *tmpdir, bool verbose, bool debug);
 static void get_quadratic_tower_root_mod_p (mpz_ptr root,
    mpzx_tower_srcptr t, mpzx_tower_srcptr u, mpz_srcptr omega,
-   mpz_srcptr p, bool verbose, bool debug);
+   mpz_srcptr p, const char *tmpdir, bool verbose, bool debug);
 static mpz_t* get_j_mod_p_from_modular (int *no, const char* modpoldir,
    char type, int level, mpz_srcptr root, mpz_srcptr p);
 static mpz_t* simpleeta_cm_get_j_mod_p (cm_param_srcptr param,
@@ -165,7 +166,7 @@ static void quadraticxx_eval_mod_p (mpzx_ptr val, mpzx_t *g, mpzx_t *h,
 /*****************************************************************************/
 
 static void get_root_mod_p (cm_param_srcptr param, cm_class_srcptr c,
-   mpz_ptr root, mpz_srcptr p, bool verbose, bool debug)
+   mpz_ptr root, mpz_srcptr p, const char *tmpdir, bool verbose, bool debug)
    /* Return a root of the minimal polynomial modulo P in root. */
 
 {
@@ -174,7 +175,7 @@ static void get_root_mod_p (cm_param_srcptr param, cm_class_srcptr c,
    mpzx_t classpol_p;
 
    if (param->field == CM_FIELD_REAL)
-      mpzx_oneroot_split_mod (root, c->classpol, p, verbose, debug);
+      mpzx_oneroot_split_mod (root, c->classpol, p, tmpdir, verbose, debug);
    else {
       mpz_init (omega);
       cm_timer_start (clock);
@@ -185,7 +186,7 @@ static void get_root_mod_p (cm_param_srcptr param, cm_class_srcptr c,
 
       mpzx_init (classpol_p, c->classpol->deg);
       quadraticx_mod_p (classpol_p, c->classpol, c->classpol_c, omega, p);
-      mpzx_oneroot_split_mod (root, classpol_p, p, verbose, debug);
+      mpzx_oneroot_split_mod (root, classpol_p, p, tmpdir, verbose, debug);
 
       mpz_clear (omega);
       mpzx_clear (classpol_p);
@@ -195,18 +196,18 @@ static void get_root_mod_p (cm_param_srcptr param, cm_class_srcptr c,
 /*****************************************************************************/
 
 static void get_tower_root_mod_p (mpz_ptr root, mpzx_tower_srcptr t,
-   mpz_srcptr p, bool verbose, bool debug)
+   mpz_srcptr p, const char *tmpdir, bool verbose, bool debug)
    /* Let t be a class field tower in which p splits totally. Return a root
       mod p of the class polynomial behind the tower. */
 {
    int i;
    mpzx_t fp;
 
-   mpzx_oneroot_split_mod (root, t->W [0][0], p, verbose, debug);
+   mpzx_oneroot_split_mod (root, t->W [0][0], p, tmpdir, verbose, debug);
    for (i = 1; i < t->levels; i++) {
       mpzx_init (fp, t->d [i]);
       mpzxx_eval_mod_p (fp, t->W [i], t->d [i], root, p);
-      mpzx_oneroot_split_mod (root, fp, p, verbose, debug);
+      mpzx_oneroot_split_mod (root, fp, p, tmpdir, verbose, debug);
       mpzx_clear (fp);
    }
 }
@@ -215,7 +216,7 @@ static void get_tower_root_mod_p (mpz_ptr root, mpzx_tower_srcptr t,
 
 static void get_quadratic_tower_root_mod_p (mpz_ptr root,
    mpzx_tower_srcptr t, mpzx_tower_srcptr u, mpz_srcptr omega, mpz_srcptr p,
-   bool verbose, bool debug)
+   const char *tmpdir, bool verbose, bool debug)
    /* Assume that t+omega*u is a class field tower defined over the
       imaginary-quadratic field defined by omega in which p splits
       completely. Return a root modulo p of the class polynomial behind
@@ -226,13 +227,13 @@ static void get_quadratic_tower_root_mod_p (mpz_ptr root,
 
    mpzx_init (fp, t->d [0]);
    quadraticx_mod_p (fp, t->W [0][0], u->W [0][0], omega, p);
-   mpzx_oneroot_split_mod (root, fp, p, verbose, debug);
+   mpzx_oneroot_split_mod (root, fp, p, tmpdir, verbose, debug);
    mpzx_clear (fp);
    for (i = 1; i < t->levels; i++) {
       mpzx_init (fp, t->d [i]);
       quadraticxx_eval_mod_p (fp, t->W [i], u->W [i], t->d [i], root,
          omega, p);
-      mpzx_oneroot_split_mod (root, fp, p, verbose, debug);
+      mpzx_oneroot_split_mod (root, fp, p, tmpdir, verbose, debug);
       mpzx_clear (fp);
    }
 }
@@ -414,8 +415,8 @@ static mpz_t* simpleeta_cm_get_j_mod_p (cm_param_srcptr param,
 /*****************************************************************************/
 
 mpz_t* cm_class_get_j_mod_p (int *no, cm_param_srcptr param,
-   cm_class_srcptr c, mpz_srcptr p, const char* modpoldir,
-   bool verbose, bool debug)
+   cm_class_srcptr c, mpz_srcptr p, const char *modpoldir,
+   const char *tmpdir, bool verbose, bool debug)
    /* Assuming that c contains a class polynomial or a tower for param,
       allocate and return a list of potential j values modulo p.
       The number of allocated values is returned in no. */
@@ -428,16 +429,16 @@ mpz_t* cm_class_get_j_mod_p (int *no, cm_param_srcptr param,
    cm_timer_start (clock);
    mpz_init (root);
    if (!c->computed_tower)
-      get_root_mod_p (param, c, root, p, verbose, debug);
+      get_root_mod_p (param, c, root, p, tmpdir, verbose, debug);
    else {
       if (param->field == CM_FIELD_REAL)
-         get_tower_root_mod_p (root, c->tower, p, verbose, debug);
+         get_tower_root_mod_p (root, c->tower, p, tmpdir, verbose, debug);
       else {
          mpz_t omega;
          mpz_init (omega);
          quadratic_basis (omega, c->dfund, p);
          get_quadratic_tower_root_mod_p (root, c->tower, c->tower_c,
-            omega, p, verbose, debug);
+            omega, p, tmpdir, verbose, debug);
          mpz_clear (omega);
       }
    }
diff --git a/lib/mpi.c b/lib/mpi.c
index 591b29e..bf2fa6b 100644
--- a/lib/mpi.c
+++ b/lib/mpi.c
@@ -2,7 +2,7 @@
 
 mpicm.c - functions enabling MPI for CM
 
-Copyright (C) 2021, 2022 Andreas Enge
+Copyright (C) 2021, 2022, 2023 Andreas Enge
 
 This file is part of CM.
 
@@ -359,18 +359,22 @@ void cm_mpi_get_tonelli (mpz_ptr root, int rank, double *t)
 /*****************************************************************************/
 
 void cm_mpi_submit_ecpp_one_step2 (int rank, int job, mpz_t *cert1,
-   const char* modpoldir)
+   const char *modpoldir, const char *tmpdir)
    /* Submit the ECPP curve creation job of the given number to the worker
       of the given rank; the other parameters are as the input in
       cm_ecpp_one_step2 in ecpp.c */
 {
-   int i;
+   int len, i;
 
    MPI_Send (&job, 1, MPI_INT, rank, MPI_TAG_JOB_ECPP2, MPI_COMM_WORLD);
    for (i = 0; i < 4; i++)
       mpi_send_mpz (cert1 [i], rank, MPI_COMM_WORLD);
    MPI_Send (modpoldir, strlen (modpoldir), MPI_CHAR, rank, MPI_TAG_DATA,
       MPI_COMM_WORLD);
+   len = (tmpdir ? strlen (tmpdir) + 1 : 0); /* +1 for trailing \0 */
+   MPI_Send (&len, 1, MPI_INT, rank, MPI_TAG_DATA, MPI_COMM_WORLD);
+   if (tmpdir)
+      MPI_Send (tmpdir, len, MPI_CHAR, rank, MPI_TAG_DATA, MPI_COMM_WORLD);
 }
 
 /*****************************************************************************/
@@ -759,10 +763,21 @@ static void mpi_worker ()
          MPI_Recv (modpoldir, len, MPI_CHAR, 0, MPI_TAG_DATA,
             MPI_COMM_WORLD, &status);
          modpoldir [len] = '\0';
+         MPI_Recv (&len, 1, MPI_INT, 0, MPI_TAG_DATA, MPI_COMM_WORLD,
+            &status);
+         if (len > 0) {
+            tmpdir = (char *) malloc (len * sizeof (char));
+            MPI_Recv (tmpdir, len, MPI_CHAR, 0, MPI_TAG_DATA,
+               MPI_COMM_WORLD, &status);
+         }
+         else
+            tmpdir = 0;
 
-         cm_ecpp_one_step2 (cert2, cert1, job, modpoldir, verbose, debug,
-            stat);
+         cm_ecpp_one_step2 (cert2, cert1, job, modpoldir,
+            tmpdir, verbose, debug, stat);
          free (modpoldir);
+         if (len > 0)
+            free (tmpdir);
 
          MPI_Send (&job, 1, MPI_INT, 0, MPI_TAG_JOB_ECPP2, MPI_COMM_WORLD);
          for (i = 0; i < 6; i++)
diff --git a/lib/pari.c b/lib/pari.c
index 0fd44bd..e7f7e6f 100644
--- a/lib/pari.c
+++ b/lib/pari.c
@@ -520,7 +520,7 @@ static void mpzx_onefactor_split_mod (mpzx_ptr factor,
 /*****************************************************************************/
 
 void mpzx_oneroot_split_mod (mpz_ptr root, mpzx_srcptr f, mpz_srcptr p,
-   bool verbose, bool debug)
+   const char *tmpdir, bool verbose, bool debug)
    /* Compute in root a root of the polynomial f over the prime field
       of characteristic p, assuming that f splits completely. */
 {
@@ -548,19 +548,22 @@ void mpzx_oneroot_split_mod (mpz_ptr root, mpzx_srcptr f, mpz_srcptr p,
 
    while (F->deg != 1) {
       /* Try to read a factor of F from a checkpointing file. */
-      mpzx_init (factorloc, -1);
-      if (cm_file_read_factor ("/tmp", factorloc, F, p)) {
-         mpzx_set (F, factorloc);
-         if (debug)
-            printf ("    Read factor of degree %i\n", F->deg);
+      if (tmpdir != NULL) {
+         mpzx_init (factorloc, -1);
+         if (cm_file_read_factor (tmpdir, factorloc, F, p)) {
+            mpzx_set (F, factorloc);
+            if (debug)
+               cm_file_printf ("    Read factor of degree %i\n", F->deg);
+         }
+         mpzx_clear (factorloc);
       }
-      mpzx_clear (factorloc);
 
       /* Find a smaller factor. */
       mpzx_onefactor_split_mod (factor, F, p, debug);
 
       /* Write the factor to a checkpointing file. */
-      cm_file_write_factor ("/tmp", factor, F, p);
+      if (tmpdir != NULL)
+         cm_file_write_factor (tmpdir, factor, F, p);
 
       /* Replace F by the factor. */
       mpzx_set (F, factor);
-- 
GitLab