/* A data passing in messages example
 * Parallel Sieve of Eratosthenes
 *
 * We start up a chain of entities which take a stream of integers from
 * the entity on their 'left', filtering some out, and passing the remaining
 * ones onto the entity on their right.
 *
 * The filtering works as follows:
 * Each entity remebers and prints out the first number it recieves,
 * it then filters out all numbers which are divisible by this number,
 * only passing on those that are not.
 *
 * A main controlling entity just spews all the natural numbers down
 * the pipe of entities, starting at 2. Now, we see that the first number each
 * entity recieves is a prime number. So if there are N entities in the pipe
 * then the first N primes will be printed.
 */

import eduni.simjava.*;

class Filter extends Sim_entity {
  private int myId;
  private int myPrime;

  public Filter(String name, int myId) {
    super(name);
    this.myId = myId;
  }

  public void body() {
    Sim_event ev = new Sim_event();
    int pass_data;

    // Get the first number and store it
    // Notice that fundamental data types such as int must be passed
    // in their wrapper class, in this case Integer, so we have a reference
    // to them.
    sim_wait(ev);
    myPrime = ((Integer)ev.get_data()).intValue();
    sim_trace(1, "My prime is "+myPrime);

    // Now filter numbers which myPrime divides, out of the stream
    while(true) {
      sim_wait(ev);
      pass_data = ((Integer)ev.get_data()).intValue();
      if((pass_data % myPrime) != 0)
        sim_schedule(myId+1, 1.0, 0, new Integer(pass_data));
    }
  }
}

// The master which spews out all the numbers from 2 to max_num
// down the pipe of entities
class Master extends Sim_entity {
  private int max_num;
  private Sim_uniform_obj delay;

  public Master(String name, int max_num) {
    super(name);
    this.max_num = max_num;
    delay = new Sim_uniform_obj("Delay", 0.1, 1.0, 234213);
  }

  public void body() {
    int i;

    for(i=2; i<max_num; i++) {
      sim_schedule(1, 0.0, i, new Integer(i));
      sim_hold(delay.sample());
    }
  }
}

// And finally a bucket which just accepts all the numbers at
// the end of the pipe, and throws them on the floor
class Bucket extends Sim_entity {
  public Bucket(String name) { super(name); }
  public void body() { Sim_event ev=null; while(true) { sim_wait(ev); } }
}

class Example3 {
  // The main, the first argument sould be the number of primes to produce
  public static void main(String args[]) {
    int num_primes, i;

    if(args.length != 1) {
      System.out.println("Usage:\n\tExample3 number_of_primes");
    } else {
      num_primes = Integer.parseInt(args[0]);
      Sim_system.initialise();
      Sim_system.set_auto_trace(false);

      System.out.println("Creating "+num_primes+" entities");
      Sim_system.add(new Master("Master", num_primes*num_primes));
      for(i=0; i<num_primes; i++)
        Sim_system.add(new Filter("Filter "+(i+1), i+1));
      Sim_system.add(new Bucket("Bucket"));

      System.out.println("Starting sim");
      Sim_system.run();
    }
  }
}
