#include <errno.h>
#include "pari.h"
#include "paripriv.h"
static char *
paristrtok_r(char *str, const char *delim, char **saveptr)
{
char *res;
if (!str) str = *saveptr;
str += strspn(str, delim);
if (!*str) return NULL;
res = str;
str += strcspn(str, delim);
if (*str) *str++ = 0;
*saveptr = str;
return res;
}
#include "mpqs.h"
static long
decimal_len(GEN N)
{
pari_sp av = avma;
long d = strlen(itostr(N));
avma = av; return d;
}
static int
mpqs_set_parameters(mpqs_handle_t *h)
{
long i;
const mpqs_parameterset_t *P;
h->digit_size_kN = decimal_len(h->kN);
if (h->digit_size_kN <= 9)
i = 0;
else if (h->digit_size_kN > MPQS_MAX_DIGIT_SIZE_KN)
return 0;
else
i = h->digit_size_kN - 9;
if (i >= 79)
pari_warn(warner, "MPQS: factoring this number will take %s hours:\nN = %Ps",
i >= 86 ? "many": "several", h->N);
if (DEBUGLEVEL >= 5)
{
err_printf("MPQS: kN = %Ps\n", h->kN);
err_printf("MPQS: kN has %ld decimal digits\n", h->digit_size_kN);
}
P = &(mpqs_parameters[i]);
h->tolerance = P->tolerance;
h->lp_scale = P->lp_scale;
h->size_of_FB = P->size_of_FB + h->_k->omega_k;
h->target_no_rels = (h->size_of_FB >= 200 ?
h->size_of_FB + 70 :
(mpqs_int32_t)(h->size_of_FB * 1.35));
h->M = P->M;
h->omega_A = P->omega_A;
h->no_B = 1UL << (P->omega_A - 1);
h->pmin_index1 = P->pmin_index1;
h->index0_FB = 3 + h->_k->omega_k;
h->first_sort_point = 10 * P->first_sort_point;
h->sort_pt_interval = 10 * P->sort_pt_interval;
if (DEBUGLEVEL >= 5)
{
double mb = (h->size_of_FB + 1)/(8.*1048576.) * h->target_no_rels;
err_printf("\t(estimated memory needed: %4.1fMBy)\n", mb);
}
return 1;
}
static mpqs_handle_t *
mpqs_handle_ctor(GEN N)
{
mpqs_handle_t *h = (mpqs_handle_t *) pari_calloc(sizeof(mpqs_handle_t));
h->N = N;
#ifdef MPQS_DEBUG_VERBOSE
err_printf("MPQS DEBUG: created handle @0x%p\n", (void *)h);
#endif
return h;
}
static mpqs_FB_entry_t *
mpqs_FB_ctor(mpqs_handle_t *h)
{
long size_FB_chunk = (h->size_of_FB + 3) * sizeof(mpqs_FB_entry_t);
long size_IAH_chunk = (h->size_of_FB + 2) * sizeof(mpqs_inv_A_H_t);
char *fbp = (char*)pari_malloc(size_FB_chunk + 64);
char *iahp = (char*)pari_malloc(size_IAH_chunk + 64);
long fbl, iahl;
h->FB_chunk = (void *)fbp;
h->invAH_chunk = (void *)iahp;
fbl = (((long)fbp) + 64) & ~0x3FL;
h->FB = (mpqs_FB_entry_t *)fbl;
iahl = (((long)iahp) + 64) & ~0x3FL;
h->inv_A_H = (mpqs_inv_A_H_t *)iahl;
return (mpqs_FB_entry_t *)fbl;
}
static void
mpqs_sieve_array_ctor(mpqs_handle_t *h)
{
long size = (h->M << 1) + 1;
mpqs_int32_t size_of_FB = h->size_of_FB;
h->sieve_array = (unsigned char *) pari_malloc(size * sizeof(unsigned char));
h->sieve_array_end = h->sieve_array + size - 2;
h->sieve_array_end[1] = 255;
h->candidates = (long *)pari_malloc(MPQS_CANDIDATE_ARRAY_SIZE * sizeof(long));
if (size_of_FB > 60) size_of_FB = 60;
h->relations = (char *) pari_malloc((8 + size_of_FB * 9) * sizeof(char));
h->relaprimes = (long *) pari_malloc((size_of_FB << 1) * sizeof(long));
#ifdef MPQS_USE_HISTOGRAMS
if (h->size_of_FB > MPQS_MIN_SIZE_FB_FOR_HISTO) {
h->do_histograms = 1;
h->histo_full = (long *) pari_calloc(128 * sizeof(long));
h->histo_lprl = (long *) pari_calloc(128 * sizeof(long));
h->histo_drop = (long *) pari_calloc(128 * sizeof(long));
}
#endif
}
static void
mpqs_poly_ctor(mpqs_handle_t *h)
{
mpqs_int32_t i;
long size_per = h->omega_A * sizeof(mpqs_per_A_prime_t);
h->per_A_pr = (mpqs_per_A_prime_t *) pari_calloc(size_per);
h->A = cgeti(h->omega_A + 2);
h->B = cgeti(h->omega_A + 3);
#ifdef MPQS_DEBUG
h->C = cgeti(h->omega_A + 4);
#endif
for (i = 0; i < h->omega_A; i++)
h->per_A_pr[i]._H = cgeti(h->omega_A + 2);
}
static void
mpqs_handle_dtor(mpqs_handle_t *h)
{
#define myfree(x) if(x) pari_free((void*)x)
myfree((h->per_A_pr));
myfree((h->relaprimes));
myfree(h->relations);
#ifdef MPQS_USE_HISTOGRAMS
myfree((h->histo_drop));
myfree((h->histo_lprl));
myfree((h->histo_full));
#endif
myfree((h->candidates));
myfree((h->sieve_array));
myfree((h->invAH_chunk));
myfree((h->FB_chunk));
myfree(h);
}
static ulong
mpqs_find_k(mpqs_handle_t *h)
{
const pari_sp av = avma;
const long N_mod_8 = mod8(h->N), N_mod_4 = N_mod_8 & 3;
forprime_t S;
struct {
const mpqs_multiplier_t *_k;
long np;
double value;
} cache[MPQS_POSSIBLE_MULTIPLIERS];
ulong p, i, nbk;
for (i = nbk = 0; i < numberof(cand_multipliers); i++)
{
const mpqs_multiplier_t *cand_k = &cand_multipliers[i];
long k = cand_k->k;
double v;
if ((k & 3) != N_mod_4) continue;
v = -0.35 * log2((double)k);
if ((k & 7) == N_mod_8) v += M_LN2;
cache[nbk].np = 0;
cache[nbk]._k = cand_k;
cache[nbk].value = v;
if (++nbk == MPQS_POSSIBLE_MULTIPLIERS) break;
}
if (nbk > MPQS_POSSIBLE_MULTIPLIERS) nbk = MPQS_POSSIBLE_MULTIPLIERS;
u_forprime_init(&S, 2, ULONG_MAX);
while ( (p = u_forprime_next(&S)) )
{
ulong Np = umodiu(h->N, p);
long kroNp, seen = 0;
if (!Np) return p;
kroNp = krouu(Np, p);
for (i = 0; i < nbk; i++)
{
if (cache[i].np > MPQS_MULTIPLIER_SEARCH_DEPTH) continue;
seen++;
if (krouu(cache[i]._k->k % p, p) == kroNp)
{
cache[i].value += log2((double) p)/p;
cache[i].np++;
}
}
if (!seen) break;
}
if (!p) pari_err_OVERFLOW("mpqs_find_k [ran out of primes]");
{
long best_i = 0;
double v = cache[0].value;
for (i = 1; i < nbk; i++)
if (cache[i].value > v) { best_i = i; v = cache[i].value; }
h->_k = cache[best_i]._k; avma = av; return 0;
}
}
static mpqs_FB_entry_t *
mpqs_create_FB(mpqs_handle_t *h, ulong *f)
{
const pari_sp av = avma;
mpqs_int32_t size = h->size_of_FB;
long i;
mpqs_uint32_t k = h->_k->k;
mpqs_FB_entry_t *FB;
forprime_t S;
FB = mpqs_FB_ctor(h);
FB[2].fbe_p = 2;
FB[2].fbe_flags = MPQS_FBE_CLEAR;
(void)u_forprime_init(&S, 3, ULONG_MAX);
for (i = 3; i < h->index0_FB; i++)
{
mpqs_uint32_t kp = (ulong)h->_k->kp[i-3];
if (MPQS_DEBUGLEVEL >= 7) err_printf(",<%lu>", (ulong)kp);
FB[i].fbe_p = kp;
FB[i].fbe_flags = MPQS_FBE_CLEAR;
FB[i].fbe_flogp = (float) log2((double) kp);
FB[i].fbe_sqrt_kN = 0;
}
while (i < size + 2)
{
ulong p = u_forprime_next(&S);
if (p > k || k % p)
{
ulong kN_mod_p = umodiu(h->kN, p);
long kr = krouu(kN_mod_p, p);
if (kr != -1)
{
if (kr == 0) { *f = p; return FB; }
FB[i].fbe_p = (mpqs_uint32_t) p;
FB[i].fbe_flags = MPQS_FBE_CLEAR;
FB[i].fbe_flogp = (float) log2((double)p);
FB[i++].fbe_sqrt_kN = (mpqs_uint32_t)Fl_sqrt(kN_mod_p, p);
}
}
}
avma = av;
if (MPQS_DEBUGLEVEL >= 7)
{
err_printf("MPQS: FB [-1,2");
for (i = 3; i < h->index0_FB; i++) err_printf(",<%lu>", FB[i].fbe_p);
for (; i < size + 2; i++) err_printf(",%lu", FB[i].fbe_p);
err_printf("]\n");
}
FB[i].fbe_p = 0;
h->largest_FB_p = FB[i-1].fbe_p;
for (i = h->index0_FB; FB[i].fbe_p != 0; i++)
if (FB[i].fbe_p >= h->pmin_index1) break;
h->index1_FB = i;
*f = 0; return FB;
}
static void
mpqs_set_sieve_threshold(mpqs_handle_t *h)
{
mpqs_FB_entry_t *FB = h->FB;
long i;
double log_maxval;
double log_multiplier;
h->l2sqrtkN = 0.5 * log2(h->dkN);
h->l2M = log2((double)h->M);
log_maxval = h->l2sqrtkN + h->l2M - MPQS_A_FUDGE;
log_multiplier = 232.0 / log_maxval;
h->sieve_threshold =
(unsigned char) (log_multiplier *
(log_maxval
- h->tolerance * log2((double)h->largest_FB_p)
)
) + 1;
if (h->sieve_threshold < 128) {
h->sieve_threshold = 128;
pari_warn(warner,
"MPQS: sizing out of tune, FB size or tolerance\n\ttoo large");
}
if (DEBUGLEVEL >= 5)
{
err_printf("MPQS: computing logarithm approximations for p_i in FB\n");
}
for (i = h->index0_FB; i < h->size_of_FB + 2; i++)
{
FB[i].fbe_logval =
(unsigned char) (log_multiplier * FB[i].fbe_flogp);
}
}
static int
mpqs_locate_A_range(mpqs_handle_t *h)
{
long i = h->index0_FB + 2*(h->omega_A) - 4;
double l2_target_pA;
mpqs_FB_entry_t *FB = h->FB;
h->l2_target_A = (h->l2sqrtkN - h->l2M - MPQS_A_FUDGE);
l2_target_pA = h->l2_target_A / h->omega_A;
while ((FB[i].fbe_p != 0) && (FB[i].fbe_flogp <= l2_target_pA)) i++;
#ifdef MPQS_DEBUG_LOCATE_A_RANGE
err_printf("MPQS DEBUG: omega_A=%ld, index0=%ld, i=%ld\n",
(long) h->omega_A, (long) h->index0_FB, i);
#endif
if (i > h->size_of_FB - 3)
{
pari_warn(warner,
"MPQS: sizing out of tune, FB too small or\n\tway too few primes in A");
return 0;
}
h->index2_FB = i - 1;
#ifdef MPQS_DEBUG_LOCATE_A_RANGE
err_printf("MPQS DEBUG: index2_FB = %ld\n", i - 1);
#endif
return 1;
}
#ifdef MPQS_USE_HISTOGRAMS
static void
mpqs_print_histo(mpqs_handle_t *h)
{
long i, tot = 0;
if (!h->do_histograms) return;
err_printf("\nMPQS: values from sieve vs. distribution of evaluated candidates:\n");
err_printf(" val ___full __lprel ___none ___total\n");
for (i = 127; i >= 0; i--)
{
long rowtot = h->histo_full[i] + h->histo_lprl[i] + h->histo_drop[i];
tot += rowtot;
if ((rowtot > 0) || (i == h->sieve_threshold))
err_printf("%s[%3d] %7ld %7ld %7ld %8ld\n",
i + 128 == h->sieve_threshold ? "^-" : " ", i + 128,
h->histo_full[i], h->histo_lprl[i], h->histo_drop[i],
rowtot);
}
err_printf(" (total evaluated candidates: %ld)\n", tot);
}
static int
mpqs_eval_histograms(mpqs_handle_t *h)
{
long tot_full = 0, tot_lprl = 0, tot_drop = 0, total = 0;
long target_full, i;
int th_full, th_base, th_drop;
int th = h->sieve_threshold - 128;
if (!h->do_histograms) return -1;
for (i = 127; i >= 0; i--)
{
tot_full += h->histo_full[i];
tot_lprl += h->histo_lprl[i];
tot_drop += h->histo_drop[i];
}
total = tot_full + tot_lprl + tot_drop;
if ((total < MPQS_MIN_CANDS_FOR_HISTO) ||
(tot_full < MPQS_MIN_FRELS_FOR_HISTO))
return 0;
th_full = th_drop = th_base = -1;
target_full = tot_full - (tot_full * MPQS_HISTO_FREL_QUANTILE) / 100.;
tot_full = 0;
for (i = 127; i >= th; i--)
{
if ((tot_full += h->histo_full[i]) >= target_full)
{
th_full = i; break;
}
}
for (i = 127; i >= th; i--)
{
if (h->histo_lprl[i] + 1 <
MPQS_HISTO_LPREL_BASEFLOW * h->histo_drop[i])
{
th_base = i; break;
}
}
tot_lprl = 0; tot_drop = 0;
for (i = 127; i >= th; i--)
{
tot_lprl += h->histo_full[i] + h->histo_lprl[i];
tot_drop += h->histo_drop[i];
if (tot_drop >
MPQS_HISTO_DROP_LIMIT * (tot_lprl + 1))
{
th_drop = i; break;
}
}
if (MPQS_DEBUGLEVEL >= 5)
{
mpqs_print_histo(h);
if (th_full >= 0)
err_printf("MPQS: threshold estimate for full rels: %d\n",
th_full + 128);
if (th_drop >= 0)
err_printf("MPQS: threshold estimate for useful candidates: %d\n",
th_drop + 128);
}
if ((th > 0) && (th_base <= th) &&
(h->histo_lprl[th] > (MPQS_MIN_FRELS_FOR_HISTO * 3.5)) )
{
h->sieve_threshold = th + 127;
if (MPQS_DEBUGLEVEL >= 4)
err_printf("MPQS: loosening sieve tolerance, new threshold %d\n",
h->sieve_threshold);
return 0;
}
th = (th_full < th_drop ? th_full : th_drop) + 128;
if (th > h->sieve_threshold)
{
h->sieve_threshold = th;
if (MPQS_DEBUGLEVEL >= 4)
err_printf("MPQS: tightening sieve tolerance, new threshold %d\n",
h->sieve_threshold);
}
return 1;
}
#endif
static char *
mpqs_get_filename(char *dir, const char *s)
{
char *buf = stack_malloc(strlen(dir) + strlen(s) + 2);
#if defined(__EMX__)
sprintf(buf, "%s\\%s", dir,s);
#else
sprintf(buf, "%s/%s", dir,s);
#endif
return buf;
}
static int
mpqs_relations_cmp(const void *a, const void *b)
{
char **sa = (char**) a;
char **sb = (char**) b;
long qa = strtol(*sa, NULL, 10);
long qb = strtol(*sb, NULL, 10);
if (qa < qb) return -1;
else if (qa > qb) return 1;
else return strcmp(*sa, *sb);
}
static void
pari_fputs(char *s, pariFILE *f)
{
if (fputs(s, f->file) < 0) pari_err_FILE("output file [fputs]", f->name);
}
#define min_bufspace 120UL
#define buflist_size 1024
static long
mpqs_sort_lp_file(char *filename)
{
pariFILE *pTMP;
FILE *TMP;
char *old_s, *buf, *cur_line;
char **sort_table, **buflist, **next_buflist, **buflist_head;
long i, j, count;
size_t length, bufspace;
pari_sp av=avma;
buflist_head = (char**) stack_malloc(buflist_size * sizeof(char*));
buflist = buflist_head;
*buflist++ = NULL;
pTMP = pari_fopen_or_fail(filename, READ);
TMP = pTMP->file;
buf = (char*) pari_malloc(MPQS_STRING_LENGTH * sizeof(char));
cur_line = buf;
bufspace = MPQS_STRING_LENGTH;
if (fgets(cur_line, bufspace, TMP) == NULL)
{
pari_free(buf); pari_fclose(pTMP);
avma = av; return 0;
}
*buflist++ = buf;
length = strlen(cur_line) + 1;
bufspace -= length;
sort_table = (char**)avma;
for (i=0, sort_table--; ; i++, sort_table--)
{
if ((i & 0xff) == 0) (void)new_chunk(0x100);
*sort_table = cur_line;
cur_line += length;
if (bufspace < min_bufspace)
{
if (MPQS_DEBUGLEVEL >= 7)
err_printf("MQPS: short of space -- another buffer for sorting\n");
buf = (char*) pari_malloc(MPQS_STRING_LENGTH * sizeof(char));
cur_line = buf;
bufspace = MPQS_STRING_LENGTH;
if (fgets(cur_line, bufspace, TMP) == NULL) { pari_free(buf); break; }
if (buflist - buflist_head >= buflist_size)
{
next_buflist = (char**) pari_malloc(buflist_size * sizeof(char*));
*next_buflist = (char*)buflist_head;
buflist_head = next_buflist;
buflist = buflist_head + 1;
}
*buflist++ = buf;
length = strlen(cur_line) + 1;
bufspace -= length; continue;
}
if (fgets(cur_line, bufspace, TMP) == NULL) break;
length = strlen(cur_line) + 1;
bufspace -= length;
if (bufspace == 0 && cur_line[length-2] != '\n')
{
size_t lg1;
if (MPQS_DEBUGLEVEL >= 7)
err_printf("MQPS: line wrap -- another buffer for sorting\n");
buf = (char*) pari_malloc(MPQS_STRING_LENGTH * sizeof(char));
if (buflist - buflist_head >= buflist_size)
{
next_buflist = (char**)pari_malloc(buflist_size * sizeof(char*));
*next_buflist = (char*)buflist_head;
buflist_head = next_buflist;
buflist = buflist_head + 1;
}
*buflist++ = buf;
(void)strcpy(buf, cur_line);
cur_line = buf + length - 1;
bufspace = MPQS_STRING_LENGTH - length + 1;
if (fgets(cur_line, bufspace, TMP) == NULL)
pari_err_FILE("TMP file [fgets]", pTMP->name);
lg1 = strlen(cur_line);
length += lg1;
bufspace -= (lg1 + 1);
cur_line = buf;
}
}
pari_fclose(pTMP);
qsort(sort_table, i, sizeof(char*), mpqs_relations_cmp);
pTMP = pari_fopen_or_fail(filename, WRITE);
old_s = sort_table[0];
pari_fputs(sort_table[0], pTMP);
count = 1;
for(j = 1; j < i; j++)
{
if (strcmp(old_s, sort_table[j]))
{
pari_fputs(sort_table[j], pTMP);
count++;
}
old_s = sort_table[j];
}
pari_fclose(pTMP);
if (MPQS_DEBUGLEVEL >= 6) err_printf("MPQS: done sorting one file.\n");
while (*--buflist)
{
if (buflist != buflist_head)
pari_free((void*) *buflist);
else
{
next_buflist = (char**)(*buflist);
pari_free((void*)buflist_head);
buflist_head = next_buflist;
buflist = buflist_head + buflist_size;
}
}
avma = av; return count;
}
static long
mpqs_append_file(pariFILE *f, FILE *fp1)
{
FILE *fp = f->file;
char line[MPQS_STRING_LENGTH];
long c = 0;
while (fgets(line, MPQS_STRING_LENGTH, fp1)) { pari_fputs(line, f); c++; }
if (fflush(fp)) pari_warn(warner, "error whilst flushing file %s", f->name);
pari_fclose(f); return c;
}
#define swap_lines() { char *line_tmp;\
line_tmp = line_new_old; \
line_new_old = line_new; \
line_new = line_tmp; }
static long
mpqs_mergesort_lp_file0(FILE *LPREL, FILE *LPNEW, pariFILE *pCOMB,
pariFILE *pTMP)
{
char line1[MPQS_STRING_LENGTH], line2[MPQS_STRING_LENGTH];
char line[MPQS_STRING_LENGTH];
char *line_new = line1, *line_new_old = line2;
long q_new, q_new_old = -1, q, i = 0, c = 0;
long comb_in_progress;
if ( !fgets(line_new, MPQS_STRING_LENGTH, LPNEW) )
{
i = mpqs_append_file(pTMP, LPREL);
return pCOMB ? 0 : i;
}
if (!fgets(line, MPQS_STRING_LENGTH, LPREL))
{
pari_fputs(line_new, pTMP);
if (!pCOMB)
{
i = mpqs_append_file(pTMP, LPNEW);
return i + 1;
}
q_new_old = atol(line_new);
swap_lines();
comb_in_progress = 0;
i = 0;
while (fgets(line_new, MPQS_STRING_LENGTH, LPNEW))
{
q_new = atol(line_new);
if (q_new_old == q_new)
{
if (!comb_in_progress)
{
pari_fputs(line_new_old, pCOMB);
comb_in_progress = 1;
}
pari_fputs(line_new, pCOMB);
i++;
}
else
{
q_new_old = q_new;
comb_in_progress = 0;
pari_fputs(line_new, pTMP);
swap_lines();
}
}
pari_fclose(pTMP); return i;
}
q_new = atol(line_new);
q = atol(line);
for(;;)
{
i = comb_in_progress = 0;
while (q > q_new)
{
if (!pCOMB || !comb_in_progress) pari_fputs(line_new, pTMP);
if (!pCOMB) c++;
else if (!comb_in_progress)
{
q_new_old = q_new;
swap_lines();
}
if (!fgets(line_new, MPQS_STRING_LENGTH, LPNEW))
{
pari_fputs(line, pTMP);
if (!pCOMB) c++; else c += i;
i = mpqs_append_file(pTMP, LPREL);
return pCOMB? c: c + i;
}
q_new = atol(line_new);
if (!pCOMB) continue;
if (q_new_old != q_new)
comb_in_progress = 0;
else
{
if (!comb_in_progress)
{
pari_fputs(line_new_old, pCOMB);
comb_in_progress = 1;
}
pari_fputs(line_new, pCOMB);
i++;
}
}
if (pCOMB) c += i;
i = 0;
comb_in_progress = 0;
while (q < q_new)
{
pari_fputs(line, pTMP);
if (!pCOMB) c++;
if (!fgets(line, MPQS_STRING_LENGTH, LPREL))
{
pari_fputs(line_new, pTMP);
i = mpqs_append_file(pTMP, LPNEW);
return pCOMB? c: c + i + 1;
}
else
q = atol(line);
}
while (q == q_new)
{
if (!strcmp(line_new, line))
{
;
}
else if (!pCOMB)
{
pari_fputs(line_new, pTMP);
c++;
}
else
{
if (!comb_in_progress)
{
pari_fputs(line, pCOMB);
comb_in_progress = 1;
}
pari_fputs(line_new, pCOMB);
i++;
}
if (!fgets(line_new, MPQS_STRING_LENGTH, LPNEW))
{
pari_fputs(line, pTMP);
if (!pCOMB) c++; else c += i;
i = mpqs_append_file(pTMP, LPREL);
return pCOMB? c: c + i;
}
else
q_new = atol(line_new);
}
if (pCOMB) c += i;
}
}
static long
mpqs_mergesort_lp_file(char *REL_str, char *NEW_str, char *TMP_str, pariFILE *pCOMB)
{
pariFILE *pREL = pari_fopen_or_fail(REL_str, READ);
pariFILE *pNEW = pari_fopen_or_fail(NEW_str, READ);
pariFILE *pTMP = pari_fopen_or_fail(TMP_str, WRITE);
long tp;
tp = mpqs_mergesort_lp_file0(pREL->file, pNEW->file, pCOMB, pTMP);
pari_fclose(pREL);
pari_fclose(pNEW);
pari_unlink(REL_str);
while (rename(TMP_str,REL_str))
{
if (errno != EEXIST)
pari_err_FILE("output file [rename]", REL_str);
}
if (MPQS_DEBUGLEVEL >= 6)
err_printf("MPQS: renamed file %s to %s\n", TMP_str, REL_str);
return tp;
}
#ifdef MPQS_DEBUG
static void
check_root(mpqs_handle_t *h, long p, long start)
{
long z = start - ((long)(h->M) % p);
if (smodis(addii(h->C, mului(z, addii(h->B, mului(z, h->A)))), p))
{
err_printf("MPQS: p = %ld\n", p);
err_printf("MPQS: A = %Ps\n", h->A);
err_printf("MPQS: B = %Ps\n", h->B);
err_printf("MPQS: C = %Ps\n", h->C);
err_printf("MPQS: z = %ld\n", z);
pari_err_BUG("MPQS: self_init: found wrong polynomial");
}
}
#endif
INLINE void
mpqs_increment(mpqs_uint32_t *x)
{
mpqs_uint32_t r1_mask, r01_mask, slider=1UL;
switch (*x & 0x1F)
{
case 29:
(*x)++; break;
case 26:
(*x) += 2; break;
case 1: case 3: case 6: case 9: case 11:
case 17: case 19: case 22: case 25: case 27:
(*x) += 3; return;
case 20:
(*x) += 4; break;
case 5: case 12: case 14: case 21:
(*x) += 5; return;
case 2: case 7: case 13: case 18: case 23:
(*x) += 6; return;
case 10:
(*x) += 7; return;
case 8:
(*x) += 8; break;
case 4: case 15:
(*x) += 12; return;
default:
r1_mask = ((*x ^ (*x - 1)) + 1) >> 1;
r01_mask = ((*x ^ (*x + r1_mask)) + r1_mask) >> 2;
if (r1_mask == r01_mask) { *x += r1_mask; break; }
if (r1_mask == 1) { *x += r01_mask; break; }
if (r1_mask == 2) { *x += (r01_mask>>1) + 1; return; }
while (r01_mask > r1_mask && slider < r1_mask)
{
r01_mask >>= 1; slider <<= 1;
}
*x += r01_mask + slider - 1;
return;
}
r1_mask = ((*x ^ (*x - 1)) + 1) >> 1;
r01_mask = ((*x ^ (*x + r1_mask)) + r1_mask) >> 2;
if (r1_mask == r01_mask) { *x += r1_mask; return; }
if (r1_mask == 1) { *x += r01_mask; return; }
if (r1_mask == 2) { *x += (r01_mask>>1) + 1; return; }
while (r01_mask > r1_mask && slider < r1_mask)
{
r01_mask >>= 1; slider <<= 1;
}
*x += r01_mask + slider - 1;
return;
}
#ifndef MPQS_DEBUG_SI_CHOOSE_PRIMES
# define MPQS_DEBUG_SI_CHOOSE_PRIMES 0
#endif
INLINE int
mpqs_si_choose_primes(mpqs_handle_t *h)
{
mpqs_FB_entry_t *FB = h->FB;
mpqs_per_A_prime_t *per_A_pr = h->per_A_pr;
double l2_last_p = h->l2_target_A;
mpqs_int32_t omega_A = h->omega_A;
int i, j, v2, prev_last_p_idx;
int room = h->index2_FB - h->index0_FB - omega_A + 4;
mpqs_uint32_t room_mask;
mpqs_int32_t p;
ulong bits;
if (h->bin_index == 0)
{
h->bin_index = (1UL << (omega_A - 1)) - 1;
prev_last_p_idx = 0;
}
else
{
for (i = 0; i < omega_A; i++)
MPQS_FLG(i) &= ~MPQS_FBE_DIVIDES_A;
prev_last_p_idx = MPQS_I(omega_A-1);
if (room > 30) room = 30;
room_mask = ~((1UL << room) - 1);
mpqs_increment(&h->bin_index);
if (h->index2_moved)
{
while ((h->bin_index & (room_mask | 0x3)) == 0)
mpqs_increment(&h->bin_index);
}
if ((h->bin_index & room_mask) != 0)
{
h->index2_FB += 2;
h->index2_moved = 1;
h->bin_index = 0;
if (MPQS_DEBUG_SI_CHOOSE_PRIMES || (MPQS_DEBUGLEVEL >= 5))
err_printf("MPQS: wrapping, more primes for A now chosen near FB[%ld] = %ld\n",
(long)h->index2_FB,
(long)FB[h->index2_FB].fbe_p);
return 0;
}
}
bits = h->bin_index;
if (MPQS_DEBUG_SI_CHOOSE_PRIMES || (MPQS_DEBUGLEVEL >= 6))
err_printf("MPQS: new bit pattern for primes for A: 0x%lX\n", bits);
j = h->index2_FB;
v2 = vals((long)bits);
if (v2) { j -= v2; bits >>= v2; }
for (i = omega_A - 2; i >= 0; i--)
{
MPQS_I(i) = j;
l2_last_p -= MPQS_LP(i);
MPQS_FLG(i) |= MPQS_FBE_DIVIDES_A;
bits &= ~1UL;
if (!bits) break;
v2 = vals((long)bits);
j -= v2;
bits >>= v2;
}
for (j = h->index2_FB + 1; (p = FB[j].fbe_p) != 0; j++)
{
if (FB[j].fbe_flogp > l2_last_p) break;
}
if ((p != 0) && (j == prev_last_p_idx))
{
j++; p = FB[j].fbe_p;
}
MPQS_I(omega_A - 1) = (p == 0 ?
h->size_of_FB + 1 :
j);
MPQS_FLG(omega_A - 1) |= MPQS_FBE_DIVIDES_A;
if (MPQS_DEBUG_SI_CHOOSE_PRIMES || (MPQS_DEBUGLEVEL >= 6))
{
err_printf("MPQS: chose primes for A");
for (i = 0; i < omega_A; i++)
{
err_printf(" FB[%ld]=%ld%s",
(long) MPQS_I(i),
(long) MPQS_AP(i),
i < omega_A - 1 ? "," : "\n");
}
}
return 1;
}
static void
mpqs_self_init(mpqs_handle_t *h)
{
const ulong size_of_FB = h->size_of_FB + 1;
mpqs_FB_entry_t *FB = h->FB;
mpqs_inv_A_H_t *inv_A_H = h->inv_A_H;
const pari_sp av = avma;
GEN p1, p2;
GEN A = h->A;
GEN B = h->B;
mpqs_per_A_prime_t *per_A_pr = h->per_A_pr;
long i, j;
long inv_A2;
ulong p;
#ifdef MPQS_DEBUG_AVMA
err_printf("MPQS DEBUG: enter self init, avma = 0x%lX\n", (ulong)avma);
#endif
if (++h->index_j == (mpqs_uint32_t)h->no_B)
{
h->index_j = 0;
h->index_i++;
}
if (h->index_j == 0)
{
if (!mpqs_si_choose_primes(h))
{
if (size_of_FB - h->index2_FB < 4) return;
(void) mpqs_si_choose_primes(h);
}
p1 = NULL;
for (i = 0; i < h->omega_A; i++)
{
p = (ulong) MPQS_AP(i);
p1 = p1 ? muliu(p1, p): utoipos(p);
}
affii(p1, A); avma = av;
p2 = NULL;
for (i = 0; i < h->omega_A; i++)
{
p = (ulong) MPQS_AP(i);
p1 = divis(A, (long)p);
p1 = muliu(p1, Fl_inv(umodiu(p1, p), p));
p1 = muliu(p1, MPQS_SQRT(i));
affii(remii(p1, A), MPQS_H(i));
p2 = p2 ? addii(p2, MPQS_H(i)) : MPQS_H(i);
}
affii(p2, B);
avma = av;
if (mod2(B) == 0)
affii(addii(B, mului(mod4(A), A)), B);
p1 = shifti(A, 1);
for (j = 3; (ulong)j <= size_of_FB; j++)
{
ulong mb, tmp1, tmp2, m;
if (FB[j].fbe_flags & MPQS_FBE_DIVIDES_A) continue;
p = (ulong)FB[j].fbe_p; m = h->M % p;
inv_A2 = Fl_inv(umodiu(p1, p), p);
mb = umodiu(B, p); if (mb) mb = p - mb;
tmp1 = Fl_sub(mb, FB[j].fbe_sqrt_kN, p);
tmp1 = Fl_mul(tmp1, inv_A2, p);
FB[j].fbe_start1 = (mpqs_int32_t)Fl_add(tmp1, m, p);
tmp2 = Fl_add(mb, FB[j].fbe_sqrt_kN, p);
tmp2 = Fl_mul(tmp2, inv_A2, p);
FB[j].fbe_start2 = (mpqs_int32_t)Fl_add(tmp2, m, p);
for (i = 0; i < h->omega_A - 1; i++)
{
ulong h = umodiu(MPQS_H(i), p) << 1; if (h > p) h -= p;
MPQS_INV_A_H(i,j) = Fl_mul(h, inv_A2, p);
}
}
}
else
{
ulong v2 = 0;
j = h->index_j;
while ((j & 1) == 0) { v2++; j >>= 1; }
p1 = shifti(MPQS_H(v2), 1);
if (j & 2)
{
for (j = 3; (ulong)j <= size_of_FB; j++)
{
if (FB[j].fbe_flags & MPQS_FBE_DIVIDES_A) continue;
p = (ulong)FB[j].fbe_p;
FB[j].fbe_start1 = Fl_sub(FB[j].fbe_start1, MPQS_INV_A_H(v2,j), p);
FB[j].fbe_start2 = Fl_sub(FB[j].fbe_start2, MPQS_INV_A_H(v2,j), p);
}
p1 = addii(B, p1);
}
else
{
for (j = 3; (ulong)j <= size_of_FB; j++)
{
if (FB[j].fbe_flags & MPQS_FBE_DIVIDES_A) continue;
p = (ulong)FB[j].fbe_p;
FB[j].fbe_start1 = Fl_add(FB[j].fbe_start1, MPQS_INV_A_H(v2,j), p);
FB[j].fbe_start2 = Fl_add(FB[j].fbe_start2, MPQS_INV_A_H(v2,j), p);
}
p1 = subii(B, p1);
}
affii(p1, B);
}
avma = av;
p1 = diviiexact(subii(h->kN, sqri(B)), shifti(A, 2));
for (i = 0; i < h->omega_A; i++)
{
ulong tmp, s;
p = (ulong) MPQS_AP(i);
tmp = Fl_div(umodiu(p1, p), umodiu(B, p), p); s = (tmp + h->M) % p;
FB[MPQS_I(i)].fbe_start1 = (mpqs_int32_t)s;
FB[MPQS_I(i)].fbe_start2 = (mpqs_int32_t)s;
}
if (MPQS_DEBUGLEVEL >= 6)
err_printf("MPQS: chose Q_%ld(x) = %Ps x^2 %c %Ps x + C\n",
(long) h->index_j, h->A,
signe(h->B) < 0? '-': '+', absi_shallow(h->B));
avma = av;
#ifdef MPQS_DEBUG
affii(negi(p1), h->C);
for (j = 3; j <= size_of_FB; j++)
{
check_root(h, FB[j].fbe_p, FB[j].fbe_start1);
check_root(h, FB[j].fbe_p, FB[j].fbe_start2); avma = av;
}
if (DEBUGLEVEL >= 6)
PRINT_IF_VERBOSE("MPQS: checking of roots of Q(x) was successful\n");
#endif
#ifdef MPQS_DEBUG_AVMA
err_printf("MPQS DEBUG: leave self init, avma = 0x%lX\n", (ulong)avma);
#endif
}
INLINE void
mpqs_sieve_p(unsigned char *begin, unsigned char *end,
long p4, long p, unsigned char log_p)
{
register unsigned char *e = end - p4;
while (e - begin >= 0)
{
(*begin) += log_p, begin += p;
(*begin) += log_p, begin += p;
(*begin) += log_p, begin += p;
(*begin) += log_p, begin += p;
}
while (end - begin >= 0)
(*begin) += log_p, begin += p;
}
static void
mpqs_sieve(mpqs_handle_t *h)
{
long p, l = h->index1_FB;
mpqs_FB_entry_t *ptr_FB;
unsigned char *sieve_array = h->sieve_array;
unsigned char *sieve_array_end = h->sieve_array_end;
for (ptr_FB = &(h->FB[l]); (p = ptr_FB->fbe_p) != 0; ptr_FB++, l++)
{
unsigned char log_p = ptr_FB->fbe_logval;
long start1 = ptr_FB->fbe_start1;
long start2 = ptr_FB->fbe_start2;
mpqs_sieve_p(sieve_array + start1, sieve_array_end, p << 2, p, log_p);
if (start1 != start2)
mpqs_sieve_p(sieve_array + start2, sieve_array_end, p << 2, p, log_p);
}
}
static long
mpqs_eval_sieve(mpqs_handle_t *h)
{
long x = 0, count = 0, M_2 = h->M << 1;
unsigned char th = h->sieve_threshold;
unsigned char *sieve_array = h->sieve_array;
long *candidates = h->candidates;
while (count < MPQS_CANDIDATE_ARRAY_SIZE - 1)
{
while (sieve_array[x] < th) x++;
if (x >= M_2) break;
candidates[count++] = x++;
}
candidates[count] = 0; return count;
}
static void
mpqs_add_factor(char **last, ulong ei, ulong pi) {
sprintf(*last, " %lu %lu", ei, pi);
*last += strlen(*last);
}
static void
mpqs_add_0(char **last) {
char *s = *last;
*s++ = ' ';
*s++ = '0';
*s++ = 0; *last = s;
}
#ifdef MPQS_DEBUG
static GEN
mpqs_factorback(mpqs_handle_t *h, char *relations)
{
char *s, *t = stack_strdup(relations), *tok;
GEN N = h->N, prod = gen_1;
long i;
mpqs_FB_entry_t *FB = h->FB;
s = paristrtok_r(t, " \n", &tok);
while (s != NULL)
{
long e = atol(s); if (!e) break;
s = paristrtok_r(NULL, " \n", &tok);
i = atol(s);
if (i == 1) { prod = Fp_neg(prod,N); s = paristrtok_r(NULL, " \n", &tok); continue; }
prod = Fp_mul(prod, Fp_powu(utoipos(FB[i].fbe_p), e, N), N);
s = paristrtok_r(NULL, " \n", &tok);
}
return prod;
}
#endif
static long
mpqs_eval_cand(mpqs_handle_t *h, long number_of_cand,
FILE *FREL, FILE *LPREL)
{
pari_sp av;
long number_of_relations = 0;
char *relations = h->relations;
long *relaprimes = h->relaprimes;
ulong i, pi;
mpqs_FB_entry_t *FB = h->FB;
GEN A = h->A;
GEN B = h->B;
int pii;
long *candidates = h->candidates;
av = avma;
#ifdef MPQS_DEBUG_AVMA
err_printf("MPQS DEBUG: enter eval cand, avma = 0x%lX\n", (ulong)avma);
#endif
for (i = 0; i < (ulong)number_of_cand; i++, avma = av)
{
GEN Qx, Qx_part, A_2x_plus_B, Y;
long powers_of_2, p;
long x = candidates[i];
long x_minus_M = x - h->M;
char *relations_end = relations;
int relaprpos = 0;
#ifdef MPQS_DEBUG_AVMA
err_printf("MPQS DEBUG: eval loop 1, avma = 0x%lX\n", (ulong)avma);
#endif
*relations_end = 0;
#ifdef MPQS_DEBUG_VERYVERBOSE
err_printf("%c", (char)('0' + i%10));
#endif
A_2x_plus_B = addii(mulis(A, 2 * x_minus_M), B);
Y = absi_shallow(A_2x_plus_B);
Qx = subii(sqri(A_2x_plus_B), h->kN);
#ifdef MPQS_DEBUG_AVMA
err_printf("MPQS DEBUG: eval loop 2, avma = 0x%lX\n", (ulong)avma);
#endif
if (!signe(Qx)) { PRINT_IF_VERBOSE("<+>"); continue; }
else if (signe(Qx) < 0) {
setabssign(Qx);
mpqs_add_factor(&relations_end, 1, 1);
}
powers_of_2 = vali(Qx);
Qx = shifti(Qx, -powers_of_2);
mpqs_add_factor(&relations_end, powers_of_2, 2);
Qx_part = A;
for (pi = 3; (p = FB[pi].fbe_p); pi++)
{
long tmp_p = x % p;
ulong ei = 0;
ei = FB[pi].fbe_flags & MPQS_FBE_DIVIDES_A;
if (tmp_p == FB[pi].fbe_start1 || tmp_p == FB[pi].fbe_start2)
{
relaprimes[relaprpos++] = pi;
relaprimes[relaprpos++] = 1 + ei;
Qx_part = muliu(Qx_part, p);
}
else if (ei)
{
relaprimes[relaprpos++] = pi;
relaprimes[relaprpos++] = 0;
}
}
Qx = diviiexact(Qx, Qx_part);
#ifdef MPQS_DEBUG_AVMA
err_printf("MPQS DEBUG: eval loop 3, avma = 0x%lX\n", (ulong)avma);
#endif
PRINT_IF_VERBOSE("a");
for (pii = 0; pii < relaprpos; pii+=2)
{
long remd_p;
ulong ei = relaprimes[pii+1];
GEN Qx_div_p;
pi = relaprimes[pii];
if ((mpqs_int32_t)pi < h->index0_FB) {
#ifdef MPQS_DEBUG
PRINT_IF_VERBOSE("\bk!");
#endif
mpqs_add_factor(&relations_end, 1, pi);
continue;
}
if (ei == 0)
{
mpqs_add_factor(&relations_end, 1, pi);
continue;
}
p = FB[pi].fbe_p;
#ifdef MPQS_DEBUG_CANDIDATE_EVALUATION
err_printf("MPQS DEBUG: Qx=%Ps p=%ld\n", Qx, p);
#endif
Qx_div_p = divis_rem(Qx, p, &remd_p);
while (remd_p == 0) {
ei++; Qx = Qx_div_p;
Qx_div_p = divis_rem(Qx, p, &remd_p);
}
mpqs_add_factor(&relations_end, ei, pi);
}
#ifdef MPQS_DEBUG_AVMA
err_printf("MPQS DEBUG: eval loop 4, avma = 0x%lX\n", (ulong)avma);
#endif
PRINT_IF_VERBOSE("\bb");
if (is_pm1(Qx))
{
mpqs_add_0(&relations_end);
fprintf(FREL, "%s :%s\n", itostr(Y), relations);
number_of_relations++;
#ifdef MPQS_USE_HISTOGRAMS
if (h->do_histograms) h->histo_full[sa[x]-128]++;
#endif
#ifdef MPQS_DEBUG
{
pari_sp av1 = avma;
GEN rhs = mpqs_factorback(h, relations);
GEN Qx_2 = remii(sqri(Y), h->N);
if (!equalii(Qx_2, rhs))
{
PRINT_IF_VERBOSE("\b(!)\n");
err_printf("MPQS: %Ps @ %Ps :%s\n", Y, Qx, relations);
err_printf("\tQx_2 = %Ps\n", Qx_2);
err_printf("\t rhs = %Ps\n", rhs);
pari_err_BUG("MPQS: wrong full relation found");
}
else
PRINT_IF_VERBOSE("\b(:)");
avma = av1;
}
#endif
}
else if (cmpis(Qx, h->lp_bound) > 0)
{
#ifdef MPQS_USE_HISTOGRAMS
if (h->do_histograms) h->histo_drop[sa[x]-128]++;
#endif
PRINT_IF_VERBOSE("\b.");
}
else
{
mpqs_add_0(&relations_end);
fprintf(LPREL, "%s @ %s :%s\n", itostr(Qx), itostr(Y), relations);
#ifdef MPQS_USE_HISTOGRAMS
if (h->do_histograms) h->histo_lprl[sa[x]-128]++;
#endif
#ifdef MPQS_DEBUG
{
pari_sp av1 = avma;
GEN rhs = mpqs_factorback(h, relations);
GEN Qx_2 = remii(sqri(Y), h->N);
rhs = modii(mulii(rhs, Qx), h->N);
if (!equalii(Qx_2, rhs))
{
PRINT_IF_VERBOSE("\b(!)\n");
err_printf("MPQS: %Ps @ %Ps :%s\n", Y, Qx, relations);
err_printf("\tQx_2 = %Ps\n", Qx_2);
err_printf("\t rhs = %Ps\n", rhs);
pari_err_BUG("MPQS: wrong large prime relation found");
}
else
PRINT_IF_VERBOSE("\b(;)");
avma = av1;
}
#endif
}
#ifdef MPQS_DEBUG_AVMA
err_printf("MPQS DEBUG: eval loop end, avma = 0x%lX\n", (ulong)avma);
#endif
}
PRINT_IF_VERBOSE("\n");
#ifdef MPQS_DEBUG_AVMA
err_printf("MPQS DEBUG: leave eval cand, avma = 0x%lX\n", (ulong)avma);
#endif
return number_of_relations;
}
typedef struct {
long q;
char Y[MPQS_STRING_LENGTH];
char E[MPQS_STRING_LENGTH];
} mpqs_lp_entry;
static void
mpqs_set_exponents(long *ei, char *r)
{
char *s, b[MPQS_STRING_LENGTH], *tok;
long e;
strcpy(b, r);
s = paristrtok_r(b, " \n", &tok);
while (s != NULL)
{
e = atol(s); if (!e) break;
s = paristrtok_r(NULL, " \n", &tok);
ei[ atol(s) ] += e;
s = paristrtok_r(NULL, " \n", &tok);
}
}
static void
set_lp_entry(mpqs_lp_entry *e, char *buf)
{
char *s1, *s2;
s1 = buf; s2 = strchr(s1, ' '); *s2 = '\0';
e->q = atol(s1);
s1 = s2 + 3; s2 = strchr(s1, ' '); *s2 = '\0';
strcpy(e->Y, s1);
s1 = s2 + 3; s2 = strchr(s1, '\n'); *s2 = '\0';
strcpy(e->E, s1);
}
static long
mpqs_combine_large_primes(mpqs_handle_t *h,
FILE *COMB, pariFILE *pFNEW, GEN *f)
{
pari_sp av0 = avma, av, av2;
char new_relation[MPQS_STRING_LENGTH], buf[MPQS_STRING_LENGTH];
mpqs_lp_entry e[2];
long *ei, ei_size = h->size_of_FB + 2;
long old_q;
GEN inv_q, Y1, Y2, new_Y, new_Y1;
long i, l, c = 0;
*f = NULL;
if (!fgets(buf, MPQS_STRING_LENGTH, COMB)) return 0;
ei = (long *) new_chunk(ei_size);
av = avma;
set_lp_entry(&e[0], buf);
i = 1;
old_q = e[0].q;
while (!invmod(utoipos(old_q), h->N, &inv_q))
{
inv_q = gcdii(inv_q, h->N);
if (is_pm1(inv_q) || equalii(inv_q, h->N))
{
#ifdef MPQS_DEBUG
err_printf("MPQS: skipping relation with non-invertible q\n");
#endif
if (!fgets(buf, MPQS_STRING_LENGTH, COMB)) { avma = av0; return 0; }
avma = av;
set_lp_entry(&e[0], buf);
old_q = e[0].q; continue;
}
*f = gerepileuptoint(av0, inv_q);
return c;
}
Y1 = strtoi(e[0].Y);
av2 = avma;
while (fgets(buf, MPQS_STRING_LENGTH, COMB))
{
set_lp_entry(&e[i], buf);
if (e[i].q != old_q)
{
old_q = e[i].q;
avma = av;
if (!invmod(utoipos(old_q), h->N, &inv_q))
{
inv_q = gcdii(inv_q, h->N);
if (is_pm1(inv_q) || equalii(inv_q, h->N))
{
#ifdef MPQS_DEBUG
err_printf("MPQS: skipping relation with non-invertible q\n");
#endif
old_q = -1;
av2 = avma = av;
continue;
}
*f = gerepileuptoint(av0, inv_q);
return c;
}
Y1 = strtoi(e[i].Y);
i = 1 - i;
av2 = avma;
continue;
}
c++;
memset((void *)ei, 0, ei_size * sizeof(long));
mpqs_set_exponents(ei, e[0].E);
mpqs_set_exponents(ei, e[1].E);
Y2 = strtoi(e[i].Y);
new_Y = modii(mulii(mulii(Y1, Y2), inv_q), h->N);
new_Y1 = subii(h->N, new_Y);
if (abscmpii(new_Y1, new_Y) < 0) new_Y = new_Y1;
strcpy(new_relation, itostr(new_Y));
strcat(new_relation, " :");
if (ei[1] & 1) strcat(new_relation, " 1 1");
for (l = 2; l < ei_size; l++)
if (ei[l])
{
sprintf(buf, " %ld %ld", ei[l], l);
strcat(new_relation, buf);
}
strcat(new_relation, " 0");
if (DEBUGLEVEL >= 6)
{
err_printf("MPQS: combining\n");
err_printf(" {%ld @ %s : %s}\n", old_q, e[1-i].Y, e[1-i].E);
err_printf(" * {%ld @ %s : %s}\n", e[i].q, e[i].Y, e[i].E);
err_printf(" == {%s}\n", new_relation);
}
strcat(new_relation, "\n");
#ifdef MPQS_DEBUG
{
GEN Qx_2, prod;
char *s = strchr(new_relation, ':') + 2;
pari_sp av1 = avma;
Qx_2 = modii(sqri(new_Y), h->N);
prod = mpqs_factorback(h, s);
if (!equalii(Qx_2, prod))
pari_err_BUG("MPQS: combined large prime relation is false");
avma = av1;
}
#endif
pari_fputs(new_relation, pFNEW);
avma = av2;
}
if (DEBUGLEVEL >= 4)
err_printf("MPQS: combined %ld full relation%s\n", c, (c!=1 ? "s" : ""));
avma = av0; return c;
}
static GEN
stream_read_F2m(pariFILE *pFREL, long rows, long cols, long *fpos)
{
FILE *FREL = pFREL->file;
long i, e, p;
char buf[MPQS_STRING_LENGTH], *s;
GEN m;
long space = 2*((nbits2nlong(rows)+3)*cols+1);
if ((long)((GEN)avma - (GEN)pari_mainstack->bot) < space)
{
pari_sp av = avma;
m = gclone(zero_F2m(rows, cols));
if (DEBUGLEVEL>=4)
err_printf("MPQS: allocating %ld words for Gauss\n",space);
avma = av;
}
else
m = zero_F2m_copy(rows, cols);
for (i = 0;; i++)
{
char *tok=NULL;
if (i < cols && (fpos[i] = ftell(FREL)) < 0)
pari_err_FILE("full relations file [ftell]", pFREL->name);
if (!fgets(buf, MPQS_STRING_LENGTH, FREL)) break;
s = strchr(buf, ':');
if (!s) pari_err_FILE("full relations file [strchr]", pFREL->name);
s = paristrtok_r(s+2, " \n", &tok);
while (s != NULL)
{
e = atol(s); if (!e) break;
s = paristrtok_r(NULL, " \n", &tok);
p = atol(s);
if (e & 1) F2m_set(m, p, i+1);
s = paristrtok_r(NULL, " \n", &tok);
}
}
if (i != cols)
{
err_printf("MPQS: full relations file %s than expected",
i > cols ? "longer" : "shorter");
pari_err(e_BUG, "MPQS [panicking]");
}
return m;
}
static GEN
mpqs_add_relation(GEN Y_prod, GEN N, long *ei, char *rel)
{
pari_sp av = avma;
GEN res;
char *s, *tok=NULL;
s = strchr(rel, ':') - 1;
*s = '\0';
res = remii(mulii(Y_prod, strtoi(rel)), N);
s = paristrtok_r(s + 3, " \n", &tok);
while (s != NULL)
{
long e = atol(s), i;
if (!e) break;
s = paristrtok_r(NULL, " \n", &tok);
i = atol(s);
ei[i] += e;
s = paristrtok_r(NULL, " \n", &tok);
}
return gerepileuptoint(av, res);
}
static char*
mpqs_get_relation(char *buf, long pos, pariFILE *pFREL)
{
if (fseek(pFREL->file, pos, SEEK_SET))
pari_err_FILE("FREL file [fseek]", pFREL->name);
if (!fgets(buf, MPQS_STRING_LENGTH, pFREL->file))
pari_err_FILE("FREL file [fgets]", pFREL->name);
return buf;
}
static int
split(GEN N, GEN *e, GEN *res)
{
ulong mask;
long flag;
GEN base;
if (MR_Jaeschke(N)) { *e = gen_1; return 1; }
if (Z_issquareall(N, &base))
{
*res = base;
*e = gen_2;
if (DEBUGLEVEL >= 5) err_printf("MPQS: decomposed a square\n");
return 1;
}
mask = 7;
if ( (flag = is_357_power(N, &base, &mask)) )
{
*res = base;
*e = utoipos(flag);
if (DEBUGLEVEL >= 5)
err_printf("MPQS: decomposed a %s\n",
(flag == 3 ? "cube" :
(flag == 5 ? "5th power" : "7th power")));
return 1;
}
*e = gen_0; return 0;
}
static GEN
mpqs_solve_linear_system(mpqs_handle_t *h, pariFILE *pFREL, long rel)
{
GEN N = h->N, X, Y_prod, X_plus_Y, D1, res, new_res;
mpqs_FB_entry_t *FB = h->FB;
pari_sp av=avma, av2, av3;
long *fpos, *ei;
long i, j, H_cols, H_rows;
long res_last, res_next, res_size, res_max;
GEN m, ker_m;
long done, rank;
char buf[MPQS_STRING_LENGTH];
fpos = (long *) pari_malloc(rel * sizeof(long));
m = stream_read_F2m(pFREL, h->size_of_FB+1, rel, fpos);
if (DEBUGLEVEL >= 7)
err_printf("\\\\ MATRIX READ BY MPQS\nFREL=%Ps\n",m);
ker_m = F2m_ker_sp(m,0); rank = lg(ker_m)-1;
if (isclone(m)) gunclone(m);
if (DEBUGLEVEL >= 4)
{
if (DEBUGLEVEL >= 7)
{
err_printf("\\\\ KERNEL COMPUTED BY MPQS\n");
err_printf("KERNEL=%Ps\n",ker_m);
}
err_printf("MPQS: Gauss done: kernel has rank %ld, taking gcds...\n", rank);
}
H_rows = rel;
H_cols = rank;
if (!H_cols)
{
if (DEBUGLEVEL >= 3)
pari_warn(warner, "MPQS: no solutions found from linear system solver");
pari_free(fpos);
avma = av; return NULL;
}
av2 = avma;
if (rank > (long)BITS_IN_LONG - 2)
res_max = LONG_MAX;
else
res_max = 1L<<rank;
res_size = 8;
res = cgetg(2*res_size+1, t_VEC);
for (i=2*res_size; i; i--) res[i] = 0;
res_next = res_last = 1;
ei = (long *) pari_malloc((h->size_of_FB + 2) * sizeof(long));
for (i = 1; i <= H_cols; i++)
{
X = Y_prod = gen_1;
memset((void *)ei, 0, (h->size_of_FB + 2) * sizeof(long));
av3 = avma;
for (j = 1; j <= H_rows; j++)
{
if (F2m_coeff(ker_m, j, i))
Y_prod = mpqs_add_relation(Y_prod, N, ei,
mpqs_get_relation(buf, fpos[j-1], pFREL));
if (gc_needed(av3,1))
{
if(DEBUGMEM>1) pari_warn(warnmem,"[1]: mpqs_solve_linear_system");
Y_prod = gerepileuptoint(av3, Y_prod);
}
}
Y_prod = gerepileuptoint(av3, Y_prod);
av3 = avma;
for (j = 2; j <= h->size_of_FB + 1; j++)
if (ei[j])
{
if (ei[j] & 1) pari_err_BUG("MPQS (relation is a nonsquare)");
X = remii(mulii(X,
Fp_powu(utoipos(FB[j].fbe_p), (ulong)ei[j]>>1, N)),
N);
if (gc_needed(av3,1))
{
if(DEBUGMEM>1) pari_warn(warnmem,"[2]: mpqs_solve_linear_system");
X = gerepileupto(av3, X);
}
}
X = gerepileuptoint(av3, X);
if (MPQS_DEBUGLEVEL >= 1)
{
if (!dvdii(subii(sqri(X), sqri(Y_prod)), N))
{
err_printf("MPQS: X^2 - Y^2 != 0 mod N\n");
err_printf("\tindex i = %ld\n", i);
pari_warn(warner, "MPQS: wrong relation found after Gauss");
}
}
done = 0;
X_plus_Y = addii(X, Y_prod);
if (res_next < 3)
{
D1 = gcdii(X_plus_Y, N);
if (is_pm1(D1) || equalii(D1,N)) { avma = av3; continue; }
if (DEBUGLEVEL >= 5)
err_printf("MPQS: splitting N after %ld kernel vector%s\n",
i+1, (i? "s" : ""));
gel(res,1) = diviiexact(N, D1);
gel(res,2) = D1;
res_last = res_next = 3;
if ( split(gel(res,1), &gel(res,res_size+1), &gel(res,1)) ) done++;
if ( split(D1, &gel(res,res_size+2), &gel(res,2)) ) done++;
if (done == 2) break;
if (res_max == 2) break;
if (DEBUGLEVEL >= 5)
err_printf("MPQS: got two factors, looking for more...\n");
}
else
{
for (j=1; j < res_next; j++)
{
if (gel(res,res_size+j) && gel(res,res_size+j) != gen_0)
{
done++; continue;
}
av3 = avma;
D1 = gcdii(X_plus_Y, gel(res,j));
if (is_pm1(D1) || equalii(D1, gel(res,j))) { avma = av3; continue; }
if (DEBUGLEVEL >= 5)
err_printf("MPQS: resplitting a factor after %ld kernel vectors\n",
i+1);
if (res_next > res_size)
{
long i1, size = 2*res_size;
GEN RES;
if (size > res_max) size = res_max;
RES = cgetg(2*size+1, t_VEC);
for (i1=2*size; i1>=res_next; i1--) gel(RES,i1) = NULL;
for (i1=1; i1<res_next; i1++)
{
icopyifstack(gel(res,i1), gel(RES,i1));
if ( gel(res,res_size+i1) )
icopyifstack(gel(res,res_size+i1), gel(RES,size+i1));
}
res = RES; res_size = size;
}
diviiz(gel(res,j), D1, gel(res,j));
gel(res,res_next) = D1;
if (split( gel(res,j), &gel(res,res_size+j), &gel(res,j)) ) done++;
(void)split(D1, &gel(res,res_size+res_next), &gel(res,res_next));
if (++res_next > res_max)
{
break;
}
}
if (res_next > res_last)
{
res_last = res_next - 1;
if (DEBUGLEVEL >= 5)
err_printf("MPQS: got %ld factors%s\n", res_last,
(done < res_last ? ", looking for more..." : ""));
res_last = res_next;
}
if (res_next > res_max || done == res_next - 1) break;
}
if (gc_needed(av2,1))
{
long i1;
if(DEBUGMEM>1) pari_warn(warnmem,"[3]: mpqs_solve_linear_system");
new_res = cgetg(lg(res), t_VEC);
for (i1=2*res_size; i1>=res_next; i1--) new_res[i1] = 0;
for (i1=1; i1<res_next; i1++)
{
icopyifstack(gel(res,i1), gel(new_res,i1));
if (gel(res,res_size+i1))
icopyifstack(gel(res,res_size+i1), gel(new_res,res_size+i1));
}
res = gerepileupto(av2, new_res);
}
}
pari_free(ei); pari_free(fpos);
if (res_next < 3) { avma = av; return NULL; }
res_last = res_next - 1;
new_res = cgetg(3*res_last + 1, t_VEC);
if (DEBUGLEVEL >= 6)
err_printf("MPQS: wrapping up vector of %ld factors\n", res_last);
for (i=1,j=1; i <= res_last; i++)
{
GEN F = gel(res, res_size+i);
icopyifstack(gel(res,i), gel(new_res,j++));
gel(new_res,j++) =
F ? (F == gen_0 ? gen_1
: (isonstack(F) ? icopy(F) : F))
: gen_1;
gel(new_res,j++) =
F == gen_0 ? gen_0 :
NULL;
if (DEBUGLEVEL >= 6)
err_printf("\tpackaging %ld: %Ps ^%ld (%s)\n", i, res[i],
itos(gel(new_res,j-2)), (F == gen_0 ? "comp." : "unknown"));
}
return gerepileupto(av, new_res);
}
static GEN
mpqs_i(mpqs_handle_t *handle)
{
GEN N = handle->N, fact;
mpqs_int32_t size_of_FB;
mpqs_FB_entry_t *FB;
mpqs_int32_t M;
ulong p;
long lp_bound;
long lp_scale;
long tc;
long tp;
long tff = 0;
long tfc;
double tfc_ratio = 0;
ulong sort_interval;
ulong followup_sort_interval;
long percentage = 0;
double net_yield;
long total_full_relations = 0, total_partial_relations = 0, total_no_cand = 0;
long vain_iterations = 0, good_iterations = 0, iterations = 0;
#ifdef MPQS_USE_HISTOGRAMS
long histo_checkpoint = MPQS_MIN_CANDS_FOR_HISTO;
#endif
pariFILE *pFNEW, *pLPNEW, *pCOMB, *pFREL, *pLPREL;
char *dir, *COMB_str, *FREL_str, *FNEW_str, *LPREL_str, *LPNEW_str, *TMP_str;
pari_timer T;
pari_sp av = avma;
if (DEBUGLEVEL >= 4)
{
timer_start(&T);
err_printf("MPQS: number to factor N = %Ps\n", N);
}
handle->digit_size_N = decimal_len(N);
if (handle->digit_size_N > MPQS_MAX_DIGIT_SIZE_KN)
{
pari_warn(warner, "MPQS: number too big to be factored with MPQS,\n\tgiving up");
return NULL;
}
if (DEBUGLEVEL >= 4)
err_printf("MPQS: factoring number of %ld decimal digits\n",
handle->digit_size_N);
p = mpqs_find_k(handle);
if (p) { avma = av; return utoipos(p); }
if (DEBUGLEVEL >= 5) err_printf("MPQS: found multiplier %ld for N\n",
handle->_k->k);
handle->kN = muliu(N, handle->_k->k);
if (!mpqs_set_parameters(handle))
{
pari_warn(warner,
"MPQS: number too big to be factored with MPQS,\n\tgiving up");
return NULL;
}
size_of_FB = handle->size_of_FB;
M = handle->M;
sort_interval = handle->first_sort_point;
followup_sort_interval = handle->sort_pt_interval;
if (DEBUGLEVEL >= 5)
err_printf("MPQS: creating factor base and allocating arrays...\n");
FB = mpqs_create_FB(handle, &p);
if (p) { avma = av; return utoipos(p); }
mpqs_sieve_array_ctor(handle);
mpqs_poly_ctor(handle);
lp_bound = handle->largest_FB_p;
if (lp_bound > MPQS_LP_BOUND) lp_bound = MPQS_LP_BOUND;
lp_scale = handle->lp_scale;
if (lp_scale >= handle->largest_FB_p)
lp_scale = handle->largest_FB_p - 1;
lp_bound *= lp_scale;
handle->lp_bound = lp_bound;
handle->dkN = gtodouble(handle->kN);
mpqs_set_sieve_threshold(handle);
if (!mpqs_locate_A_range(handle)) return NULL;
if (DEBUGLEVEL >= 4)
{
err_printf("MPQS: sieving interval = [%ld, %ld]\n", -(long)M, (long)M);
err_printf("MPQS: size of factor base = %ld\n",
(long)size_of_FB);
err_printf("MPQS: striving for %ld relations\n",
(long)handle->target_no_rels);
err_printf("MPQS: coefficients A will be built from %ld primes each\n",
(long)handle->omega_A);
err_printf("MPQS: primes for A to be chosen near FB[%ld] = %ld\n",
(long)handle->index2_FB,
(long)FB[handle->index2_FB].fbe_p);
err_printf("MPQS: smallest prime used for sieving FB[%ld] = %ld\n",
(long)handle->index1_FB,
(long)FB[handle->index1_FB].fbe_p);
err_printf("MPQS: largest prime in FB = %ld\n",
(long)handle->largest_FB_p);
err_printf("MPQS: bound for `large primes' = %ld\n", (long)lp_bound);
}
if (DEBUGLEVEL >= 5)
{
err_printf("MPQS: sieve threshold = %u\n",
(unsigned int)handle->sieve_threshold);
}
if (DEBUGLEVEL >= 4)
{
err_printf("MPQS: first sorting at %ld%%, then every %3.1f%% / %3.1f%%\n",
sort_interval/10, followup_sort_interval/10.,
followup_sort_interval/20.);
}
handle->index_j = (mpqs_uint32_t)-1;
if (DEBUGLEVEL >= 5) err_printf("MPQS: starting main loop\n");
dir = pari_unique_dir("MPQS");
TMP_str = mpqs_get_filename(dir, "LPTMP");
FREL_str = mpqs_get_filename(dir, "FREL");
FNEW_str = mpqs_get_filename(dir, "FNEW");
LPREL_str = mpqs_get_filename(dir, "LPREL");
LPNEW_str = mpqs_get_filename(dir, "LPNEW");
COMB_str = mpqs_get_filename(dir, "COMB");
#define unlink_all()\
pari_unlink(FREL_str);\
pari_unlink(FNEW_str);\
pari_unlink(LPREL_str);\
pari_unlink(LPNEW_str);\
if (pCOMB) pari_unlink(COMB_str);\
while(rmdir(dir)) \
{ if (errno != ENOTEMPTY && errno != EEXIST) break; } \
pari_free(dir);
pFREL = pari_fopen_or_fail(FREL_str, WRITE); pari_fclose(pFREL);
pLPREL = pari_fopen_or_fail(LPREL_str, WRITE); pari_fclose(pLPREL);
pFNEW = pari_fopen_or_fail(FNEW_str, WRITE);
pLPNEW= pari_fopen_or_fail(LPNEW_str, WRITE);
pCOMB = NULL;
for(;;)
{
iterations++;
mpqs_self_init(handle);
if (handle->bin_index == 0)
{
if (DEBUGLEVEL >= 2)
err_printf("MPQS: Ran out of primes for A, giving up.\n");
pari_fclose(pFNEW);
pari_fclose(pLPNEW);
unlink_all(); avma = av; return NULL;
}
memset((void*)(handle->sieve_array), 0, (M << 1) * sizeof(unsigned char));
mpqs_sieve(handle);
tc = mpqs_eval_sieve(handle);
total_no_cand += tc;
if (DEBUGLEVEL >= 6)
err_printf("MPQS: found %lu candidate%s\n", tc, (tc==1? "" : "s"));
if (tc)
{
long t = mpqs_eval_cand(handle, tc, pFNEW->file, pLPNEW->file);
total_full_relations += t;
tff += t;
good_iterations++;
}
#ifdef MPQS_USE_HISTOGRAMS
if (handle->do_histograms && !handle->done_histograms &&
total_no_cand >= histo_checkpoint)
{
int res = mpqs_eval_histograms(handle);
if (res >= 0)
{
if (res > 0)
handle->do_histograms = 0;
else
histo_checkpoint += (MPQS_MIN_CANDS_FOR_HISTO );
}
else
handle->done_histograms = 1;
}
#endif
percentage =
(long)((1000.0 * total_full_relations) / handle->target_no_rels);
if ((ulong)percentage < sort_interval) continue;
if (DEBUGLEVEL >= 3)
{
if (DEBUGLEVEL >= 4)
err_printf("\nMPQS: passing the %3.1f%% sort point, time = %ld ms\n",
sort_interval/10., timer_delay(&T));
else
err_printf("\nMPQS: passing the %3.1f%% sort point\n",
sort_interval/10.);
err_flush();
}
pari_fclose(pLPNEW);
(void)mpqs_sort_lp_file(LPNEW_str);
pCOMB = pari_fopen_or_fail(COMB_str, WRITE);
tp = mpqs_mergesort_lp_file(LPREL_str, LPNEW_str, TMP_str, pCOMB);
pari_fclose(pCOMB);
pLPNEW = pari_fopen_or_fail(LPNEW_str, WRITE);
tfc = 0;
if (tp > 0)
{
pCOMB = pari_fopen_or_fail(COMB_str, READ);
tfc = mpqs_combine_large_primes(handle, pCOMB->file, pFNEW, &fact);
pari_fclose(pCOMB);
if (fact)
{
if (DEBUGLEVEL >= 4)
{
err_printf("\nMPQS: split N whilst combining, time = %ld ms\n",
timer_delay(&T));
err_printf("MPQS: found factor = %Ps\n", fact);
}
pari_fclose(pLPNEW);
pari_fclose(pFNEW);
unlink_all();
return gerepileupto(av, fact);
}
total_partial_relations += tp;
}
pari_fclose(pFNEW);
(void)mpqs_sort_lp_file(FNEW_str);
total_full_relations = mpqs_mergesort_lp_file(FREL_str, FNEW_str, TMP_str, NULL);
percentage =
(long)((1000.0 * total_full_relations) / handle->target_no_rels);
net_yield =
(total_full_relations * 100.) / (total_no_cand ? total_no_cand : 1);
vain_iterations =
(long)((1000.0 * (iterations - good_iterations)) / iterations);
if ((tfc >= 16) && (tff >= 20))
tfc_ratio = (tfc + tff + 0.) / tff;
tff = 0;
if (percentage >= 1000)
sort_interval = percentage + 2;
else if (percentage >= 820)
{
if (tfc_ratio > 1.)
{
if (percentage + (followup_sort_interval >> 1) * tfc_ratio > 994)
{
sort_interval = (ulong)(percentage + 2 +
(1000 - percentage) / tfc_ratio);
}
else if (percentage >= 980)
sort_interval = percentage + 8;
else
sort_interval = percentage + (followup_sort_interval >> 1);
}
else
{
if (percentage >= 980)
sort_interval = percentage + 10;
else
sort_interval = percentage + (followup_sort_interval >> 1);
if (sort_interval >= 1000 && percentage < 1000)
sort_interval = 1000;
}
}
else
sort_interval = percentage + followup_sort_interval;
if (DEBUGLEVEL >= 4)
{
err_printf("MPQS: done sorting%s, time = %ld ms\n",
tp > 0 ? " and combining" : "", timer_delay(&T));
err_printf("MPQS: found %3.1f%% of the required relations\n",
percentage/10.);
if (DEBUGLEVEL >= 5)
{
err_printf("MPQS: found %ld full relations\n",
total_full_relations);
if (lp_scale > 1)
err_printf("MPQS: (%ld of these from partial relations)\n",
total_partial_relations);
err_printf("MPQS: Net yield: %4.3g full relations per 100 candidates\n",
net_yield);
err_printf("MPQS: %4.3g full relations per 100 polynomials\n",
(total_full_relations * 100.) / iterations);
err_printf("MPQS: %4.1f%% of the polynomials yielded no candidates\n",
vain_iterations/10.);
err_printf("MPQS: next sort point at %3.1f%%\n", sort_interval/10.);
}
}
if (percentage < 1000)
{
pFNEW = pari_fopen_or_fail(FNEW_str, WRITE);
continue;
}
if (DEBUGLEVEL >= 4)
err_printf("\nMPQS: starting Gauss over F_2 on %ld relations\n",
total_full_relations);
pFREL = pari_fopen_or_fail(FREL_str, READ);
fact = mpqs_solve_linear_system(handle, pFREL, total_full_relations);
pari_fclose(pFREL);
if (fact)
{
if (DEBUGLEVEL >= 4)
{
err_printf("\nMPQS: time in Gauss and gcds = %ld ms\n", timer_delay(&T));
if (typ(fact) == t_INT) err_printf("MPQS: found factor = %Ps\n", fact);
else
{
long j, nf = (lg(fact)-1)/3;
if (nf == 2)
err_printf("MPQS: found factors = %Ps\n\tand %Ps\n",
fact[1], fact[4]);
else
{
err_printf("MPQS: found %ld factors =\n", nf);
for (j=1; j<=nf; j++)
err_printf("\t%Ps%s\n", fact[3*j-2], (j<nf ? "," : ""));
}
}
}
pari_fclose(pLPNEW);
unlink_all();
return gerepileupto(av, fact);
}
else
{
if (DEBUGLEVEL >= 4)
{
err_printf("\nMPQS: time in Gauss and gcds = %ld ms\n",timer_delay(&T));
err_printf("MPQS: no factors found.\n");
if (percentage <= MPQS_ADMIT_DEFEAT)
err_printf("\nMPQS: restarting sieving ...\n");
else
err_printf("\nMPQS: giving up.\n");
}
if (percentage > MPQS_ADMIT_DEFEAT)
{
pari_fclose(pLPNEW);
unlink_all(); avma = av; return NULL;
}
pFNEW = pari_fopen_or_fail(FNEW_str, WRITE);
}
}
}
GEN
mpqs(GEN N)
{
mpqs_handle_t *handle = mpqs_handle_ctor(N);
GEN fact = mpqs_i(handle);
mpqs_handle_dtor(handle); return fact;
}