/*
GP;default(threadsize,32M);
GP;default(debugmem,0);
GP;default(factor_add_primes,1);
*/
install(Z_pollardbrent,GLL);
install(Z_ECM,GLLU);
isnull(x:vec=[])=x; \\ функция нужна для корректной обработки NULL
\\ который может вернуть Z_pollardbrent
my_pollard(n:int)={
my(
v:vec,
size:small=logint(n,2)+1,
tune:small=14,
\\ количество итераций вычисляется так же, как в исходном коде pari/gp
c0:small = if(size>301,
49152
,
tune + size-60 + ((size-73)>>1)*((size-70)>>3)*((size-56)>>4)
)
);
v=(isnull(Z_pollardbrent(n,c0,0)));
if(v!=[],return(v));
\\ Если хочется чтобы поллард пытался/не пытался зайти несколько раз,
\\ надо раскомментировать/закомментировать следующий for
/*
for(i:small=1,4,
v=isnull(Z_pollardbrent(n,c0,i));
if(v!=[],return(v));
);
*/
return([]);
}
install(mpqs,G);
\\ mpqs возвращает вектор с какой-то дичью
\\ но первый компонент это один из найденных множителей
\\ второй компонент это вероятно степень (например квадрат)
\\ но это не проверяется тут в надежде что возвращается
\\ всегда первая степень
my_mpqs(n:int)=my(divisor=component(mpqs(n),1));return([divisor,n/divisor]);
\\ заготтвка на будущее, если разобраться какие
\\ параметры rounds и b1 оптимальны
my_ecm(n,rounds,seed,b1)=isnull(Z_ECM(n,rounds,seed,b1));
/* Основная функция */
em4(myname,k_start:small,k_len:small,lim:small)={
/* myname - будет печататься в отчёте
* k_start - начало диапазона
* k_len - количество цепочек для просчёта
* lim - порог для пробных простых, оптимальный будет 2^20
*
*/
my(t0=getwalltime());\\Замер времени подготовки
\\ переменные специфичные для паттерна
my(v=[63, 3610, 3179, 12, 17797, 3362, 75, 392, 841, 18, 2209, 20, 1587, 242, 6727, 96, 14045, 338, 243, 68, 35131]);
my(n0=283938699868309713921641266159023371777169):int; \\ база
my(m=229403502054304075118105309394590566946400*2):int; \\ шаг
my(len=21):small; \\ длина цепочки
my(DN=48):small; \\ нужное количество делителей N в D(N,l)
my(pat_prime_lim:small=nextprime(vecmax(factor(vecprod(v))[,1])+1)); \\ следующее простое за максимальным в паттерне
\\ вычисляем остатки по всем малым простым диапазона
my(pr:vecsmall=Vecsmall(primes([pat_prime_lim,lim]))); \\ вектор с простыми
my(pr_q=#pr):small; \\ и сколько их там оказалось
my(pmods_n0:vecsmall=vectorsmall(pr_q,i,n0%pr[i])); \\вектор с остатками по простым по n0
my(pmods_m:vecsmall=vectorsmall(pr_q,i,m%pr[i])); \\ вектор с остатками по простым по m
\\ вспомогательные переменные
my(q=0,q1=0,q2=0,q3=0, q4=0, q5=0, q6=0, q7=0, q8=0):small;\\Разные счетчики
my(qp=0, qp1=0, qp2=0, qp3=0, c=0):small; \\Разные счётчики
my(ai=0):small; \\ степень с которой i-е простое входит в разложение на множители
my(d):small; \\ индекс числа от начала цепочки
my(nsigma:vecsmall=vectorsmall(len,d,numdiv(v[d]))); \\ Стартовый вектор количества делителей (сигма-ноль)
my(ns:vecsmall=vectorsmall(len,d,numdiv(v[d]))); \\ Вектор накопления количества делителей (сигма-ноль)
my(need_f:vecsmall=vectorsmall(len,d,1)); \\ вектор с флагом необходимости дофакторизации
my(nf=need_f); \\ Вектор накопления флагов дофакторизации
my(nv=v); \\ Вектор накопления факторизованной части
my(nr=vector(len,d,1)); \\ Вектор накопления остаток-частное
my(n):int; \\ Проверяемое число
my(pv:vec); \\ вектор, который вернёт поллард
\\ TODO: представим паттерн для красивой печати
\\ Печать стартового сообщения
print("Воркер ",myname, ": ++++++ Начинаю подготовку ++++++");
print("Воркер ",myname, ": Поиск D(",DN,",",len,")");
print("Воркер ",myname, ": База n0=",n0," бит:",logint(n0,2)+1);
print("Воркер ",myname, ": шаг m=",m, " бит:",logint(m,2)+1);
\\ TODO: придумать как красиво/полезно печатать паттерн
print("Воркер ",myname, ": Паттерн: секретный");
print("Воркер ",myname, ": Задание: Старт: ",k_start," шагов:",k_len," Таблица простых до:", lim);
print("Воркер ",myname, ": Подготовился за:",strtime(getwalltime()-t0));
print("Воркер ",myname, ": ++++++ Приступил к работе ++++++ ");
\\ +++++++++++++++++++ основной цикл
t0=getwalltime();\\Замер времени отсеивания
for(k:small=k_start,k_start+k_len-1, \\ идём по цепочкам
\\Здесь начинается проверка конкретной цепочки
\\ начинающейся с числа n0+k*m
\\ Фильтр "терапевтика" отбрасывает цепочки
\\ в которые попали множители 3^6 или кубы любых
\\ простых чисел от 5 до pat_prime_lim
n=n0+k*m+len-1;
if(n%3^6<len, q++;next);
forprime(p=5,pat_prime_lim-1, if(n%p^3<len, q++; next(2)));
\\ здесь цикл идёт по табличным простым числам
\\ инициализируем аккумуляторы для цепочки
\\ nv - вектор, накопленная часть факторизации, произведение всех известных
\\ на текущий момент простых множителей с учётом их степеней
\\ ns - вектор, количество делителей (сигма-ноль, numdiv) числа в цепочке
nv=v; ns=nsigma;
for(i=1,pr_q,\\ идём по простым для каждой цепочки
\\ вычисляем положение числа которое делится на это простое относительно начала цепочки
d=len-(pmods_n0[i]+k*pmods_m[i]+len-1)%pr[i];
\\ если на i-е простое ни одно число цепочки не разделилось, выходим на проверку следующего
if(d<1, next());
\\ на i-е простое разделилось d-е число цепочки
\\ выясняем не входит ли i-е простое в более высокой степени чем 1
ai=valuation(n0+k*m+d-1,pr[i]);
\\ теперь ai равно степени в которой i-е простое вошло в разложение
ns[d]*=ai+1; \\ накопили сигма-ноль
\\ проверим является ли сигма-ноль делителем DN
\\ если не является, отбрасываем всю цепочку
if(DN%ns[d]>0,q1++;next(2));
\\ проверим нет ли перебора делителей
if(ns[d]>DN/2, q1++;next(2)); \\ явный перебор делителей - отбрасываем всю цепочку
nv[d]*=(pr[i]^ai); \\ накопим по текущему числу в цепочке факторизованную часть
\\ перебора делителей пока нет
\\Если делителей уже набралась больше четверти
if(ns[d]>DN/4,
\\ то проверим композитный ли остаток-частное
\\ и если композитный, то будет перебор делителей
\\ и тогда отбрасываем цепочку
qp++;qp2++;
if(!ispseudoprime((n0+k*m+d-1)/(nv[d]),1), q1++;next(2)));
);
\\Проверка на переполнение по таблице простых закончена.
\\ next();
\\ Теперь числа в цепочке точно не содержат простых множителей, больших чем lim
n=n0+k*m; \\ начальное число цепочки
\\ Посчитаем остаток-частное для каждого числа из цепочки
for(d=1,len,nr[d]=(n+d-1)/(nv[d]));
\\ Проверим нет ли в цепочке чисел, у которых делителей явно меньше чем DN
for(d=1,len,
\\ Берём такие числа, у которых делителей меньше половины DN
if(ns[d]<(DN/2),
\\ накапливаем счетчик проверок на простоту
\\ если остаток-частное простой, то делителей будет недостаточно
\\ тогда отбрасываем эту и переходим к следующей цепочке
qp++; qp1++;
if(ispseudoprime(nr[d]),q2++;next(2));
);
);
\\ Теперь проверяем переполнение множителями
\\ большими чем lim и дофакторизованность
\\ nf - вектор с флагами где необходима дофакторизация, 1 значит необходима
\\ для начала, считаем что всем числам в цепочке необходима дофакторизация
nf=need_f;
for(d=1,len,
\\ если остаток-частное простой, то это число полностью дофакторизованно
if(nr[d]>1,qp3++;
if(ispseudoprime(nr[d]),
nr[d]=1;ns[d]=ns[d]*2;nf[d]=0;next()
);
); \\ его больше не трогаем
\\ в q6 посчитаем сколько раз полларда вызвали
q6++;
pv=my_pollard(nr[d]);
if(pv==[],pv=my_mpqs(nr[d])); \\ поллард не справился, ухнем mpqs-ом
while(pv!=[],
q7++; \\ сколько раз поллард/mpqs вернул непустой ответ
\\ TODO: добавить проверку что поллард вернул простое число
ns[d]*=(#pv-1)*2; \\ увеличиваем количество найденных делителей в 2 или 4 раза
if(ns[d]>(DN/2), q3++; next(3)); \\ если переполнение делителями, отбрасываем цепочку
nr[d]=nr[d]/pv[1]; \\ найден один множитель, уменьшим на него остаток-частное
if(#pv==3,nr[d]=nr[d]/pv[2]); \\ найден ещё один, уменьшим на него остаток-частное
\\ поллард справился, но надо проверить составной ли остаток-частное
qp++;qp3++;
if(!ispseudoprime(nr[d]),
if(ns[d]>(DN/4),q3++; next(3)) \\ остаток-частное составной - переполнение,
\\ отбрасываем всю цепочку
,
nf[d]=0;nr[d]=1;ns[d]=ns[d]*2;next(2); \\ остаток-частное простой
\\ помечаем что факторизация закончена
\\ и переходим к следующему d в цепочке
);
\\ TODO проверить а точно ли нужна ли дофакторизация
\\ нужна дофакторизация, опять пробуем полларда
pv=my_pollard(nr[d]);
if(pv==[],pv=my_mpqs(nr[d])); \\ поллард не справился, ухнем mpqs-ом
\\ если mpqs не справится, то будет прерывание всей программы с ошибкой
\\ поскольку mpqs вернёт в my_mpqs NULL а мы там ожидаем вектор
\\ TODO попробовать научиться обрабатывать любой выхлоп my_mpqs
\\ как дикий вектор так и NULL
);
);
\\ Включаем "тяжелую артиллерию"
\\ прошедшие полностью факторизуем, не будем откладывать.
\\ TODO: часть у нас уже полностью факторизована, учесть это
\\ пока оставлена полная проверка на случай ошибок в программе
q8++; \\ посчитаем сколько дошло до numdiv
\\ print("Дошло до numdiv: ", n);
c=0; for(i=1,len,if(numdiv(n+i-1)==DN,c++));if(c==len,q4++;print("Воркер ", myname,": Найдено D(",DN,",",len,"):",n),q5++,next());
);
\\ отчитаемся о работе
print("Воркер ", myname,": ++++++ Счёт закончен, доклад ниже ++++++");
print("Воркер ", myname,": Дошло до | numdiv: ",q8," | Найдено:",q4," | Отброшено:",q+q1+q2+q3+q5);
print("Воркер ", myname,": Отброшено по | терапевтика:",q," | таблица:",q1," | недобор:",q2," | Поллард/mpqs:",q3," | numdiv:",q5);
print("Воркер ", myname,": Проверок псевдопростых | всего:",qp," | из них в таблице:", qp2," | в недоборах:",qp1," | в полларде:",qp3);
print("Воркер ", myname,": Проверок поллардом/mpqs начато:", q6," Поллард/mpqs нашёл множитель:", q7);
print("Воркер ", myname,": Cкорость счета: ", 1000*k_len\(getwalltime()-t0)," цеп/сек. Время счёта:",strtime(getwalltime()-t0));
print("Воркер ", myname,": ++++++ Работа закончена ++++++");
return(q4);
}