COCS3308		Program-5 (FORTRAN-2)
		     Single-Server Simulation		due: 10/25/2006
	A. Objectives
	   1. Linked-List (of simulation events) processing
	   2. Implementing a single-server queuing system for obtaining
	      some performance measures such as
		Steady-State average delay (Average queue waiting time)
		Steady-State average waiting time (Average queue waiting time + average service time)
		Steady-State time-average number in queue (Average queue size)
		Utilization factor.
	      Expected conservation equation:
		Q = D*U/S = D/E = D*A
		           where Q is Average queue size
		                 U is the Utilization factor 
		                 D is the average queue waiting time 
		                 S is the average service time 
				 E is the mean interarrival time and 
		                 A is Arrival Rate, number of arrivals per time unit
		                 which is the reciprocal of the Mean interarrival time..
	B. Assumptions
	   1. Simulation time: 500,000 Units
	   2. Customer interarrival time: 
	      Uniform distribution between 0 and an input MAX value.
	   3. Service Time: Uniform distribution between 10 and an input MAX.
	   4. Queue-Waiting jobs classified into two types:
	      LOW-Queue Jobs: requested service time > 20% of the service time
	      range (the difference between the min service time expected and 
	      the max service time expected).
	      HIGH-Queue Jobs: Other jobs (with relatively short service time)
	   5. A Low-Queue job will be picked up for service only if
		a. there is NO job in the HIGH-Queue or
		b. a Low-Queue job has been waiting more than 5,000 time units 
		   longer than any HIGH-Queue job.
	C. Required outputs
	   For each input parameter set (of Max interarrival time and Max
	   service time),
	   1. Utilization Factor (Expected to be the mean Service time divided by the expected mean 
	      Inter-arrival time)
	   2. For each waiting queue and two queues combined:
	      2a. # of jobs processed
	      2b. Average queue size
	      2c. Average queue waiting time
	      2d. Max waiting time.
	D. Use following inputs:
	   500	300
	   500	400
	   500	510	
	E. Your task:
	   Now, you only may have to fill in three routines
	   that have been left out in the following partial solution:
	   They are
		Subroutine InsertEvent (Node, Head, Tail)
		Subroutine QDelete(Q)
		Subroutine finishing

	******************PARTIAL SOLUTION********************

  ! 	Single-server queueing system  simulation.
  !
	Module events
	  Implicit None
	  Type Event
	    Integer:: 	           	EType
	    Integer::              	OccurTime
	    Type(Event), Pointer:: 	Next
	    End Type Event
	  Contains
	    Subroutine Initialize (Head,Tail)
	      Type(Event), Pointer :: Head, Tail
	      Nullify (Head,Tail)
	      Return
	      End Subroutine Initialize
	    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	    Recursive Subroutine InsertEvent (Node, Head, Tail)
	    ! Inserts the given event-node into the list
	    ! in ascending order of occurrence time of scheduled events
	      Type(Event), pointer :: Head, Tail
	      Type(Event), pointer :: Node, NewHead
	    ! Rest is left out for you to fill in
	    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	    Subroutine DeleteEvent (H,T,Delete)
	    ! This subroutine simply removes the first event from
	    ! the event-queue and returns it
	      Type(Event), pointer :: H,T
	      Type(Event), pointer :: Delete 
	      if (.NOT.Associated(H))  then	! queue-empty and an error
		Print *, "ERROR:::EVENT-QUEUE IS ALREADY EMPTY"
		Return
		end if
	      Delete => H
	      H => H%Next
	      ! If this new head is empty, Tail must become empty as well
	      if (.NOT. Associated(H)) Nullify(T)
	      return
	    End Subroutine DeleteEvent
	  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
	End Module Events
	!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	Program SingleServer
	Use Events
	Implicit None
	Integer, parameter :: Hq=2000, Lq=4000
  	LOGICAL IDLE	
	Integer HIGHQ(Hq,2), LOWQ(Lq,2), TOTAL(2), 	& !CUMULATIVE QUEUE SIZES
	        QSIZE(2), QMAX(2), FRONT(2), JOBS(2),	&
	        WAIT(2), &   !CUMULATIVE WAITING TIMES IN EACH QUE
	        LASTCH(2), & !LAST QUEUE SIZE CHANGE TIME
	        TIME, &      ! CURRENT SIMULATION TIME
	        LASTIDLE, &  !LAST server IDLE TIME
	        IDLETIME, &  !CUMULATIVE server IDLE TIMES
	        OFTEN, &     ! INTERARRIVAL TIME UpperBound
	        ServHigh, &  !Service time upperbound
	        TotalTime, Inputs, K
	Logical More
	Type(Event), Pointer :: Head, Tail, Node, Current
	Integer testing /0/
  !	INITIALIZATIONS
	Qmax = (/Hq, Lq/)
	totaltime=500000
	Inputs=0
	More = .true.
	Do while(More)
	  Print *, "--------------------------------"
	  Print *, 'Type in' 
	  Print *, "The InterArrival Time Upperbound"
	  Print *, 'And Service Time Upperbound!'
	  Read(5,*,End=9999) Often, ServHigh
	print *,     '------------------------------------------'
	  PRINT 999, 'UPPER BOUND FOR INTERARRIVAL TIME :::', OFTEN
	  Print 999, 'UPPER BOUND FOR SERVICE TIME:::::::::', ServHigh
  999	  Format (1X, A, I5)
	  Inputs = Inputs+1
	  LASTIDLE=0
	  TIME=0
	  IdleTime=0
	  IDLE= .TRUE.
	  DO K=1,2
	    QSIZE(K)=0
	    TOTAL(K)=0
	    LASTCH(K)=0
	    JOBS(K)=0
	    WAIT(K)=0
  	    FRONT(K)=0
	    end do
  !
	  Call Initialize (Head,Tail)
	  Call MakeNode(1,0,Node)
	  CALL InsertEvent (Node,Head,Tail)  !FIRST ARRIVAL EVENT SCHEDULED
	  Call MakeNode(2,0,Node)
	  CALL InsertEvent (Node,Head,Tail)  !FIRST FINISHING EVENT SCHEDULED
	  Call MakeNode(3,TotalTime,Node)
	  CALL InsertEvent (Node,Head,Tail)  !SIMULATION TERMINATION
  !
  !	LOOP TO CONTINUE THE SIMULATION BY PICKING UP AN EVENT
  !	after another until simulation total time comes up.
	  TIME=Head%OccurTime
	  DO WHILE (TIME .LT. totaltime)
	    Select Case  (Head%EType)
 100	      Case(1) ! NEW ARRIVAL
	        CALL ARRIVAL
	        IF (IDLE) CALL FINISHING
	    
 200	      Case(2) ! FINISHING EVENT
	        CALL FINISHING

	      Case(3) ! Termination Event
	        Call Finishing	! Just update statistics
		Call Qdelete(1)
		Call Qdelete(2)
		Exit

	      Case Default	! Safety measure
		Print *, "ERROR: ILLEGAL EVENT TYPE IN EVENT-QUEUE:"
	      End Select
	    Call DeleteEvent (Head,Tail,Current)
	    Time=Head%OccurTime	   ! Advance the simulation time in all cases
				   ! unless it is over
	    END DO !END OF ONE SIMULATION RUN
  !
  !	END OF ONE SIMULATION RUN!!!!!
  !
  
	  Print *,   '-------------------------------'
	  Print *,   "     SIMULATION STATISTICS"
	  Print *,   "-------------------------------"
	  Print *,   "                       High-Q:  Low_Q:  Total:"
	  PRINT 777, 'JOBS PROCESSED:       ', JOBS, jobs(1)+jobs(2)
 777	  Format (1X, A, 3I8)
	  PRINT 788, 'AVERAGE QUEUE SIZES:  ', & 
                      1.0*TOTAL(1)/totaltime,  &
                      1.0*TOTAL(2)/totaltime,  &
                      0.5*(Total(1)+Total(2))/TotalTime
 788  	  format (1X, A, 3F8.2)
	  PRINT 788, 'AVERAGE WAITING TIMES:', & 
                      1.0*WAIT(1)/JOBS(1),     &
                      1.0*WAIT(2)/JOBS(2),     &
                      1.0*(wait(1)+wait(2))/(jobs(1)+Jobs(2))
	  PRINT 888, 'TOTAL IDLE TIME PERCENTS:::', 100.0*IDLETIME/TotalTime
  888	  format (1X, A, F10.2, "%")
	  END DO  !!!END OF ALL SIMULATION RUNS
 9999	Print *
	Print *, "SIMULATION OVER AFTER PROCESSING", Inputs, "INPUTS."
  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	Stop	! Just in case.
	Contains
	Subroutine MakeNode (EventType, EventTime, Item)
	  Type(Event), pointer :: Item
	  Integer EventType, EventTime, Err
	  Allocate (Item, Stat=Err)
	  if (Err /= 0) then
		Print *, "MEMORY ALLOCATION FAILURE:::"
		Stop
		end if
	  Item%EType = EventType
	  Item%OccurTime = EventTime
	  Nullify (Item%Next)
	  return
	  End  Subroutine	! Mandatory
	!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	SUBROUTINE QINSERT(Q,REQUEST)
	Integer Q, Loc, Request
	IF (QSIZE(Q) .GE. QMAX(Q)) THEN !QUEUE IS FULL
	  PRINT*, 'ERROR::: QUEUE IS FULL::::', Q
	  RETURN
	  END IF
	QSIZE(Q)=QSIZE(Q)+1
	LOC=FRONT(Q)+QSIZE(Q)
	IF (LOC .GT. QMAX(Q)) LOC=LOC-QMAX(Q)
	IF (Q .EQ. 1) THEN !!HIGHQ!!!
	  HIGHQ(LOC,1)=TIME !ARRIVAL TIME!!!
	  HIGHQ(LOC,2)=REQUEST !Service REQUEST TIME!!!
	ELSE
	  LOWQ(LOC,1)=TIME
	  LOWQ(LOC,2)=REQUEST
	END IF
  
  !	UPDATE CUMULATIVE QUEUE SIZE
	TOTAL(Q)=TOTAL(Q)+ (TIME-LASTCH(Q))*(QSIZE(Q)-1)
	LASTCH(Q)=TIME
	RETURN
	END Subroutine
        !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	SUBROUTINE QDELETE(Q)
	! This subroutine does the following:
          1. Check to see If the given queue is empty.
	     If it is then an ERROR-CASE and return.
	  2. Else
	     2.a update Front and Qsize of the queue to get the request 
	         time to schedule a FINISHING event by:
	  		Call MakeNode(2, Time+Request, Node)
	  		CALL InsertEvent(Node, Head, Tail)
             2.b Update
			Total(Q)
			JOBS(Q)
			WAIT(Q)
			LastCh(Q)
			if (Qsize(Q) == 0) Front(Q) = 0
	END Subroutine
  !*********************************************
  !	SUBROUTINE  ARRIVAL
  !*********************************************
	SUBROUTINE ARRIVAL
	Integer Request, After, Type
	REQUEST=KRAND(ServHigh-10)+10
	TYPE=1
	IF (REQUEST .GT. 0.2*(ServHigh-10)+10.001) TYPE=2
	CALL QINSERT(TYPE,REQUEST)
	AFTER=KRAND(OFTEN)! It is between 0 and OFTEN
	Call MakeNode(1,Time+After,Node)
	CALL InsertEvent(Node,Head,Tail)
	RETURN
	END Subroutine
  !********************************************
  !	FUNCTION KRAND(MAX)
  !********************************************
	Integer FUNCTION KRAND(MAX)
	! Computes and returns an integer between
	! 0 and the given MAX
	Integer Max
	Integer SEED/9753135/
	Krand = Ran(Seed) * Max + 0.0001	
	RETURN
	END Function
!*********************************************
!	SUBROUTINE FINISHING
!*********************************************
	SUBROUTINE FINISHING
	Integer front1, front2
!	THIS SUBROUTINE IS CALLED WHENEVER A JOB HAS BEEN FINISHED,
!	OR THE SIMULATION IS OVER.
!	HENCE, IT DOES:
!	1. IF server HAS BEEN IDLE, UPDATE CUMULATIVE IDLE TIME
!	   AND RESET THE LAST IDLE TIME.
!	2. IF BOTH QUEUES ARE EMPTY, RESET IDLE AND LASTIDLE,
!	   AND RETURN.
!	3. ELSE, IF THE LOWQUE IS EMPTY, THEN DELETE THE HIGHQUE
!	4. ELSE, IF THE HIGHQUE IS EMPTY, DELETE THE LOWQUE.
!	5. ELSE, (BOTH QUEUES ARE NON-EMPTY) SO DECIDE WHICH
!	   QUEUE HAS TO BE DELETED. AND DELETE THAT QUEUE AND
!	   RESET IDLE INDICATOR.
!	In all, this subroutine deletes a queue if both are not empty
	by calling Qdelete(Q) and updates if necessary 
		IdleTime
		LastIdle and 
		Idle