// PrimesThreads.c

// threads-based program to find the number of primes between 2 and n;
// uses the Sieve of Eratosthenes, deleting all multiples of 2, all
// multiples of 3, all multiples of 5, etc. 

// for illustration purposes only; NOT claimed to be efficient

// Linux compilation:  gcc -g -o primesthreads PrimesThreads.c -lpthread -lm

// usage:  primesthreads n num_threads

#include <stdio.h>
#include <math.h>
#include <pthread.h>  // required for threads usage

#define MAX_N 1000000
#define MAX_THREADS 25

struct threadargs {
   int n;  // max number to check for primeness
   int threadnum;  // thread number
   int *nbptr;  // nextbase
   int *primearray;  // array recording prime/composite
   pthread_mutex_t *lockptr;  // lock pointer
};

// "crosses out" all odd multiples of k
void crossout(int k, int n, int *primearr)
{  int i;
   for (i = 3; i*k <= n; i += 2)  {
      primearr[i*k] = 0;
   }
}

// each thread runs this routine
void *worker(struct threadargs *ta) 
{  int lim,base,
       work = 0;  // amount of work done by this thread
   int n = ta->n;
   int tn = ta->threadnum;
   int *primes = ta->primearray;
   pthread_mutex_t *nblckptr = ta->lockptr;
   // no need to check multipliers bigger than sqrt(n)
   lim = sqrt(n);
   do  {
      // get next sieve multiplier, avoiding duplication across threads
      // lock the lock
      pthread_mutex_lock(nblckptr);
      base = *(ta->nbptr);
      *(ta->nbptr) += 2;
      // unlock
      pthread_mutex_unlock(nblckptr);
      if (base <= lim)  {
         // don't bother crossing out if base known composite
         if (primes[base])  {
            crossout(base,n,primes);
            work++;  // log work done by this thread
         }
      }
      else return work; 
   } while (1);
}

main(int argc, char **argv)
{  int nprimes,  // number of primes found 
       i,work;
   int n = atoi(argv[1]);  // range to check for primeness
   int nthreads = atoi(argv[2]);  // number of threads (not counting main())

   // shared variables
   int prime[MAX_N+1],  // in the end, prime[i] = 1 if i prime, else 0
       nextbase;  // next sieve multiplier to be used
   // lock for the shared variable nextbase
   pthread_mutex_t nextbaselock = PTHREAD_MUTEX_INITIALIZER;
   // ID structs for the threads
   pthread_t id[MAX_THREADS];

   // mark all even numbers nonprime, and the rest "prime until
   // shown otherwise"
   for (i = 3; i <= n; i++)  {
      if (i%2 == 0) prime[i] = 0;
      else prime[i] = 1;
   }
   nextbase = 3;
   struct threadargs thrdargs[MAX_THREADS];
   // get threads started
   for (i = 0; i < nthreads; i++)  {
      // this call says create a thread, record its ID in the array
      // id, and get the thread started executing the function worker(), 
      // passing the argument i to that function
      thrdargs[i].n = n;  // lock pointer
      thrdargs[i].threadnum = i;
      thrdargs[i].nbptr = &nextbase;
      thrdargs[i].primearray = prime;
      thrdargs[i].lockptr = &nextbaselock;
      pthread_create(&id[i],NULL,worker,&thrdargs[i]);
   }

   // wait for all done
   for (i = 0; i < nthreads; i++)  {
      // this call says wait until thread number id[i] finishes
      // execution, and to assign the return value of that thread to our
      // local variable work here
      pthread_join(id[i],&work);
      printf("%d values of base done\n",work);
   }
   
   // report results
   nprimes = 1;
   for (i = 3; i <= n; i++)
      if (prime[i])  {
         nprimes++;
      }
   printf("the number of primes found was %d\n",nprimes);

}
