segprime2.c
text only
index
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <assert.h>
#define BITNUM (ull)8
#define BITNUM_MASK 7
#define BITNUM_SHIFT 3
#define DEFAULT_WORKARR_NUM (ull)700000
#define DEFAULT_DISPARR_NUM (unsigned long)100000
typedef unsigned long long ull;
ull findprimes_upthrough(ull limit, char *workarr, ull walen, ull **parr_out)
{
ull i, j, sq, left, *parr, wablen;
//initiate
sq = (ull)ceill(sqrtl((long double)limit));
wablen = walen * BITNUM;
//clear
for(i = 0; i < walen; i++) workarr[i] = 0;
//we only need to cancel out through the square root
for(i = 2, left = limit - 1; i <= sq; i++)
{
if(workarr[i >> BITNUM_SHIFT] & 1 << (i & BITNUM_MASK)) continue;
for(j = i * 2; j <= limit; j += i)
{
if(workarr[j >> BITNUM_SHIFT] & 1 << (j & BITNUM_MASK)) continue;
left--;
workarr[j >> BITNUM_SHIFT] |= 1 << (j & BITNUM_MASK);
}
}
parr = malloc(left * sizeof(ull));
assert(parr);
//copy prime data
for(i = 2, j = 0; i <= limit; i++)
{
if(workarr[i >> BITNUM_SHIFT] & 1 << (i & BITNUM_MASK)) continue;
parr[j++] = i;
}
*parr_out = parr;
return j;
}
//j = offset + num % fact[i]
ull findprimes(FILE *fd, ull start, ull end)
{
ull sq, cf, j, i, lim, total, wablen, walen, offset, pflen, untildlen, *pfarr;
unsigned long curdisp, idisp, curdlen;
char *workarr, *disparr;
//set up all the info
sq = (ull)ceill(sqrtl((long double)end));
wablen = sq > DEFAULT_WORKARR_NUM ? sq : DEFAULT_WORKARR_NUM;
walen = wablen / BITNUM + 1;
//allocate
workarr = malloc(walen);
assert(workarr);
//set up our display array (to minimize i/o)
//calc max number of digits in each number, + 1 for newline
curdlen = (unsigned long)ceill(logl((long double)end + 1) / logl((long double)10)) + 1;
disparr = malloc(curdlen * DEFAULT_DISPARR_NUM + 1);
assert(disparr);
//find primes <= square root (in other words, the prime factors)
pflen = findprimes_upthrough(sq, workarr, walen, &pfarr);
printf("found prime factors, starting on %lu segment(s) of %lu\n",
(unsigned long)ceill((long double)(end - start + 1) / (long double)wablen), (unsigned long)wablen);
if(pfarr[pflen - 1] >= start)
{
//find position at which this occurs
for(i = 0; i < pflen && pfarr[i] < start; i++) ;
//print
//these are few enough in number we don't need to bother with a buffer array
for(; i < pflen; i++)
fprintf(fd, "%s\n", ulltoa(pfarr[i], disparr, 10));
//reset range
start = pfarr[pflen - 1] + 1;
}
//initialize display array info
curdisp = idisp = 0;
//we need to know how far away 'start' is from the subsequent power of ten
for(untildlen = 1, curdlen = 0; untildlen <= start; untildlen *= 10, curdlen++) ;
untildlen -= start;
//now step through segments
for(offset = start, total = 0; offset <= end; offset += wablen)
{
//clear workarr
//for(i = 0; i < walen; i++) workarr[i] = 0;
memset(workarr, 0, walen);
//cancel out all multiples of each factor
for(i = 0; i < pflen; i++)
{
cf = pfarr[i];
if(cf * cf > offset + wablen) break;
//j = (cf - offset % cf) % cf;
j = offset % cf;
if(j) j = cf - j;
for(; j < wablen; j += cf)
workarr[j >> BITNUM_SHIFT] |= 1 << (j & BITNUM_MASK);
}
//in logarithmic steps
for(i = 0; i < wablen; )
{
//move up to next power of ten
if(!untildlen)
{
untildlen = 9 * (ull)pow(10, curdlen);
curdlen++;
}
//set limit
if(untildlen + i < wablen)
{
lim = i + untildlen;
untildlen = 0;
}
else
{
lim = wablen;
untildlen -= wablen;
}
//print primes
for(; i < lim; i++)
{
//not a prime
if(workarr[i >> BITNUM_SHIFT] & 1 << (i & BITNUM_MASK)) continue;
//bigger than end
if((j = i + offset) > end) goto continue_mainloop;
//print if we've reached our limit
if(curdisp == DEFAULT_DISPARR_NUM)
{
disparr[idisp] = 0;
fprintf(fd, "%s", disparr);
idisp = curdisp = 0;
}
//convert to string
ulltoa(j, &disparr[idisp], 10);
//move our pointer along
idisp += curdlen;
disparr[idisp++] = '\n';
//fputs(ulltoa(j, disparr, 10), fd);
curdisp++;
total++;
}
}
continue_mainloop:
continue;
}
//print what's left over
disparr[idisp] = 0;
fprintf(fd, "%s", disparr);
free(disparr);
free(pfarr);
free(workarr);
return total;
}
int main(int argc, char *argv[])
{
unsigned long total;
clock_t t1, t2;
FILE *fd;
if(argc != 4) return 0;
t1 = clock();
fd = fopen(argv[3], "wb");
assert(fd);
printf("file opened\n");
total = (unsigned long)findprimes(fd, atoll(argv[1]), atoll(argv[2]));
printf("total: %lu\n", total);
fclose(fd);
t2 = clock();
printf("time elapsed: %f\n", (double)(t2 - t1) / (double)CLOCKS_PER_SEC);
return 0;
}