// sieve.cc 
//  sample of how threads and bounded buffers (that use synchronization
//  primitives) can be used to do something quasi-useful, namely sieving
//  for primes. Note that no attempt is made to delete any dynamically
//  allocated objects, since they get cleaned up soon enough when the
//  program ends.  -Max Hailperin <max@gac.edu> 9/23/96

#include "system.h"
#include "boundedbuf.h"

class BBufPair{  // used just to pass two bounded buffers (input and output
public:          //   channels) into a sieve thread packaged together
  BBufPair(BBuf *inBuf, BBuf *outBuf){in = inBuf; out = outBuf;}
  BBuf *in, *out;
};

//----------------------------------------------------------------------
// Sieve
//   This procedure receives an int which should be cast back into a pointer
//   to a BBufPair, which in turn contains the sieves input and output
//   bounded buffers.  The first number on the input is prime, and
//   none of the remaining numbers are divisible by any prime less than that
//   initial prime.  The sieve is to copy all input numbers that are prime
//   to the output bounded buffer.
//----------------------------------------------------------------------

void
Sieve(int bufPairAddr){
  BBufPair *p = (BBufPair*) bufPairAddr;
  BBuf *in = p->in;
  BBuf *out = p->out;
  int divisor;
  if(!in->get(divisor)){
    out->shutdown();  // no input, so no output
    return;
  }
  out->put(divisor);  // we were told this is prime
  BBuf *intermediate = new BBuf;
  Thread *sieve = new Thread("lower-level sieve");
  sieve->Fork(Sieve, (int) new BBufPair(intermediate, out));
  int n;
  while(in->get(n)){
    if(n % divisor != 0){  // if we can't already prove it composite
      intermediate->put(n);// give the next sieve a chance
    }
  }
  intermediate->shutdown();// no more in, so no more out to next sieve
}
 
//----------------------------------------------------------------------
// Printer
//      This gets as its integer argument a value that it should cast
//      to be a pointer to a bounded buffer, which is the printer thread's
//      input source.  Each prime read from that bounded buffer gets printed
//      out, and a message when there are no more
//----------------------------------------------------------------------

void
Printer(int bufAddr){
  BBuf *in = (BBuf*)bufAddr;
  int prime, count=0;
  while(in->get(prime)){
    printf("prime %d is %d\n", ++count, prime);
  }
  printf("end of primes\n");
}

//----------------------------------------------------------------------
// ThreadTest
// 	This is the main startup procedure, called from main.  It
//      sets up the bounded buffer communication channels and the sieving
//      and printing threads, and then itself becomes the enumerating thread
//      that feeds the whole pipeline.
//----------------------------------------------------------------------

void
ThreadTest()
{
  BBuf *enumToSieve, *sieveToPrinter;
  Thread *sieveThread, *printerThread;

  DEBUG('t', "Entering SieveTest");
  enumToSieve = new BBuf;
  sieveToPrinter = new BBuf;
  sieveThread = new Thread("top-level sieve thread");
  printerThread = new Thread("printer thread");

  sieveThread->Fork(Sieve, (int) new BBufPair(enumToSieve, sieveToPrinter));
  printerThread->Fork(Printer, (int) sieveToPrinter);

  for(int i = 2; i <= 1000; i++){
    enumToSieve->put(i);
  }
  enumToSieve->shutdown();
}