/*
Copyright (C) 2020 William Hart
This file is part of FLINT.
FLINT is free software: you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License (LGPL) as published
by the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version. See .
*/
#include
#include
#include "flint.h"
#include "fmpz.h"
#include "fmpz_poly.h"
#include "mpn_extras.h"
#include "ulong_extras.h"
#if FLINT_REENTRANT && !FLINT_USES_TLS
#include
static pthread_once_t _factor_trial_initialised = PTHREAD_ONCE_INIT;
pthread_mutex_t _factor_trial_lock;
#endif
FLINT_TLS_PREFIX mp_ptr _factor_trial_tree[16 - (FLINT_BITS/32)];
FLINT_TLS_PREFIX int _factor_trial_tree_initialised = 0;
#if FLINT_REENTRANT && !FLINT_USES_TLS
void _tree_mutex_init(void)
{
pthread_mutex_init(&_factor_trial_lock, NULL);
}
#endif
void _cleanup_trial_tree(void)
{
slong i;
for (i = 0; i < 13 - (FLINT_BITS/32); i++)
flint_free(_factor_trial_tree[i]);
_factor_trial_tree_initialised = 0;
}
void
_factor_trial_tree_init(void)
{
slong i, j, k, m, n;
const mp_limb_t * primes;
#if FLINT_REENTRANT && !FLINT_USES_TLS
pthread_once(&_factor_trial_initialised, _tree_mutex_init);
pthread_mutex_lock(&_factor_trial_lock);
#endif
if (!_factor_trial_tree_initialised)
{
primes = n_primes_arr_readonly(3512);
flint_register_cleanup_function(_cleanup_trial_tree);
/*
Initialise mpn's in tree, each of which is thought of as an array
of products of primes
Note there are 3512 primes less than 32768
*/
for (i = 0; i < 13 - (FLINT_BITS/32); i++)
{
_factor_trial_tree[i] = (mp_ptr)
flint_malloc(4096/(FLINT_BITS/16)*sizeof(mp_limb_t));
}
/* initialise products in first layer of tree */
for (i = 0, j = 0; i < 3512; i+=(FLINT_BITS/16), j++)
{
#if FLINT64
_factor_trial_tree[0][j] =
primes[i]*primes[i + 1]*primes[i + 2]*primes[i + 3];
#else
_factor_trial_tree[0][j] =
primes[i]*primes[i + 1];
#endif
}
m = 0; /* level in tree that has been computed already */
n = 1; /* number of words per entry in that level */
k = 3512/(FLINT_BITS/16); /* number of entries on that level */
/* compute remaining levels of tree */
for ( ; m < 12 - (FLINT_BITS/32); m++, n*=2, k=(k+1)/2)
{
/* multiply entries in pairs */
for (i = 0, j = 0; j < k/2; j++, i+=(2*n))
{
mpn_mul_n(_factor_trial_tree[m + 1] + i,
_factor_trial_tree[m] + i,
_factor_trial_tree[m] + i + n, n);
}
if ((k % 2) == 1) /* copy across last entry if k is odd */
{
mpn_copyi(_factor_trial_tree[m + 1] + i,
_factor_trial_tree[m] + i, n);
flint_mpn_zero(_factor_trial_tree[m + 1] + i + n, n);
}
}
_factor_trial_tree_initialised = 1;
}
#if FLINT_REENTRANT && !FLINT_USES_TLS
pthread_mutex_unlock(&_factor_trial_lock);
#endif
}
int flint_mpn_factor_trial_tree(slong * factors,
mp_srcptr x, mp_size_t xsize, slong num_primes)
{
slong i, j, m, n, n2, nmax;
const mp_limb_t * primes;
mp_ptr gtemp; /* temporary space for recursive gcd's */
mp_ptr temp; /* temporary space for flint_mpn_gcd_full2 */
slong rlimbs[13 - (FLINT_BITS/32)]; /* gcd lengths at each level */
slong idx[13 - (FLINT_BITS/32)]; /* indexes of entries at each level */
slong offset; /* offset into gtemp */
slong valid_level; /* last level with valid gcd computed */
int numfacs = 0; /* number of prime factors found */
int gcd1; /* whether we hit gcd = 1, can prune this branch of tree */
_factor_trial_tree_init();
primes = n_primes_arr_readonly(num_primes);
gtemp = (mp_ptr)
flint_malloc((3*4096/(FLINT_BITS/16) + xsize)*sizeof(mp_limb_t));
temp = gtemp + 2*4096/(FLINT_BITS/16);
/* compute gcd of x with top level in tree */
m = FLINT_MAX(FLINT_BIT_COUNT(num_primes) - (FLINT_BITS/32), 0); /* top level in tree */
n = 4096/(FLINT_BITS/16); /* number of words of integer in tree */
for (i = 12 - (FLINT_BITS/32); i > m; i--)
n /= 2;
n2 = n; /* number of words per entry at this level */
nmax = n; /* save for later */
MPN_NORM(_factor_trial_tree[m] + 0, n);
if (n == 0) /* nothing to be done */
{
flint_free(gtemp);
return 0;
}
rlimbs[m] = flint_mpn_gcd_full2(gtemp, x, xsize,
_factor_trial_tree[m] + 0, n, temp);
if (rlimbs[m] == 1 && gtemp[0] == 1) /* gcd is 1, no factors found */
{
flint_free(gtemp);
return 0;
}
/* set indexes into arrays of entries at each level */
for (j = 0; j < m; j++)
idx[j] = -WORD(1);
idx[m] = 0;
valid_level = m; /* last level with valid gcd */
/* compute gcd with each entry at bottom level of tree */
for (i = 0; i < (num_primes + (FLINT_BITS/16) - 1)/(FLINT_BITS/16); i++)
{
gcd1 = 0;
n2 = nmax; /* number of words per entry */
offset = 0; /* offset into gtemp */
/* compute gcd's down the tree for this i */
for (j = m; j >= 0; j--)
{
/* must update indexes for current i */
if ((idx[j] & 1) != ((i >> j) & 1))
idx[j]++;
/* see if we need to compute new gcd for this level */
if (!gcd1 && (valid_level > j || (idx[j] & 1) != ((i >> j) & 1)))
{
n = n2;
MPN_NORM(_factor_trial_tree[j] + idx[j]*n2, n);
rlimbs[j] = flint_mpn_gcd_full2(gtemp + offset,
_factor_trial_tree[j] + idx[j]*n2, n,
gtemp + offset - 2*n2, rlimbs[j + 1], temp);
valid_level = j;
if (rlimbs[j] == 1 && gtemp[offset] == 1)
gcd1 = 1;
}
offset += n2;
n2/=2;
}
if (!gcd1)
{
for (j = 0; j < FLINT_BITS/16; j++)
{
/* check divisibility by primes with index 4*i + j */
if (flint_mpn_divisible_1_p(x, xsize, primes[(FLINT_BITS/16)*i + j]))
factors[numfacs++] = (FLINT_BITS/16)*i + j;
}
}
}
flint_free(gtemp);
return numfacs;
}