class myThread extends Thread {

   myThread() {} // default constructor

   private static long startTime = System.currentTimeMillis();

   protected static void nap(int ms) {
      try {
         sleep(ms);
      } catch (InterruptedException e) {
      }
   }

   protected static long age() {
      return System.currentTimeMillis()-startTime;
   }

   protected static int getarg(int i, int n, String args[]) {
      int value;
      try {
         value = Integer.parseInt(args[i-1]);
      } catch (ArrayIndexOutOfBoundsException e) {
         return n;
      } catch (NumberFormatException e) {
         return n;
      }
      return value;
   }

   protected static float getarg(int i, float f, String args[]) {
      float value;
      try {
         value = Float.valueOf(args[i-1]).floatValue();
      } catch (ArrayIndexOutOfBoundsException e) {
         return f;
      } catch (NumberFormatException e) {
         return f;
      }
      return value;
   }
}

class BinarySemaphore {

   private boolean locked = false;

   BinarySemaphore() {}  // constructors
   BinarySemaphore(boolean initial) {locked = initial;}
   BinarySemaphore(int initial) {locked = (initial == 0);}

   public synchronized void P() {
      while (locked) {
         try {
            wait();
         } catch (InterruptedException e) {
         }
      }
      locked = true;
   }

   public synchronized void V() {
      if (locked) notify();
      locked = false;
   }
}

class Semaphore {

   private int value = 0;

   Semaphore() {value = 0;}  // constructors
   Semaphore(int initial) {value = initial;}

   public synchronized void P() {
      value--;
      if (value < 0) {
         try {
            wait();
         } catch (InterruptedException e) {
            System.err.println("Semaphore.P: wait InterruptedException");
         }
      }
   }

   public synchronized void V() {
      value++;
      if (value <= 0) notify();
   }
}

class BoundedBuffer {

   private int slots = 0;
   private float[] buffer = null;
   private int count = 0;
   private int putIn = 0;
   private int takeOut = 0;
   private BinarySemaphore mutex = null;
   private Semaphore elements = null;
   private Semaphore spaces = null;

   private BoundedBuffer() {} // no instantiation without a parameter

   BoundedBuffer(int slots) {
      this.slots = slots;
      System.out.println("BoundedBuffer created with " + slots + " slots");
      mutex = new BinarySemaphore(1);
      elements = new Semaphore(0);
      spaces = new Semaphore(slots);
      buffer = new float[slots];
      count = 0;
      takeOut = 0;
      putIn = 0;
   }

   public void deposit(float item) {
      spaces.P();
      buffer[putIn] = item;
      putIn = (putIn + 1) % slots;
      mutex.P();
      count++;
      System.out.println("deposit: count = " + count);
      mutex.V();
      elements.V();
   }

   public float fetch() {
      float item;
      elements.P();
      item = buffer[takeOut];
      takeOut = (takeOut + 1) % slots;
      mutex.P();
      count--;
      System.out.println("fetch: count = " + count);
      mutex.V();
      spaces.V();
      return item;
   }
}

class aProducer extends myThread {

   private int num;
   private int sleepInterval;
   private BoundedBuffer bb;

   private aProducer() {}   // instantiation without parameters not allowed

   aProducer(int num, int sleepInterval, BoundedBuffer bb) {  // constructor
      this.num = num;
      this.sleepInterval = sleepInterval;
      this.bb = bb;
      System.out.println("Producer #" + num + " constructed, sleepInterval=" +
         sleepInterval);
   }

   public void run() {
      int napping;
      float item;
      System.out.println("Producer #" + num + " running");
      while (true) {
         napping = (int) (Math.random() * (sleepInterval*1000));
         System.out.println("age()=" + age() + " Producer #" +
            num + " constructing item for " + napping + " ms");
         nap(napping);
         item = (float) Math.random();
         System.out.println("age()=" + age() + " Producer #" +
            num + " wants to deposit item " + item);
         bb.deposit(item);
      }
   }
}

class aConsumer extends myThread {

   private int num;
   private int sleepInterval;
   private BoundedBuffer bb;

   private aConsumer() {}   // instantiation without parameters not allowed

   aConsumer(int num, int sleepInterval, BoundedBuffer bb) {  // constructor
      this.num = num;
      this.sleepInterval = sleepInterval;
      this.bb = bb;
      System.out.println("Consumer #" + num + " constructed, sleepInterval=" +
         sleepInterval);
   }

   public void run() {
      int napping;
      float item;
      System.out.println("Consumer #" + num + " running");
      while (true) {
         System.out.println("age()=" + age() + " Consumer #" +
            num + " wants to consume an item");
         item = bb.fetch();
         napping = (int) (Math.random() * (sleepInterval*1000));
         System.out.println("age()=" + age() + " Consumer #" +
            num + " consuming item " + item + " for " + napping + " ms");
         nap(napping);
      }
   }
}

class driver extends myThread {

   private driver() {} // prevent instantiation

   private static int numSlots = 5;
   private static int napProducer = 2;
   private static int napConsumer = 2;
   private static int runInterval = 20;

   public static void main(String args[]) {

      numSlots = getarg(1, numSlots, args);
      napProducer = getarg(2, napProducer, args);
      napConsumer = getarg(3, napConsumer, args);
      runInterval = getarg(4, runInterval, args);

      System.out.println("BBPC: numSlots=" + numSlots +
         ", napProducer=" + napProducer + ", napConsumer=" + napConsumer +
         ", runInterval=" + runInterval);

      BoundedBuffer bb = new BoundedBuffer(numSlots);
      aProducer theProducer = new aProducer(1, napProducer, bb);
      aConsumer theConsumer = new aConsumer(1, napConsumer, bb);

      theProducer.start();
      theConsumer.start();
      nap(runInterval*1000);
      theProducer.stop(); theProducer = null;
      theConsumer.stop(); theConsumer = null;
      System.out.println("BBPC done");
      System.exit(0);
   }
}

/* ............... Example compile and run(s)

% /homeb/shartley/Java/java/bin/javac bbpc.java
% /homeb/shartley/Java/java/bin/java driver      
BBPC: numSlots=5, napProducer=2, napConsumer=2, runInterval=20
BoundedBuffer created with 5 slots
Producer #1 constructed, sleepInterval=2
Consumer #1 constructed, sleepInterval=2
Producer #1 running
age()=706 Producer #1 constructing item for 810 ms
Consumer #1 running
age()=750 Consumer #1 wants to consume an item
age()=1553 Producer #1 wants to deposit item 0.0216875
deposit: count = 1
age()=1591 Producer #1 constructing item for 1281 ms
fetch: count = 0
age()=1608 Consumer #1 consuming item 0.0216875 for 88 ms
age()=1712 Consumer #1 wants to consume an item
age()=2892 Producer #1 wants to deposit item 0.325629
deposit: count = 1
age()=2907 Producer #1 constructing item for 648 ms
fetch: count = 0
age()=2921 Consumer #1 consuming item 0.325629 for 1659 ms
age()=3572 Producer #1 wants to deposit item 0.905767
deposit: count = 1
age()=3586 Producer #1 constructing item for 75 ms
age()=3672 Producer #1 wants to deposit item 0.330639
deposit: count = 2
age()=3686 Producer #1 constructing item for 1332 ms
age()=4602 Consumer #1 wants to consume an item
fetch: count = 1
age()=4614 Consumer #1 consuming item 0.905767 for 308 ms
age()=4942 Consumer #1 wants to consume an item
fetch: count = 0
age()=4954 Consumer #1 consuming item 0.330639 for 751 ms
age()=5032 Producer #1 wants to deposit item 0.840799
deposit: count = 1
age()=5048 Producer #1 constructing item for 163 ms
age()=5222 Producer #1 wants to deposit item 0.309836
deposit: count = 2
age()=5237 Producer #1 constructing item for 1080 ms
age()=5722 Consumer #1 wants to consume an item
fetch: count = 1
age()=5734 Consumer #1 consuming item 0.840799 for 864 ms
age()=6332 Producer #1 wants to deposit item 0.281866
deposit: count = 2
age()=6346 Producer #1 constructing item for 505 ms
age()=6614 Consumer #1 wants to consume an item
fetch: count = 1
age()=6627 Consumer #1 consuming item 0.309836 for 55 ms
age()=6702 Consumer #1 wants to consume an item
fetch: count = 0
age()=6713 Consumer #1 consuming item 0.281866 for 846 ms
age()=6863 Producer #1 wants to deposit item 0.221728
deposit: count = 1
age()=6877 Producer #1 constructing item for 956 ms
age()=7572 Consumer #1 wants to consume an item
fetch: count = 0
age()=7584 Consumer #1 consuming item 0.221728 for 1619 ms
age()=7852 Producer #1 wants to deposit item 0.937879
deposit: count = 1
age()=7866 Producer #1 constructing item for 1100 ms
age()=8983 Producer #1 wants to deposit item 0.574658
deposit: count = 2
age()=8997 Producer #1 constructing item for 499 ms
age()=9222 Consumer #1 wants to consume an item
fetch: count = 1
age()=9234 Consumer #1 consuming item 0.937879 for 586 ms
age()=9512 Producer #1 wants to deposit item 0.374546
deposit: count = 2
age()=9526 Producer #1 constructing item for 529 ms
age()=9832 Consumer #1 wants to consume an item
fetch: count = 1
age()=9844 Consumer #1 consuming item 0.574658 for 1088 ms
age()=10072 Producer #1 wants to deposit item 0.0645171
deposit: count = 2
age()=10087 Producer #1 constructing item for 1484 ms
age()=10952 Consumer #1 wants to consume an item
fetch: count = 1
age()=10964 Consumer #1 consuming item 0.374546 for 770 ms
age()=11582 Producer #1 wants to deposit item 0.519687
deposit: count = 2
age()=11596 Producer #1 constructing item for 678 ms
age()=11752 Consumer #1 wants to consume an item
fetch: count = 1
age()=11765 Consumer #1 consuming item 0.0645171 for 1147 ms
age()=12292 Producer #1 wants to deposit item 0.724322
deposit: count = 2
age()=12306 Producer #1 constructing item for 962 ms
age()=12932 Consumer #1 wants to consume an item
fetch: count = 1
age()=12944 Consumer #1 consuming item 0.519687 for 1767 ms
age()=13282 Producer #1 wants to deposit item 0.878702
deposit: count = 2
age()=13297 Producer #1 constructing item for 1461 ms
age()=14732 Consumer #1 wants to consume an item
fetch: count = 1
age()=14745 Consumer #1 consuming item 0.724322 for 830 ms
age()=14772 Producer #1 wants to deposit item 0.155488
deposit: count = 2
age()=14786 Producer #1 constructing item for 980 ms
age()=15592 Consumer #1 wants to consume an item
fetch: count = 1
age()=15604 Consumer #1 consuming item 0.878702 for 1454 ms
age()=15782 Producer #1 wants to deposit item 0.270823
deposit: count = 2
age()=15797 Producer #1 constructing item for 737 ms
age()=16553 Producer #1 wants to deposit item 0.21758
deposit: count = 3
age()=16567 Producer #1 constructing item for 804 ms
age()=17072 Consumer #1 wants to consume an item
fetch: count = 2
age()=17085 Consumer #1 consuming item 0.155488 for 453 ms
age()=17382 Producer #1 wants to deposit item 0.100138
deposit: count = 3
age()=17396 Producer #1 constructing item for 555 ms
age()=17552 Consumer #1 wants to consume an item
fetch: count = 2
age()=17564 Consumer #1 consuming item 0.270823 for 85 ms
age()=17662 Consumer #1 wants to consume an item
fetch: count = 1
age()=17675 Consumer #1 consuming item 0.21758 for 1641 ms
age()=17962 Producer #1 wants to deposit item 0.246954
deposit: count = 2
age()=17977 Producer #1 constructing item for 187 ms
age()=18182 Producer #1 wants to deposit item 0.18355
deposit: count = 3
age()=18196 Producer #1 constructing item for 1130 ms
age()=19332 Consumer #1 wants to consume an item
fetch: count = 2
age()=19352 Producer #1 wants to deposit item 0.0727698
deposit: count = 3
age()=19347 Consumer #1 consuming item 0.100138 for 1009 ms
age()=19393 Producer #1 constructing item for 158 ms
age()=19573 Producer #1 wants to deposit item 0.220376
deposit: count = 4
age()=19587 Producer #1 constructing item for 1819 ms
age()=20422 Consumer #1 wants to consume an item
fetch: count = 3
age()=20435 Consumer #1 consuming item 0.246954 for 1542 ms
BBPC done
% /homeb/shartley/Java/java/bin/java driver 5 5 1   
BBPC: numSlots=5, napProducer=5, napConsumer=1, runInterval=20
BoundedBuffer created with 5 slots
Producer #1 constructed, sleepInterval=5
Consumer #1 constructed, sleepInterval=1
Producer #1 running
age()=530 Producer #1 constructing item for 3809 ms
Consumer #1 running
age()=569 Consumer #1 wants to consume an item
age()=4376 Producer #1 wants to deposit item 0.707449
deposit: count = 1
age()=4415 Producer #1 constructing item for 3861 ms
fetch: count = 0
age()=4432 Consumer #1 consuming item 0.707449 for 599 ms
age()=5055 Consumer #1 wants to consume an item
age()=8296 Producer #1 wants to deposit item 0.445485
deposit: count = 1
age()=8310 Producer #1 constructing item for 4858 ms
fetch: count = 0
age()=8325 Consumer #1 consuming item 0.445485 for 869 ms
age()=9215 Consumer #1 wants to consume an item
age()=13185 Producer #1 wants to deposit item 0.028162
deposit: count = 1
age()=13200 Producer #1 constructing item for 2236 ms
fetch: count = 0
age()=13215 Consumer #1 consuming item 0.028162 for 237 ms
age()=13465 Consumer #1 wants to consume an item
age()=15456 Producer #1 wants to deposit item 0.432711
deposit: count = 1
age()=15471 Producer #1 constructing item for 1493 ms
fetch: count = 0
age()=15486 Consumer #1 consuming item 0.432711 for 722 ms
age()=16225 Consumer #1 wants to consume an item
age()=16976 Producer #1 wants to deposit item 0.75107
deposit: count = 1
age()=16990 Producer #1 constructing item for 3271 ms
fetch: count = 0
age()=17005 Consumer #1 consuming item 0.75107 for 664 ms
age()=17685 Consumer #1 wants to consume an item
age()=20276 Producer #1 wants to deposit item 0.817654
deposit: count = 1
age()=20290 Producer #1 constructing item for 2592 ms
fetch: count = 0
age()=20304 Consumer #1 consuming item 0.817654 for 962 ms
BBPC done
% /homeb/shartley/Java/java/bin/java driver 5 1 5   
BBPC: numSlots=5, napProducer=1, napConsumer=5, runInterval=20
BoundedBuffer created with 5 slots
Producer #1 constructed, sleepInterval=1
Consumer #1 constructed, sleepInterval=5
Producer #1 running
age()=498 Producer #1 constructing item for 897 ms
Consumer #1 running
age()=536 Consumer #1 wants to consume an item
age()=1431 Producer #1 wants to deposit item 0.567698
deposit: count = 1
age()=1469 Producer #1 constructing item for 313 ms
fetch: count = 0
age()=1485 Consumer #1 consuming item 0.567698 for 664 ms
age()=1800 Producer #1 wants to deposit item 0.411007
deposit: count = 1
age()=1815 Producer #1 constructing item for 601 ms
age()=2170 Consumer #1 wants to consume an item
fetch: count = 0
age()=2182 Consumer #1 consuming item 0.411007 for 1103 ms
age()=2430 Producer #1 wants to deposit item 0.736059
deposit: count = 1
age()=2445 Producer #1 constructing item for 415 ms
age()=2870 Producer #1 wants to deposit item 0.979717
deposit: count = 2
age()=2885 Producer #1 constructing item for 917 ms
age()=3300 Consumer #1 wants to consume an item
fetch: count = 1
age()=3312 Consumer #1 consuming item 0.736059 for 2881 ms
age()=3820 Producer #1 wants to deposit item 0.198447
deposit: count = 2
age()=3834 Producer #1 constructing item for 458 ms
age()=4310 Producer #1 wants to deposit item 0.975152
deposit: count = 3
age()=4324 Producer #1 constructing item for 306 ms
age()=4650 Producer #1 wants to deposit item 0.0189581
deposit: count = 4
age()=4665 Producer #1 constructing item for 571 ms
age()=5251 Producer #1 wants to deposit item 0.633621
deposit: count = 5
age()=5264 Producer #1 constructing item for 462 ms
age()=5740 Producer #1 wants to deposit item 0.378471
age()=6210 Consumer #1 wants to consume an item
fetch: count = 4
age()=6223 Consumer #1 consuming item 0.979717 for 79 ms
deposit: count = 5
age()=6238 Producer #1 constructing item for 816 ms
age()=6320 Consumer #1 wants to consume an item
fetch: count = 4
age()=6332 Consumer #1 consuming item 0.198447 for 4199 ms
age()=7070 Producer #1 wants to deposit item 0.36766
deposit: count = 5
age()=7085 Producer #1 constructing item for 910 ms
age()=8011 Producer #1 wants to deposit item 0.765426
age()=10550 Consumer #1 wants to consume an item
fetch: count = 4
age()=10562 Consumer #1 consuming item 0.975152 for 2831 ms
deposit: count = 5
age()=10579 Producer #1 constructing item for 617 ms
age()=11210 Producer #1 wants to deposit item 0.346689
age()=13410 Consumer #1 wants to consume an item
fetch: count = 4
age()=13424 Consumer #1 consuming item 0.0189581 for 836 ms
deposit: count = 5
age()=13440 Producer #1 constructing item for 503 ms
age()=13966 Producer #1 wants to deposit item 0.585681
age()=14280 Consumer #1 wants to consume an item
fetch: count = 4
age()=14293 Consumer #1 consuming item 0.633621 for 2190 ms
deposit: count = 5
age()=14310 Producer #1 constructing item for 10 ms
age()=14340 Producer #1 wants to deposit item 0.168645
age()=16500 Consumer #1 wants to consume an item
fetch: count = 4
age()=16513 Consumer #1 consuming item 0.378471 for 3301 ms
deposit: count = 5
age()=16529 Producer #1 constructing item for 581 ms
age()=17122 Producer #1 wants to deposit item 0.76766
age()=19830 Consumer #1 wants to consume an item
fetch: count = 4
age()=19843 Consumer #1 consuming item 0.36766 for 655 ms
deposit: count = 5
age()=19859 Producer #1 constructing item for 825 ms
BBPC done
                                              */
