diff --git a/.gitignore b/.gitignore index 524f096..04a0844 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,7 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* replay_pid* + + +# mine +pdf/* \ No newline at end of file diff --git a/SimulatoreBCNN/SimulatoreBCNN.iml b/SimulatoreBCNN/SimulatoreBCNN.iml new file mode 100644 index 0000000..c90834f --- /dev/null +++ b/SimulatoreBCNN/SimulatoreBCNN.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/SimulatoreBCNN/src/Sim.java b/SimulatoreBCNN/src/Sim.java new file mode 100644 index 0000000..fc19159 --- /dev/null +++ b/SimulatoreBCNN/src/Sim.java @@ -0,0 +1,149 @@ +import SimUtils.*; +import SimUtils.Queue; + +import java.util.*; +class Sim { + + // Class Sim variables + public static double Clock, MeanInterArrivalTime, MeanServiceTime, SIGMA, LastEventTime, + TotalBusy, MaxQueueLength, SumResponseTime; + public static long NumberOfCustomers, QueueLength, NumberInService, + TotalCustomers, NumberOfDepartures, LongService; + + public final static int arrival = 1; + public final static int departure = 2; + + public static EventList FutureEventList; + public static Queue Customers; + public static Random stream; + + public static void main(String argv[]) { + + MeanInterArrivalTime = 4.5; MeanServiceTime = 3.2; + SIGMA = 0.6; TotalCustomers = 1000; + long seed = Long.parseLong(argv[0]); + + stream = new Random(seed); // initialize rng stream + FutureEventList = new EventList(); + Customers = new Queue(); + + Initialization(); + + // Loop until first "TotalCustomers" have departed + while(NumberOfDepartures < TotalCustomers ) { + Event evt = (Event)FutureEventList.getMin(); // get imminent event + FutureEventList.dequeue(); // be rid of it + Clock = evt.get_time(); // advance simulation time + if( evt.get_type() == arrival ) ProcessArrival(evt); + else ProcessDeparture(evt); + } + ReportGeneration(); + } + + // seed the event list with TotalCustomers arrivals + public static void Initialization() { + Clock = 0.0; + QueueLength = 0; + NumberInService = 0; + LastEventTime = 0.0; + TotalBusy = 0 ; + MaxQueueLength = 0; + SumResponseTime = 0; + NumberOfDepartures = 0; + LongService = 0; + + // create first arrival event + Event evt = new Event(arrival, exponential( stream, MeanInterArrivalTime)); + FutureEventList.enqueue( evt ); + } + + public static void ProcessArrival(Event evt) { + Customers.enqueue(evt); + QueueLength++; + // if the server is idle, fetch the event, do statistics + // and put into service + if( NumberInService == 0) ScheduleDeparture(); + else TotalBusy += (Clock - LastEventTime); // server is busy + + // adjust max queue length statistics + if (MaxQueueLength < QueueLength) MaxQueueLength = QueueLength; + + // schedule the next arrival + Event next_arrival = new Event(arrival, Clock+exponential(stream, MeanInterArrivalTime)); + FutureEventList.enqueue( next_arrival ); + LastEventTime = Clock; + } + + public static void ScheduleDeparture() { + double ServiceTime; + // get the job at the head of the queue + while (( ServiceTime = normal(stream, MeanServiceTime, SIGMA)) < 0 ); + Event depart = new Event(departure,Clock+ServiceTime); + FutureEventList.enqueue( depart ); + NumberInService = 1; + QueueLength--; + } + + public static void ProcessDeparture(Event e) { + // get the customer description + Event finished = (Event) Customers.dequeue(); + // if there are customers in the queue then schedule + // the departure of the next one + if( QueueLength > 0 ) ScheduleDeparture(); + else NumberInService = 0; + // measure the response time and add to the sum + double response = (Clock - finished.get_time()); + SumResponseTime += response; + if( response > 4.0 ) LongService++; // record long service + TotalBusy += (Clock - LastEventTime ); + NumberOfDepartures++; + LastEventTime = Clock; + } + + public static void ReportGeneration() { + double RHO = TotalBusy/Clock; + double AVGR = SumResponseTime/TotalCustomers; + double PC4 = ((double)LongService)/TotalCustomers; + + + System.out.println( "SINGLE SERVER QUEUE SIMULATION - GROCERY STORE CHECKOUT COUNTER "); + System.out.println( "\tMEAN INTERARRIVAL TIME " + + MeanInterArrivalTime ); + System.out.println( "\tMEAN SERVICE TIME " + + MeanServiceTime ); + System.out.println( "\tSTANDARD DEVIATION OF SERVICE TIMES " + SIGMA ); + System.out.println( "\tNUMBER OF CUSTOMERS SERVED " + TotalCustomers ); + System.out.println(); + System.out.println( "\tSERVER UTILIZATION " + RHO ); + System.out.println( "\tMAXIMUM LINE LENGTH " + MaxQueueLength ); + System.out.println( "\tAVERAGE RESPONSE TIME " + AVGR + " MINUTES" ); + System.out.println( "\tPROPORTION WHO SPEND FOUR "); + System.out.println( "\t MINUTES OR MORE IN SYSTEM " + PC4 ); + System.out.println( "\tSIMULATION RUNLENGTH " + Clock + " MINUTES" ); + System.out.println( "\tNUMBER OF DEPARTURES " + TotalCustomers ); + } + + public static double exponential(Random rng, double mean) { + return -mean*Math.log( rng.nextDouble() ); + } + + public static double SaveNormal; + public static int NumNormals = 0; + public static final double PI = 3.1415927 ; + + public static double normal(Random rng, double mean, double sigma) { + double ReturnNormal; + // should we generate two normals? + if(NumNormals == 0 ) { + double r1 = rng.nextDouble(); + double r2 = rng.nextDouble(); + ReturnNormal = Math.sqrt(-2*Math.log(r1))*Math.cos(2*PI*r2); + SaveNormal = Math.sqrt(-2*Math.log(r1))*Math.sin(2*PI*r2); + NumNormals = 1; + } else { + NumNormals = 0; + ReturnNormal = SaveNormal; + } + return ReturnNormal*sigma + mean ; + } +} \ No newline at end of file diff --git a/SimulatoreBCNN/src/SimUtils/Event.java b/SimulatoreBCNN/src/SimUtils/Event.java new file mode 100644 index 0000000..113ccbb --- /dev/null +++ b/SimulatoreBCNN/src/SimUtils/Event.java @@ -0,0 +1,21 @@ +package SimUtils; + +public class Event implements Comparable { + + public Event(int a_type, double a_time) { _type = a_type; time = a_time; } + + public double time; + private int _type; + + public int get_type() { return _type; } + public double get_time() { return time; } + + public Event leftlink, rightlink, uplink; + + public int compareTo(Object _cmpEvent ) { + double _cmp_time = ((Event) _cmpEvent).get_time() ; + if( this.time < _cmp_time) return -1; + if( this.time == _cmp_time) return 0; + return 1; + } +} diff --git a/SimulatoreBCNN/src/SimUtils/EventList.java b/SimulatoreBCNN/src/SimUtils/EventList.java new file mode 100644 index 0000000..448e65b --- /dev/null +++ b/SimulatoreBCNN/src/SimUtils/EventList.java @@ -0,0 +1,195 @@ +package SimUtils; + +// EventList is implemented as a splay tree +// v1. class Event knows this and has fields +// leftlink, rightlink, uplink +public class EventList { + Event root; + Event update_p; + Event left,right,next,temp,farleft,farfarleft; + double ntime; + boolean update_code; + int size = 0; + Object owner; + + public EventList(){ } + + public String toString(){ + return "EventList "; + } + + public boolean isEmpty(){ + return (this.root == null); + } + + public void enqueue(Event n){ + size++; + n.uplink = null; + next = this.root; + this.root = n; + if (next == null){ + n.leftlink = null; + n.rightlink = null; + } + else{ + mainblock: { + ntime = n.time; + left = n; + right = n; + if (next.time > ntime){ + //2222222222222222222222222 + do{ + temp = next.leftlink; + if (temp == null){ + right.leftlink = next; + next.uplink = right; + left.rightlink = null; + break mainblock; + } + if (temp.time <= ntime){ + right.leftlink = next; + next.uplink = right; + right = next; + next = temp; + break; + } + next.leftlink = temp.rightlink; + if (temp.rightlink != null) + temp.rightlink.uplink = next; + right.leftlink = temp; + temp.uplink = right; + temp.rightlink = next; + next.uplink = temp; + right = temp; + next = temp.leftlink; + if (next == null){ + left.rightlink = null; + break mainblock; + } + }while (next.time > ntime); + } + forblock: + for( ; ; ){ + //111111111111111111111111111 + do{ + temp = next.rightlink; + if (temp == null){ + left.rightlink = next; + next.uplink = left; + right.leftlink = null; + break forblock; + } + if (temp.time > ntime){ + left.rightlink = next; + next.uplink = left; + left = next; + next = temp; + break; + } + next.rightlink = temp.leftlink; + if (temp.leftlink!=null) + temp.leftlink.uplink = next; + left.rightlink = temp; + temp.uplink = left; + temp.leftlink = next; + next.uplink = temp; + left = temp; + next = temp.rightlink; + if (next == null){ + right.leftlink = null; + break forblock; + } + }while (next.time <= ntime); + + //2222222222222222222222222222 + do{ + temp = next.leftlink; + if (temp == null){ + right.leftlink = next; + next.uplink = right; + left.rightlink = null; + break forblock; + } + if (temp.time <= ntime){ + right.leftlink = next; + next.uplink = right; + right = next; + next = temp; + break; + } + next.leftlink = temp.rightlink; + if (temp.rightlink != null) + temp.rightlink.uplink = next; + right.leftlink = temp; + temp.uplink = right; + temp.rightlink = next; + next.uplink = temp; + right = temp; + next = temp.leftlink; + if (next == null){ + left.rightlink = null; + break forblock; + } + }while (next.time > ntime); + } + } + + //99999999999999999999999999999 + temp = n.leftlink; + n.leftlink = n.rightlink; + n.rightlink = temp; + } + } + public Event getMin(){ + + //assumed that the tree is not empty + next = this.root; + left = next.leftlink; + if (left ==null){ + update_code = true; + return next; + } + else{ + for ( ; ; ){ + farleft = left.leftlink; + if (farleft == null){ + update_code = false; + update_p = next; + return left; + } + farfarleft = farleft.leftlink; + if (farfarleft==null){ + update_code = false; + update_p = left; + return farleft; + } + next.leftlink = farleft; + farleft.uplink = next; + left.leftlink = farleft.rightlink; + if (farleft.rightlink!=null) + farleft.rightlink.uplink = left; + farleft.rightlink = left; + left.uplink = farleft; + next = farleft; + left = farfarleft; + } + } + } + + public void dequeue(){ + size--; + //assumed that getMin has been the last splay tree operation invoked. Removes that element. + if (update_code){ + next = null; //remove pointer to the returned Event + root = root.rightlink; + if (root!=null) + root.uplink = null; + } + else{ + left = farleft = null; //remove pointers to the returned event + if (update_p.leftlink.rightlink!=null) + update_p.leftlink.rightlink.uplink = update_p; + update_p.leftlink = update_p.leftlink.rightlink; + } + } +} diff --git a/SimulatoreBCNN/src/SimUtils/Queue.java b/SimulatoreBCNN/src/SimUtils/Queue.java new file mode 100644 index 0000000..32f0f07 --- /dev/null +++ b/SimulatoreBCNN/src/SimUtils/Queue.java @@ -0,0 +1,253 @@ +package SimUtils; + +/** + + This class provides a fairly efficient implementation of a FIFO + queue. We use an array to implement the queue which is + automatically increased in size when necessary. Note that the queue + is never shrunk in the current implementation. + + @author Mark Astley + @version $Revision: 1.5 $ ($Date: 1998/09/07 23:00:37 $) + + */ + +// public class Queue implements Cloneable, Serializable { +public class Queue { + /** + This constant sets the initial size of a queue when the default + constructor is invoked to create a queue. Users requiring a + different initial size may invoke the appropriate constructor + below. + */ + public static final int INITIAL_SIZE = 10; + + /** + This array holds the queue. We use a dynamically growing circular + array to represent the queue. Queue size is doubled when + necessary which should make for a rather efficient implementation. + */ + Object[] theQ; + + /** + These two fields hold indices to the first and last element of the + queue respectively. Technically, back points to the + first open spot in the queue rather than the last element. + */ + int front; + int back; + + /** + Default constructor for the Queue class. + */ + public Queue() { + theQ = new Object[INITIAL_SIZE]; + front=back=0; + } + + /** + Alternative constructor for the Queue class which allows + the specification of the initial size of the queue. + + @param initialSize The initial size of the new Queue. + */ + public Queue(int initialSize) { + theQ = new Object[initialSize]; + front=back=0; + } + + /** + This private method is used to double the size of the queue when + more space is required (as a result of calling enqueue). Note + that because the implementation is in terms of a circular array, + we have to be careful when copying elements from the old array to + the new array. + */ + synchronized private void grow() { + Object[] newQ = new Object[2 * theQ.length]; + + if (back >= front) + System.arraycopy(theQ, front, newQ, front, back - front + 1); + else { + System.arraycopy(theQ, front, newQ, front, theQ.length - front); + System.arraycopy(theQ, 0, newQ, theQ.length, back); + back = front + theQ.length - 1; + } + + theQ = newQ; + } + + /** + Determine if the queue is empty. + + @return true if the queue contains no elements, + false otherwise. + */ + synchronized public boolean empty() { + return (front == back); + } + + /** + Add an object to the end of the queue. + + @param q A reference to the object to add. + @return void + */ + synchronized public void enqueue(Object q) { + if (((back + 1) % theQ.length) == front) + grow(); + + theQ[back] = q; + back = (back + 1) % theQ.length; + } + + /** + Remove an object from the front of the queue. The returned object + is removed from the queue. + + @return A reference to the Object at the front of the + queue. Returns null if the queue is empty. + */ + synchronized public Object dequeue() { + Object toReturn = null; + + if (!empty()) { + toReturn = theQ[front]; + front = (front + 1) % theQ.length; + } + + return toReturn; + } + + /** + Get the object at the front of the queue without removing it. + + @return A reference to the Object at the front of the + queue. + */ + synchronized public Object peekFront() { + if (!empty()) + return theQ[front]; + else + return null; + } + + /** + Determine how many objects are currently stored in the queue. + + @return An int indicating the number of objects in the + queue. + */ + synchronized public int numElements() { + if (front <= back) + return (back - front); + else + return (back + (theQ.length - front)); + } + + + /** + This function allows arbitrary queue elements to be removed. + Elements which are equal (according to the equal + function) to the argument are removed from the queue. Note that + this version of the function only removes the FIRST element + found. Use the removeAll function to remove ALL elements + equal to the argument. + + @param rem The object to be removed from the queue. + @return true if an element was removed, false + otherwise. + */ + synchronized public boolean remove(Object rem) { + int i, x, y; + + // PRAGMA [debug,osl.util.Queue] Log.println(" Searching for element: " + rem); + // First track down the element to remove (if there is one) + for(i=front; i != back; i = (i + 1) % theQ.length) + if (rem.equals(theQ[i])) + break; + + // If nothing found then return false... + if (i == back) + // PRAGMA [debug,osl.util.Queue] { + // PRAGMA [debug,osl.util.Queue] Log.println(" Element not found, returning"); + return false; + // PRAGMA [debug,osl.util.Queue] } else { + // PRAGMA [debug,osl.util.Queue] Log.println(" Element found at position: " + i); + // PRAGMA [debug,osl.util.Queue] } + + // Otherwise remove the one element. This is a bit of a hack but + // should be fairly efficient. + Object[] newQ = new Object[theQ.length]; + for(x=front, y=0; x != back; x = (x + 1) % theQ.length) + if (x != i) { + newQ[y] = theQ[x]; + y++; + } + + theQ = newQ; + front = 0; + back = y; + + return true; + } + + /** + This function has the same behavior as remove except that + ALL matching elements are removed from the queue. + + @param rem The object to be removed from the queue. + @return The number of elements removed. + */ + synchronized public int removeAll(Object rem) { + int i, match, x, y, z; + + // Track down all the elements to remove. + int[] toRemove = new int[numElements()]; + for(i=front, match=0; i != back; i = (i + 1) % theQ.length) + if (rem.equals(theQ[i])) { + toRemove[match] = i; + match++; + } + + // See if anything matched, if not then just return + if (match == 0) + return 0; + + // Now make a new array with all the matching elements removed + Object[] newQ = new Object[theQ.length]; + for(x=front, y=0, z=0; x != back; x = (x + 1) % theQ.length) + if (z < match) + if (x != toRemove[z]) { + newQ[y] = theQ[x]; + y++; + } else + z++; + else { + newQ[y] = theQ[x]; + y++; + } + + theQ = newQ; + front = 0; + back = y; + + // match holds the number of things we removed + return match; + } + + /** + The canonical toString method. + */ + synchronized public String toString() { + String returnVal = "Queue: "; + + if (empty()) + return returnVal + "no elements"; + + for(int i=front; i != back; i = (i + 1) % theQ.length) + returnVal = returnVal + theQ[i].toString() + " "; + + return returnVal; + } +} diff --git a/pdf/README.txt b/pdf/README.txt new file mode 100644 index 0000000..984971e --- /dev/null +++ b/pdf/README.txt @@ -0,0 +1,13 @@ +Il file SimulatoreJava-BCNNcapitolo4.zip contiene il simulatore presentato nel capitolo 4 del libro di testo + +Il file SimulatoreBCNNintelliJ.zip contiene un export da IntelliJ IDEA dello stesso simulatore; +unica differenza: le classi di utilità generale sono contenute in un package "SimUtils" + +Il file QueueingTools.xls contiene vari fogli di calcolo che implementano il calcolo dei principali indici +di prestazione per alcune categorie di modelli costituiti da una sola coda (si veda il capitolo 6 del libro di testo). +Per esempio un foglio è dedicato alla M/M/c mentre un altro foglio è dedicato alla M/G/1. +Potete usare questi fogli di calcolo per confrontare i risultati ottenuti con il simulatore e quelli ricavati dalle formule +dei fogli excel, come strumento di verifica del buon funzionamento del simulatore. + +Potete anche confrontare i risultati ottenuti con questo simulatore e quelli ottenuti con JMT (Java Modelling Tools), a parità +di parametri e di distribuzione dei tempi di interarrivo e di servizio. diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..15cf713 --- /dev/null +++ b/pom.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + + net.berack + valpre + 1.0-SNAPSHOT + + + 23 + 23 + + + \ No newline at end of file