// Unrolled and not inlined version
// A Vector is used as a shared data structure
// No priorities are given or policies are used in this example

class RWvector{
  static Controller ctl;
  static Vector data;

  public static void main (String argv[]) {
    ctl = new Controller();
    data = new Vector();

      (new Reader1(ctl, data)).start();
      (new Reader2(ctl, data)).start();
      (new Writer1(ctl, data)).start();
      (new Writer2(ctl, data)).start();
    } 
  }


final class Reader1 extends Thread {
  protected Controller ctl;
  protected Vector data;

  public Reader1(Controller c, Vector d) { ctl = c; data = d;}

  public void run() 
  {
    MyObject tmp;
    while (true) {
      ctl.startRead();
      System.out.println("reader reading:");
      if (!data.isEmpty()) {
        tmp = (MyObject)data.firstElement();
	int key = tmp.id();
	System.out.println("The id of the first element is :"+key);
      }
      System.out.println("done reading");
      ctl.stopRead();
    } 
  } // end public void run()
}

final class Reader2 extends Thread {
  protected Controller ctl;
  protected Vector data;

  public Reader2(Controller c, Vector d) { ctl = c; data = d;}

  public void run()
  {
    MyObject tmp;
    while (true) {
      ctl.startRead();
      System.out.println("reader reading:");
      if (!data.isEmpty()) {
        tmp = (MyObject)data.lastElement(); 
        int key = tmp.id(); 
        System.out.println("The id of the last element is :"+key);
      }
      System.out.println("done reading");
      ctl.stopRead();
    }
  } // end public void run()
}

final class Writer1 extends Thread {
  protected Controller ctl;
  protected Vector data;
  protected int count;

  public Writer1(Controller c, Vector d) { ctl = c; data = d; count = 0;}

  public void run() 
  {
    while (true) {
      ctl.startWrite();
      System.out.println("writer writing:");
      MyObject tmp = new MyObject(count);
      data.addElement((Object) tmp );  //add element at the end
      count++;
      System.out.println("done writing");
      ctl.stopWrite();
    } 
  } // end public void run()
}

final class Writer2 extends Thread {
  protected Controller ctl;
  protected Vector data;
  protected int count;
  
  public Writer2(Controller c, Vector d) { ctl = c; data = d; count = 0;}           
  
  public void run()
  {
    while (true) {
      ctl.startWrite();
      System.out.println("writer writing:");
      data.removeElement();   //remove the first element
      System.out.println("done writing");   
      ctl.stopWrite();
    }
  } // end public void run()
}

class MyObject
{
    public int id;
     
    MyObject(int n)
    {
        id = n;
    }
    public int id()
    {
        return id;
    }
}  

final class Controller {
  protected int activeReaders = 0;  
  protected boolean writerPresent = false;  

  protected boolean writeCondition() {
    return activeReaders == 0 && !writerPresent;
  }

  protected boolean readCondition() {
    return !writerPresent;
  }

  protected synchronized void startRead() {
    while (!readCondition())
      try { wait(); } catch (InterruptedException ex) {}
    ++activeReaders;
  }

  protected synchronized void stopRead()  { 
    --activeReaders;
    notifyAll();
  }

  protected synchronized void startWrite() {
    while (!writeCondition()) 
      try { wait(); } catch (InterruptedException ex) {}
    writerPresent = true;
  }

  protected synchronized void stopWrite() { 
    writerPresent = false;
    notifyAll();
  }
}

  class Vector
  {
    private Object[] list = new Object[10];
    private int capacity =10;
    private int size = 0;
     
    public boolean isEmpty()
    {
        return size==0;
    }
     
    public boolean isFull()
    {
        return size == capacity;
    }
/**
 * Returns the component at the specified index
 */
    public Object elementAt(int index)
    {
        return list[index];
    }
   
/**
 * Inserts the specified object as a component in this array at the
 * specified index.
 */
    public void insertElementAt(Object obj, int index)
    {  
        if(!this.isFull())
        {
            for(int i=size-1; i>=index; i--)
            {
                list[i+1] = list[i];
            }
            list[index] = obj;
            size++;
        }
    }
/**  
 * Adds the specified component to the end of this array, increasing its
 * size by one.
 */
    public void addElement(Object obj)
    {
        if(!this.isFull())
        {
            list[size] = obj;
            size++;
        }
    }
/**
 * Returns the first component of this array
 */
    public Object firstElement()
    {  
        return list[0];   
    }

/**
 * Returns the last component of this array
 */
    public Object lastElement()
    {
	return list[size-1];
    }
/**
 * Deletes the component at the specified index
 */
    public void removeElementAt(int index)
    {
        if(!this.isEmpty())
        {
            for(int i=index; i<size-1; i++)
            {
                list[i] = list[i+1];
            }
            list[size-1] = null;
            size--;
        }
    }
/**
 * Deletes the first element of the Vector
 */
    public void removeElement()
    {
        if(!this.isEmpty())   
        {
            for(int i=0; i<size-1; i++)
            {
                list[i] = list[i+1];
            }
            list[size-1] = null;
            size--;
        }
    }

  }

