package umontreal.iro.lecuyer.simevents;
import java.util.Collection;
import java.util.List;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import umontreal.iro.lecuyer.stat.Tally;
import umontreal.iro.lecuyer.util.TransformingList;
import umontreal.iro.lecuyer.util.PrintfFormat;


/**
 * Implements a list with integrated statistical
 * probes to provide automatic collection of
 * statistics on the sojourn times of objects in the list and on the
 * size of the list as a function of time given by a simulator.
 * The automatic statistical collection can be
 * enabled or disabled for each list, to reduce overhead.
 * This class extends {@link umontreal.iro.lecuyer.util.TransformingList TransformingList}
 * and transforms elements into nodes associating insertion times with elements.
 * 
 */
public class ListWithStat
             extends TransformingList> {
   private boolean stats; // true si on a appele setStatCollecting
   private double initTime; // temps de la derniere initialisation
   private Accumulate blockSize; //block stat. sur la longueur de la liste
   private Tally blockSojourn; // block stat. sur les durees de sejour
   private String name;
   private Simulator sim;



   /**
    * Constructs a new list with internal data structure using the default simulator and implemented by nodeList.
    *   The given list is cleared for the constructed list to be initially empty.
    * 
    */
   public ListWithStat (List> nodeList) {
      super (nodeList);
      nodeList.clear();
      sim = Simulator.getDefaultSimulator();
      stats = false;
   }


   /**
    * Constructs a new list with internal data structure implemented by
    *   nodeList.
    *   The given list is cleared for the constructed list to be initially empty.
    * 
    * @param nodeList the list containing the nodes
    * 
    * 
    */
   public ListWithStat (Simulator inSim, List> nodeList) {
      super (nodeList);
      if (inSim == null || nodeList == null)
          throw new NullPointerException();
      nodeList.clear();
      sim = inSim;
      stats = false;
   }


   /**
    * Constructs a list containing the elements of the specified
    *    collection, whose elements are stored into nodeList and using the default simulator.
    * 
    * @param nodeList the list containing the nodes
    * 
    *     @param c collection containing elements to fill in this list with
    * 
    * 
    */
   public ListWithStat (List> nodeList, Collection c) {
      this (Simulator.getDefaultSimulator(), nodeList);
      addAll (c);
   }


   /**
    * Constructs a list containing the elements of the specified
    *    collection, whose elements are stored into nodeList.
    * 
    * @param inSim simulator associate to the current variable
    * 
    *     @param nodeList the list containing the nodes
    * 
    *     @param c collection containing elements to fill in this list with
    * 
    * 
    */
   public ListWithStat (Simulator inSim, List> nodeList,
                        Collection c) {
      this (inSim, nodeList);
      addAll (c);
   }


   /**
    * Constructs a new list with name name, internal
    *    list nodeList, and using the default simulator.
    *    This name can be used to identify the list in traces and reports.
    *    The given list is cleared for the constructed list to be initially empty.
    * 
    * @param nodeList the list containing the nodes
    * 
    *    @param name name for the list object
    * 
    * 
    */
   public ListWithStat (List> nodeList, String name) {
      this (Simulator.getDefaultSimulator(), nodeList);
      this.name = name;
   }


   /**
    * Constructs a new list with name name, and
    *   internal list nodeList.
    *    This name can be used to identify the list in traces and reports.
    *   The given list is cleared for the constructed list to be initially empty.
    * 
    * @param inSim simulator associate to the current variable
    * 
    *     @param nodeList the list containing the nodes
    * 
    *    @param name name for the list object
    * 
    * 
    */
   public ListWithStat (Simulator inSim, List> nodeList,
                        String name) {
      this (inSim, nodeList);
      this.name = name;
   }


   /**
    * Constructs a new list containing the elements of the
    *    specified collection c, with name name,
    *    internal list nodeList, and using the default simulator.
    *    This name can be used to identify the list in traces and reports.
    * 
    * @param nodeList the list containing the nodes
    * 
    *    @param c collection containing elements to fill in this list with
    * 
    *    @param name name for the list object
    * 
    * 
    */
   public ListWithStat (List> nodeList, Collection c,
                        String name) {
      this (Simulator.getDefaultSimulator(), nodeList);
      this.name = name;
      addAll (c);
   }


   /**
    * Constructs a new list containing the elements of the
    *    specified collection c, with name name, and
    *    internal list nodeList.
    *    This name can be used to identify the list in traces and reports.
    * 
    * @param inSim simulator associate to the current variable
    * 
    *     @param nodeList the list containing the nodes
    * 
    *    @param c collection containing elements to fill in this list with
    * 
    *    @param name name for the list object
    * 
    */
   public ListWithStat (Simulator inSim, List> nodeList,
                        Collection c, String name) {
      this (inSim, nodeList);
      this.name = name;
      addAll (c);
   }


   public E convertFromInnerType (Node node) {
      return node.getElement();
   }

   public Node convertToInnerType (E element) {
      return new Node (element, sim.time());
   }

   /**
    * Returns the simulator associated with this list.
    *  
    * @return the simulator associated with this list
    * 
    */
   public Simulator simulator()  {
      return sim;
   } 


   /**
    * Sets the simulator associated with this list.
    *    This list should be cleared after this method is called.
    *  
    * @param sim the simulator of this list
    * 
    * 
    */
   public void setSimulator(Simulator sim)  {
       if (sim == null)
         throw new NullPointerException();
      this.sim = sim;
      if (blockSize != null)
         blockSize.setSimulator (sim);
   } 


   @Override
   public void clear() {
      if (stats)
         initStat();
      super.clear();
   }

   @Override
   public void add (int index, E obj)  {
      super.add (index, obj);
      if (stats)
         blockSize.update (size());
   }

   @Override
   public E remove (int index) {
      Node node = getInnerList().get (index);
      if (stats)
         blockSojourn.add (sim.time() - node.getInsertionTime());
      E e = super.remove (index);
      if (stats)
         blockSize.update (size());
      return e;
   }

   @Override
   public Iterator iterator() {
      return new IteratorWithStat (getInnerList().iterator());
   }

   @Override
   public ListIterator listIterator() {
      return new ListIteratorWithStat (getInnerList().listIterator());
   }

   @Override
   public ListIterator listIterator (int index) {
      return new ListIteratorWithStat (getInnerList().listIterator (index));
   }

   @Override
   public E set (int index, E element) {
      Node oldNode = getInnerList().get (index);
      E oldElement = oldNode.getElement();
      boolean equal;
      if (oldElement == null || element == null)
         equal = oldElement == oldElement;
      else
         equal = oldElement.equals (element);
      if (equal) {
         getInnerList().set (index, new Node (element, oldNode.getInsertionTime()));
         return oldElement;
      }
      else {
         if (stats)
            blockSojourn.add (sim.time() - oldNode.getInsertionTime());
         getInnerList().set (index, new Node (element, sim.time()));
         return oldElement;
      }
   }

   private class IteratorWithStat implements Iterator {
      private Iterator> itr;
      private Node lastRet;

      public IteratorWithStat (Iterator> itr) {
         this.itr = itr;
      }

      public boolean hasNext() {
         return itr.hasNext();
      }

      public E next() {
         lastRet = itr.next();
         return lastRet.getElement();
      }

      public void remove() {
         itr.remove();
         if (stats) {
            blockSize.update (size());
            blockSojourn.add (sim.time() - lastRet.getInsertionTime());
         }
         lastRet = null;
      }
   }

   private class ListIteratorWithStat implements ListIterator {
      private ListIterator> itr;
      private Node lastRet;

      public ListIteratorWithStat (ListIterator> itr) {
         this.itr = itr;
      }

      public void add (E o) {
         itr.add (new Node (o, sim.time()));
         lastRet = null;
         if (stats)
            blockSize.update (size());
      }

      public boolean hasNext() {
         return itr.hasNext();
      }

      public boolean hasPrevious() {
         return itr.hasPrevious();
      }

      public E next() {
         lastRet = itr.next();
         return lastRet.getElement();
      }

      public int nextIndex() {
         return itr.nextIndex();
      }

      public E previous() {
         lastRet = itr.previous();
         return lastRet.getElement();
      }

      public int previousIndex() {
         return itr.previousIndex();
      }

      public void remove() {
         itr.remove();
         if (stats) {
            blockSize.update (size());
            blockSojourn.add (sim.time() - lastRet.getInsertionTime());
         }
         lastRet = null;
      }

      public void set (E element) {
         if (lastRet == null)
            throw new NoSuchElementException();
         Node oldNode = lastRet;
         E oldElement = oldNode.getElement();
         boolean equal;
         if (oldElement == null || element == null)
            equal = oldElement == oldElement;
         else
            equal = oldElement.equals (element);
         if (equal) {
            lastRet = new Node (element, oldNode.getInsertionTime());
            itr.set (lastRet);
         }
         else {
            if (stats)
               blockSojourn.add (sim.time() - oldNode.getInsertionTime());
            lastRet = new Node (element, sim.time());
            itr.set (lastRet);
         }
      }
   }



   /**
    * Returns true if the list collects statistics
    *   about its size and sojourn times of elements, and
    *   false otherwise.
    *   By default, statistical collecting is turned off.
    * 
    * @return the status of statistical collecting
    * 
    */
  public boolean getStatCollecting() {
     return stats;
  }


   /**
    * Starts or stops collecting statistics on this list.
    *    If the statistical collection is turned ON, the method
    *    creates two statistical probes if they do not exist yet.
    *    The first one, of the class {@link Accumulate}, measures the evolution
    *    of the size of the list as a function of time.
    *    It can be accessed by the method {@link #statSize statSize}.
    *    The second one, of the class {@link Tally}  and accessible via
    *    {@link #statSojourn statSojourn}, samples the sojourn times in the list of the
    *    objects removed during the observation period,
    *    i.e., between the last initialization time of this statistical
    *    probe and the current time.
    *    The method automatically calls {@link #initStat initStat} to
    *    initialize these two probes.
    *    When this method is used, it is normally invoked immediately after
    *    calling the constructor of the list.
    *   
    * @exception IllegalStateException if the statistical collection
    *      is in the same state as the caller requires
    * 
    * 
    */
  public void setStatCollecting (boolean b) {
    if (b && !stats) {
      if (blockSize == null)
      blockSize = new Accumulate(sim, "List Size " + name);
      if (blockSojourn == null)
      blockSojourn = new Tally("List Sojourn " + name);
      blockSize.update (size());
      stats = true;
      initStat();
    } else
    stats = false;
  }


   /**
    * Reinitializes the two statistical probes created by
    *    {@link #setStatCollecting setStatCollecting} (true) and makes an update for the
    *    probe on the list size.
    *   
    * @exception IllegalStateException if the statistical collection is disabled
    * 
    * 
    */
   public void initStat()  {
    if (!stats)
    throw new IllegalStateException("initStat for a list that did not call setStatCollecting (true).");
    blockSize.init();
    blockSojourn.init();
    blockSize.update (size());
    initTime = sim.time();
  }


   /**
    * Returns the last simulation time {@link #initStat initStat} was called.
    * 
    * @return the last simulation time {@link #initStat initStat} was called
    * 
    */
   public double getInitTime() {
      return initTime;
   }


   /**
    * Returns the statistical probe on the evolution of the size of
    *    the list as a function of the simulation time.  This probe
    *    exists only if {@link #setStatCollecting setStatCollecting} (true)
    *    has been called for this list.
    *   
    * @return the statistical probe on the evolution of the size of the list
    * 
    */
   public Accumulate statSize()    {
       return blockSize;
   }


   /**
    * Returns the statistical probe on the sojourn times of the objects in
    *    the list.  This probe exists
    *    only if {@link #setStatCollecting setStatCollecting} (true) has been called for this list.
    *   
    * @return the statistical probe for the sojourn times in the list
    * 
    */
   public Tally statSojourn()   {
      return blockSojourn;
   }


   /**
    * Returns a string containing a statistical report on the list,
    *    provided that {@link #setStatCollecting setStatCollecting} (true) has been
    *    called before for this list.
    *    Even If {@link #setStatCollecting setStatCollecting} was called with false
    *    afterward, the report will be made for
    *    the collected observations.
    *    If the probes do not exist, i.e., {@link #setStatCollecting setStatCollecting} was never called
    *    for this object, an illegal state exception will be thrown.
    *   
    * @return a statistical report, represented as a string
    *    @exception IllegalStateException if no statistical probes exist
    * 
    * 
    */
   public String report()   {
        if (blockSojourn == null || blockSize == null)
            throw new IllegalStateException
                ("Calling report when no statistics were collected");

        PrintfFormat str = new PrintfFormat();
        str.append (PrintfFormat.NEWLINE +
            "REPORT ON LIST : ").append (name).append (PrintfFormat.NEWLINE);
        str.append ("   From time: ").append (7, 2, 2, initTime);
        str.append (" to time: ").append (10, 2, 2, sim.time());
        str.append ("                  min        max      average  ");
        str.append ("standard dev.  nb. Obs");

        str.append ("   Size    ");
        str.append (9, (int)(blockSize.min()+0.5));
        str.append (11, (int)(blockSize.max()+0.5));
        str.append (14, 3, 2, blockSize.average()).append(PrintfFormat.NEWLINE);

        str.append ("   Sojourn ");
        str.append ( 12, 3, 2, blockSojourn.min()).append (" ");
        str.append (10, 3, 2, blockSojourn.max()).append (" ");
        str.append (10, 3, 2, blockSojourn.average()).append (" ");
        str.append (10, 3, 2, blockSojourn.standardDeviation()).append (" ");
        str.append (11, blockSojourn.numberObs()).append (PrintfFormat.NEWLINE);

        return str.toString();
    }


   /**
    * Returns the name associated to this list,
    *   or null if no name was assigned.
    * 
    * @return the name associated to this list
    * 
    */
   public String getName() {
      return name;
   }


   /**
    * Represents a node that can be part of a list with
    *   statistical collecting.
    * 
    */
   public static class Node {
      private E element;
      private double insertionTime;


   /**
    * Constructs a new node containing element
    *   element
    *   inserted into the list at time insertionTime.
    * 
    * @param element the element to add into this new node
    * 
    *    @param insertionTime the insertion time of the element
    * 
    * 
    */
      public Node (E element, double insertionTime) {
         this.element = element;
         this.insertionTime = insertionTime;
      }


   /**
    * Returns the element stored into this node.
    * 
    * @return the element into this node
    * 
    */
      public E getElement() { return element; }


   /**
    * Returns the insertion time of the element in this node.
    * 
    * @return the insertion time of the element
    * 
    */
      public double getInsertionTime() { return insertionTime; }


      public String toString() {
         String str = element == null ? "null" : element.toString();
         str += " (inserted at time " + insertionTime + ")";
         return str;
      }
   }

}

 

Ads help maintain this website.