Exemplo n.º 1
0
class ABCLine(metaclass=ABCMeta):
    """
    This class contains everything that is common to the Line and LineStack 
    classes. Since this is also an abstract base class, it cannot be used in a 
    standalone fashion. Its methods and attributes can only be reached through 
    its subclasses Line and LineStack, which inherit from this class.
    """
# ------------------------------------------------------------------------------

    @abstractmethod
    def __init__(self, line, nserv):
        """
        line  is 'Line' or 'LineStack'
        nserv is the initial number of servers
        """

        # Attributes made available from the outside (not assignable, though):
        # --------------------------------------------------------------------
        
        self.__narriv    = 0     # Accumulated number of arrivers until present
        
        self.__ninline   = 0     # Present number waiting in line

        self.__nfreeserv = nserv # Present number of free servers

        self.__nbalked   = 0     # Accumulated number of balkers

        self.__nreneged  = 0     # Accumulated number of renegers  

        self.__nescaped  = 0     # = self.__nbalked + self.__nreneged


        # Attributes not available from the outside:
        # --------------------------------------------------------------------
        
        self.__nserv    = nserv # Initial number of free servers

        if   line == 'Line':       self.__line = Deque()
        elif line == 'LineStack':  self.__line = Stack()
        else: raise Error("'line' must be 'Line' or 'LineStack' in ABCLine!")

                                # dict containing the "reneging times" (time  
        self.__renegers = {}    # points of the future reneging events) with 
                                # the corresponding arrival times as keys

        self.__wtimes   = array('d', [])   
                          # 'd' array of waiting times for those not escaped

        self.__length   = {}    # dict containing the line length history with 
                                # the corresponding time spans as keys

        self.__prevctl  = 0.0   # The previous clock time when the line length 
                                # was last changed

                                # dict containing the history of the number of 
        self.__systemh  = {}    # customers in the system with the corresponding
                                # time spans as keys

        self.__prevcts  = 0.0   # The previous clock time when the number of 
                                # customers in system was last changed

        self.__length   = {}    # dict containing the line length history with 
                                # the corresponding time spans as keys

        self.__prevctl  = 0.0   # The previous clock time when the line length 
                                # was last changed

                                # dict containing the history of the number of 
        self.__idleh    = {}    # of free/idle servers in the system with the 
                                # corresponding time spans as keys

        self.__prevcti  = 0.0   # The previous clock time when the number of 
                                # free/idle servers was last changed

    # end of __init__

# ------------------------------------------------------------------------------

    def __getattr__(self, attr_name):
        """
        This method overrides the built-in __getattr__ and makes the values
        of the internal attributes externally available.
        """

        if   attr_name == "ninline":    return self.__ninline
        elif attr_name == "nfreeserv":  return self.__nfreeserv
        elif attr_name == "narriv":     return self.__narriv
        elif attr_name == "nbalked":    return self.__nbalked
        elif attr_name == "renegers":   return self.__renegers
        elif attr_name == "nreneged":   return self.__nreneged
        elif attr_name == "nescaped":   return self.__nescaped

    # end of __getattr__

# ------------------------------------------------------------------------------

    def __setattr__(self, attr_name, value):
        """
        This method overrides the built-in __setattr__ and makes the values of 
        the internal attributes externally available more difficult to screw 
        up from the outside.
        """

        if attr_name == "ninline"   \
        or attr_name == "nfreeserv" \
        or attr_name == "narriv"    \
        or attr_name == "nbalked"   \
        or attr_name == "renegers"  \
        or attr_name == "nreneged"  \
        or attr_name == "nescaped"  :
            errtxt1 = ": Can't change value/length of attribute"
            errtxt2 = " from the outside!"
            raise Error(attr_name + errtxt1 + errtxt2)

        else:
            self.__dict__[attr_name] = value

    # end of __setattr__

# ------------------------------------------------------------------------------

    def place_last_in_line(self, times):
        """
        Add one or several arrival times at the back end of the line. 
        The length of the expanded line is returned. 

        NB The elements in an iterable will be placed so that the last 
        will be the last in the line etc.

        Arguments:
        ----------
        times     single time or tuple/list of times

        Outputs:
        ----------
        The number presently in the expanded line
        """

        try:       # First try the hunch that the argument is a sequence:
            self.__narriv += len(times)
            for tim in times:
                self.__length[tim-self.__prevctl]  = len(self.__line)
                self.__systemh[tim-self.__prevcts] = len(self.__line) + \
                                                 self.__nserv - self.__nfreeserv
                self.__prevctl = tim
                self.__prevcts = tim
            
        except TypeError:   # ..otherwise the arg is a single number
            self.__narriv += 1
            self.__length[times-self.__prevctl]  = len(self.__line)
            self.__systemh[times-self.__prevcts] = len(self.__line) + \
                                               self.__nserv - self.__nfreeserv
            self.__prevctl = times
            self.__prevcts = times

        self.__line.push(times)
            
        self.__ninline = len(self.__line)

        return self.__ninline

    # end of place_last_in_line

# ------------------------------------------------------------------------------

    def place_first_in_line(self, times):
        """
        Add one or several arrival times at the front end of the line. 
        The length of the expanded line is returned. 

        NB The elements in an iterable will be placed so that the first 
        will be the first in the line etc.

        Arguments:
        ----------
        times     single time or tuple/list of times

        Outputs:
        ----------
        The length of the expanded line 
        """

        try:          # First try the hunch that the argument is a sequence:
            self.__narriv += len(times)
            for tim in times:
                self.__length[tim-self.__prevctl]  = len(self.__line)
                self.__systemh[tim-self.__prevcts] = len(self.__line) + \
                                                 self.__nserv - self.__nfreeserv
                self.__prevctl = tim
                self.__prevcts = tim

        except TypeError:   # ..otherwise the arg is a single number
            self.__narriv += 1
            self.__length[times-self.__prevctl]  = len(self.__line)
            self.__systemh[times-self.__prevctl] = len(self.__line) + \
                                               self.__nserv - self.__nfreeserv
            self.__prevctl = times
            self.__prevcts = times

        self.__line.unshift(times)
            
        self.__ninline = len(self.__line)

        return self.__ninline

    # end of place_first_in_line

# ------------------------------------------------------------------------------

    def call_next_in_line(self, calltime):
        """
        Fetch the first arrival time at the front end of the line,
        remove it from the line, and make one server busy.
        
        Outputs:
        --------
        The arrival time at the front end of the line 
        """

        self.__length[calltime-self.__prevctl]  = len(self.__line)
        self.__idleh[calltime-self.__prevcti]   = self.nfreeserv
        self.__systemh[calltime-self.__prevcts] = len(self.__line) + \
                                              self.__nserv - self.__nfreeserv
        self.__prevctl    = calltime
        self.__prevcti    = calltime
        self.__prevcts    = calltime

        arrivaltime       = self.__line.shift()

        self.__nfreeserv -= 1
        if self.__nfreeserv < 0:
            raise Error("Number of servers are negative in call_next_in_line!")

        self.__wtimes.append(calltime-arrivaltime)

        self.__ninline    = len(self.__line)

        return arrivaltime

    # end of call_next_in_line

# ------------------------------------------------------------------------------

    def remove_last_in_line(self, tim):
        """
        Fetch the last arrival time at the back end of the line and remove 
        it from the line. To be used - for instance - when a customer that 
        already has been placed in line balks (even balkers must first be 
        placed in line - before balking!).

        Outputs:
        --------
        The arrival time at the back end of the line 
        """

        self.__length[tim-self.__prevctl]  = len(self.__line)
        self.__systemh[tim-self.__prevcts] = len(self.__line) + \
                                         self.__nserv - self.__nfreeserv
        self.__prevctl = tim
        self.__prevcts = tim

        lasttime       = self.__line.pop()

        self.__ninline = len(self.__line)

        return lasttime

    # end of remove_last_in_line

# ------------------------------------------------------------------------------

    def server_freed_up(self, tim):
        """
        Adds '1' to the present number of free servers - 
        to be used when a customer has been served.
        May also be used to change the total number of servers 
        during the course of a simulation.
        """

        self.__idleh[tim-self.__prevcti]   = self.nfreeserv
        self.__systemh[tim-self.__prevcts] = len(self.__line) + \
                                         self.__nserv - self.__nfreeserv
        self.__nfreeserv += 1
        self.__prevcti = tim
        self.__prevcts = tim

    # end of server_freed_up

# ------------------------------------------------------------------------------

    def balker(self, tim):
        """
        Removes the arrival just placed last in line. Returns the arrival 
        time of the balker.
        
        NB. Balkers must first be placed in line - before balking!
        Outputs:
        --------
        The arrival time of the balker 
        """

        self.__nbalked  += 1
        self.__nescaped += 1
        
        return self.remove_last_in_line(tim)

    # end of balker

# ------------------------------------------------------------------------------

    def reneger(self, arentime):
        """
        To be used when a "renege" type event is picked up by get_next_event 
        and removes the corresponding arrival time (arentime) in the line of 
        arrival times GIVEN that it has not been removed already from calling  
        call_next_in_line (the existence of the corresponding arrival time in 
        the line is checked first). 
        """

        arrivaltime = self.__renegers[arentime]
        del self.__renegers[arentime]

        if arrivaltime in self.__line:
            self.__length[arentime-self.__prevctl]  = len(self.__line)
            self.__systemh[arentime-self.__prevcts] = len(self.__line) + \
                                                  self.__nserv - self.__nfreeserv
            self.__prevctl   = arentime
            self.__prevcts   = arentime
            self.__line.remove(arrivaltime)
            self.__ninline   = len(self.__line)
            self.__nreneged += 1
            self.__nescaped += 1
            return True 
        else:
            return False

    # end of reneger

# ------------------------------------------------------------------------------

    def waiting_times_all(self):
        """
        Returns an unsorted 'd' array containing the waiting times 
        for all served. 
        """

        return self.__wtimes

    # end of waiting_times_all

# ------------------------------------------------------------------------------

    def waiting_times_linedup(self):
        """
        Returns an unsorted 'd' array containing the waiting times 
        only for those who had to wait in line before being served. 
        """

        wtimesshort = array('d', [])
        for wt in self.__wtimes:
            if wt != 0.0: wtimesshort.append(wt)

        return wtimesshort

    # end of waiting_times_linedup

# ------------------------------------------------------------------------------

    def line_stats(self):
        """
        Returns a dict containing line length statistics with the line lengths 
        as keys and the corresponding times as values.
        """

        # This method turns the self.__length dict around: the statistics are 
        # collected with the line length as keys and the corresponding times 
        # as values (there will be fewer elements in the returned dict than in 
        # self.__length, of course...)

        statdict = {}
        for keytime in self.__length:
            try:
                statdict[self.__length[keytime]] += keytime
            except KeyError:
                statdict[self.__length[keytime]]  = keytime

        return statdict

    # end of line_stats

# ------------------------------------------------------------------------------

    def idle_stats(self):
        """
        Returns a dict containing server statistics with the number of idle 
        servers as keys and the corresponding times as values.
        """

        # This method turns the self.__idleh dict around: the statistics are 
        # collected with the number of free/idle servers in the system as keys 
        # and the corresponding times as values (there will be fewer elements 
        # in the returned dict than in self.__idleh, of course...)

        statdict = {}
        for keytime in self.__idleh:
            try:
                statdict[self.__idleh[keytime]] += keytime
            except KeyError:
                statdict[self.__idleh[keytime]]  = keytime

        return statdict

    # end of idle_stats

# ------------------------------------------------------------------------------

    def system_stats(self):
        """
        Returns a dict containing statistics for the total number of customers 
        in the system as keys and the corresponding times as values.
        """

        # This method turns the self.__systemh dict around: the statstics are 
        # collected with the number of customers in the system as keys and the 
        # corresponding times as values (there will be fewer elements in the 
        # returned dict than in self.__systemh, of course...)

        statdict = {}
        for keytime in self.__systemh:
            try:
                statdict[self.__systemh[keytime]] += keytime
            except KeyError:
                statdict[self.__systemh[keytime]]  = keytime

        return statdict

    # end of system_stats

# ------------------------------------------------------------------------------

# end of ABCLine

# ------------------------------------------------------------------------------
Exemplo n.º 2
0
class EventScheduleStack:
    """
    The class defines a schedule of events in a general discrete-event 
    simulation. It relies on the misclib.Stack class (but it does not 
    inherit from the Stack class). 

    EventScheduleStack is less efficient (=slower) than the heap-based 
    EventSchedule class but the two classes are otherwise equivalent in 
    principle. Since it uses the Stack class also for the event types,
    and since all the methods of the list class are available via the 
    Stack class, EventScheduleStack may be used for creating subclasses 
    to this class which can handle more complex types of schedules than 
    those that can be handled by the dict-based EventSchedule.

    An additional feature of EventScheduleStack is that it can handle TIES 
    perfectly (the dict-based EventSchedule only handles ties approximately).
    """
# ------------------------------------------------------------------------------

    def __init__(self, eventlist=[], timelist=[], sort=False):
        """
        Creates two new stacks: one for the events and one for the corresponding 
        time points. The events could for instance be described by strings. The 
        times are (of course) floating-point numbers. The two stacks can be 
        filled here but they have to be synchronized and in temporal order. 
        """

        assert len(timelist) == len(eventlist), \
                "input lists are of unequal length in EventScheduleStack!"

        # If sort:    # to be added later

        self.__eventstack = Stack(eventlist)
        self.__timestack  = Stack(timelist)

    # end of __init__

# ------------------------------------------------------------------------------

    def put_event(self, eventtype, eventtime):
        """
        Method used to place an event in the event schedule. The event is placed 
        in temporal order in the synchronized stacks eventstack and timestack. 
        """

        # Place in correct time order (what push and unshift returns - the 
        # number of  elements in the Stack After the "putting" - is not needed 
        # for anything and it is OK to do as below)

        if not self.__timestack or eventtime >= self.__timestack[-1]: # Put last
            self.__timestack.push(eventtime)
            self.__eventstack.push(eventtype)
            #self.__timestack  = [eventtime] # Does not work - turns the 
            #self.__eventstack = [eventtype] # Stack into a list again
    
        elif eventtime < self.__timestack[0]:                # Put first
            self.__timestack.unshift(eventtime)
            self.__eventstack.unshift(eventtype)

        else:
            index = bisect(self.__timestack, eventtime)
            self.__timestack.splice(index, 0, eventtime)
            self.__eventstack.splice(index, 0, eventtype)

        return eventtime  # For symmetry with put_event of EventSchedule

    # end of put_event

# ------------------------------------------------------------------------------

    def show_next_event(self):
        """
        Just look at the next event without touching the stack. 
        """

        #if not equal_length(self.__eventstack, self.__timestack):
        # is not needed due to put_event

        try:
            nextevent = self.__eventstack[0]
        except IndexError:
            nextevent = None

        try:
            nexttime  = self.__timestack[0]
        except IndexError:
            nexttime  =  None

        return nextevent, nexttime

    # end of show_next_event

# ------------------------------------------------------------------------------

    def get_next_event(self):
        """
        Method used to get the next stored event (the first in time) from the 
        event schedule. The synchronized stacks eventstack and timestack are 
        shifted in Perl fashion - i. e. the element that is returned is also 
        removed from the stack. Returns None if the stacks are empty. 
        """

        #if not equal_length(self.__eventstack, self.__timestack):
        # is not needed due to put_event

        nextevent = self.__eventstack.shift()   # None if empty
        nexttime  = self.__timestack.shift()    # None if empty

        return nextevent, nexttime

    # end of get_next_event

# ------------------------------------------------------------------------------

    def zap_events(self):
        """
        Empty the schedule to allow for a restart. Return the length of 
        the stack as it was before zapping.
        """

        #if not equal_length(self.__eventstack, self.__timestack):
        # is not needed due to put_event

        length = self.__eventstack.zap()
        length = self.__timestack.zap()

        return length

    # end of zap_events

# ------------------------------------------------------------------------------

# end of EventScheduleStack

# ------------------------------------------------------------------------------
Exemplo n.º 3
0
class EventScheduleStack:
    """
    The class defines a schedule of events in a general discrete-event 
    simulation. It relies on the misclib.Stack class (but it does not 
    inherit from the Stack class). 

    EventScheduleStack is less efficient (=slower) than the heap-based 
    EventSchedule class but the two classes are otherwise equivalent in 
    principle. Since it uses the Stack class also for the event types,
    and since all the methods of the list class are available via the 
    Stack class, EventScheduleStack may be used for creating subclasses 
    to this class which can handle more complex types of schedules than 
    those that can be handled by the dict-based EventSchedule.

    An additional feature of EventScheduleStack is that it can handle TIES 
    perfectly (the dict-based EventSchedule only handles ties approximately).
    """

    # ------------------------------------------------------------------------------

    def __init__(self, eventlist=[], timelist=[], sort=False):
        """
        Creates two new stacks: one for the events and one for the corresponding 
        time points. The events could for instance be described by strings. The 
        times are (of course) floating-point numbers. The two stacks can be 
        filled here but they have to be synchronized and in temporal order. 
        """

        assert len(timelist) == len(eventlist), \
                "input lists are of unequal length in EventScheduleStack!"

        # If sort:    # to be added later

        self.__eventstack = Stack(eventlist)
        self.__timestack = Stack(timelist)

    # end of __init__

# ------------------------------------------------------------------------------

    def put_event(self, eventtype, eventtime):
        """
        Method used to place an event in the event schedule. The event is placed 
        in temporal order in the synchronized stacks eventstack and timestack. 
        """

        # Place in correct time order (what push and unshift returns - the
        # number of  elements in the Stack After the "putting" - is not needed
        # for anything and it is OK to do as below)

        if not self.__timestack or eventtime >= self.__timestack[
                -1]:  # Put last
            self.__timestack.push(eventtime)
            self.__eventstack.push(eventtype)
            #self.__timestack  = [eventtime] # Does not work - turns the
            #self.__eventstack = [eventtype] # Stack into a list again

        elif eventtime < self.__timestack[0]:  # Put first
            self.__timestack.unshift(eventtime)
            self.__eventstack.unshift(eventtype)

        else:
            index = bisect(self.__timestack, eventtime)
            self.__timestack.splice(index, 0, eventtime)
            self.__eventstack.splice(index, 0, eventtype)

        return eventtime  # For symmetry with put_event of EventSchedule

    # end of put_event

# ------------------------------------------------------------------------------

    def show_next_event(self):
        """
        Just look at the next event without touching the stack. 
        """

        #if not equal_length(self.__eventstack, self.__timestack):
        # is not needed due to put_event

        try:
            nextevent = self.__eventstack[0]
        except IndexError:
            nextevent = None

        try:
            nexttime = self.__timestack[0]
        except IndexError:
            nexttime = None

        return nextevent, nexttime

    # end of show_next_event

# ------------------------------------------------------------------------------

    def get_next_event(self):
        """
        Method used to get the next stored event (the first in time) from the 
        event schedule. The synchronized stacks eventstack and timestack are 
        shifted in Perl fashion - i. e. the element that is returned is also 
        removed from the stack. Returns None if the stacks are empty. 
        """

        #if not equal_length(self.__eventstack, self.__timestack):
        # is not needed due to put_event

        nextevent = self.__eventstack.shift()  # None if empty
        nexttime = self.__timestack.shift()  # None if empty

        return nextevent, nexttime

    # end of get_next_event

# ------------------------------------------------------------------------------

    def zap_events(self):
        """
        Empty the schedule to allow for a restart. Return the length of 
        the stack as it was before zapping.
        """

        #if not equal_length(self.__eventstack, self.__timestack):
        # is not needed due to put_event

        length = self.__eventstack.zap()
        length = self.__timestack.zap()

        return length

    # end of zap_events


# ------------------------------------------------------------------------------

# end of EventScheduleStack

# ------------------------------------------------------------------------------
Exemplo n.º 4
0
class ABCLine(metaclass=ABCMeta):
    """
    This class contains everything that is common to the Line and LineStack 
    classes. Since this is also an abstract base class, it cannot be used in a 
    standalone fashion. Its methods and attributes can only be reached through 
    its subclasses Line and LineStack, which inherit from this class.
    """
    # ------------------------------------------------------------------------------

    @abstractmethod
    def __init__(self, line, nserv):
        """
        line  is 'Line' or 'LineStack'
        nserv is the initial number of servers
        """

        # Attributes made available from the outside (not assignable, though):
        # --------------------------------------------------------------------

        self.__narriv = 0  # Accumulated number of arrivers until present

        self.__ninline = 0  # Present number waiting in line

        self.__nfreeserv = nserv  # Present number of free servers

        self.__nbalked = 0  # Accumulated number of balkers

        self.__nreneged = 0  # Accumulated number of renegers

        self.__nescaped = 0  # = self.__nbalked + self.__nreneged

        # Attributes not available from the outside:
        # --------------------------------------------------------------------

        self.__nserv = nserv  # Initial number of free servers

        if line == 'Line': self.__line = Deque()
        elif line == 'LineStack': self.__line = Stack()
        else: raise Error("'line' must be 'Line' or 'LineStack' in ABCLine!")

        # dict containing the "reneging times" (time
        self.__renegers = {}  # points of the future reneging events) with
        # the corresponding arrival times as keys

        self.__wtimes = array('d', [])
        # 'd' array of waiting times for those not escaped

        self.__length = {}  # dict containing the line length history with
        # the corresponding time spans as keys

        self.__prevctl = 0.0  # The previous clock time when the line length
        # was last changed

        # dict containing the history of the number of
        self.__systemh = {}  # customers in the system with the corresponding
        # time spans as keys

        self.__prevcts = 0.0  # The previous clock time when the number of
        # customers in system was last changed

        self.__length = {}  # dict containing the line length history with
        # the corresponding time spans as keys

        self.__prevctl = 0.0  # The previous clock time when the line length
        # was last changed

        # dict containing the history of the number of
        self.__idleh = {}  # of free/idle servers in the system with the
        # corresponding time spans as keys

        self.__prevcti = 0.0  # The previous clock time when the number of
        # free/idle servers was last changed

    # end of __init__

# ------------------------------------------------------------------------------

    def __getattr__(self, attr_name):
        """
        This method overrides the built-in __getattr__ and makes the values
        of the internal attributes externally available.
        """

        if attr_name == "ninline": return self.__ninline
        elif attr_name == "nfreeserv": return self.__nfreeserv
        elif attr_name == "narriv": return self.__narriv
        elif attr_name == "nbalked": return self.__nbalked
        elif attr_name == "renegers": return self.__renegers
        elif attr_name == "nreneged": return self.__nreneged
        elif attr_name == "nescaped": return self.__nescaped

    # end of __getattr__

# ------------------------------------------------------------------------------

    def __setattr__(self, attr_name, value):
        """
        This method overrides the built-in __setattr__ and makes the values of 
        the internal attributes externally available more difficult to screw 
        up from the outside.
        """

        if attr_name == "ninline"   \
        or attr_name == "nfreeserv" \
        or attr_name == "narriv"    \
        or attr_name == "nbalked"   \
        or attr_name == "renegers"  \
        or attr_name == "nreneged"  \
        or attr_name == "nescaped"  :
            errtxt1 = ": Can't change value/length of attribute"
            errtxt2 = " from the outside!"
            raise Error(attr_name + errtxt1 + errtxt2)

        else:
            self.__dict__[attr_name] = value

    # end of __setattr__

# ------------------------------------------------------------------------------

    def place_last_in_line(self, times):
        """
        Add one or several arrival times at the back end of the line. 
        The length of the expanded line is returned. 

        NB The elements in an iterable will be placed so that the last 
        will be the last in the line etc.

        Arguments:
        ----------
        times     single time or tuple/list of times

        Outputs:
        ----------
        The number presently in the expanded line
        """

        try:  # First try the hunch that the argument is a sequence:
            self.__narriv += len(times)
            for tim in times:
                self.__length[tim - self.__prevctl] = len(self.__line)
                self.__systemh[tim-self.__prevcts] = len(self.__line) + \
                                                 self.__nserv - self.__nfreeserv
                self.__prevctl = tim
                self.__prevcts = tim

        except TypeError:  # ..otherwise the arg is a single number
            self.__narriv += 1
            self.__length[times - self.__prevctl] = len(self.__line)
            self.__systemh[times-self.__prevcts] = len(self.__line) + \
                                               self.__nserv - self.__nfreeserv
            self.__prevctl = times
            self.__prevcts = times

        self.__line.push(times)

        self.__ninline = len(self.__line)

        return self.__ninline

    # end of place_last_in_line

# ------------------------------------------------------------------------------

    def place_first_in_line(self, times):
        """
        Add one or several arrival times at the front end of the line. 
        The length of the expanded line is returned. 

        NB The elements in an iterable will be placed so that the first 
        will be the first in the line etc.

        Arguments:
        ----------
        times     single time or tuple/list of times

        Outputs:
        ----------
        The length of the expanded line 
        """

        try:  # First try the hunch that the argument is a sequence:
            self.__narriv += len(times)
            for tim in times:
                self.__length[tim - self.__prevctl] = len(self.__line)
                self.__systemh[tim-self.__prevcts] = len(self.__line) + \
                                                 self.__nserv - self.__nfreeserv
                self.__prevctl = tim
                self.__prevcts = tim

        except TypeError:  # ..otherwise the arg is a single number
            self.__narriv += 1
            self.__length[times - self.__prevctl] = len(self.__line)
            self.__systemh[times-self.__prevctl] = len(self.__line) + \
                                               self.__nserv - self.__nfreeserv
            self.__prevctl = times
            self.__prevcts = times

        self.__line.unshift(times)

        self.__ninline = len(self.__line)

        return self.__ninline

    # end of place_first_in_line

# ------------------------------------------------------------------------------

    def call_next_in_line(self, calltime):
        """
        Fetch the first arrival time at the front end of the line,
        remove it from the line, and make one server busy.
        
        Outputs:
        --------
        The arrival time at the front end of the line 
        """

        self.__length[calltime - self.__prevctl] = len(self.__line)
        self.__idleh[calltime - self.__prevcti] = self.nfreeserv
        self.__systemh[calltime-self.__prevcts] = len(self.__line) + \
                                              self.__nserv - self.__nfreeserv
        self.__prevctl = calltime
        self.__prevcti = calltime
        self.__prevcts = calltime

        arrivaltime = self.__line.shift()

        self.__nfreeserv -= 1
        if self.__nfreeserv < 0:
            raise Error("Number of servers are negative in call_next_in_line!")

        self.__wtimes.append(calltime - arrivaltime)

        self.__ninline = len(self.__line)

        return arrivaltime

    # end of call_next_in_line

# ------------------------------------------------------------------------------

    def remove_last_in_line(self, tim):
        """
        Fetch the last arrival time at the back end of the line and remove 
        it from the line. To be used - for instance - when a customer that 
        already has been placed in line balks (even balkers must first be 
        placed in line - before balking!).

        Outputs:
        --------
        The arrival time at the back end of the line 
        """

        self.__length[tim - self.__prevctl] = len(self.__line)
        self.__systemh[tim-self.__prevcts] = len(self.__line) + \
                                         self.__nserv - self.__nfreeserv
        self.__prevctl = tim
        self.__prevcts = tim

        lasttime = self.__line.pop()

        self.__ninline = len(self.__line)

        return lasttime

    # end of remove_last_in_line

# ------------------------------------------------------------------------------

    def server_freed_up(self, tim):
        """
        Adds '1' to the present number of free servers - 
        to be used when a customer has been served.
        May also be used to change the total number of servers 
        during the course of a simulation.
        """

        self.__idleh[tim - self.__prevcti] = self.nfreeserv
        self.__systemh[tim-self.__prevcts] = len(self.__line) + \
                                         self.__nserv - self.__nfreeserv
        self.__nfreeserv += 1
        self.__prevcti = tim
        self.__prevcts = tim

    # end of server_freed_up

# ------------------------------------------------------------------------------

    def balker(self, tim):
        """
        Removes the arrival just placed last in line. Returns the arrival 
        time of the balker.
        
        NB. Balkers must first be placed in line - before balking!
        Outputs:
        --------
        The arrival time of the balker 
        """

        self.__nbalked += 1
        self.__nescaped += 1

        return self.remove_last_in_line(tim)

    # end of balker

# ------------------------------------------------------------------------------

    def reneger(self, arentime):
        """
        To be used when a "renege" type event is picked up by get_next_event 
        and removes the corresponding arrival time (arentime) in the line of 
        arrival times GIVEN that it has not been removed already from calling  
        call_next_in_line (the existence of the corresponding arrival time in 
        the line is checked first). 
        """

        arrivaltime = self.__renegers[arentime]
        del self.__renegers[arentime]

        if arrivaltime in self.__line:
            self.__length[arentime - self.__prevctl] = len(self.__line)
            self.__systemh[arentime-self.__prevcts] = len(self.__line) + \
                                                  self.__nserv - self.__nfreeserv
            self.__prevctl = arentime
            self.__prevcts = arentime
            self.__line.remove(arrivaltime)
            self.__ninline = len(self.__line)
            self.__nreneged += 1
            self.__nescaped += 1
            return True
        else:
            return False

    # end of reneger

# ------------------------------------------------------------------------------

    def waiting_times_all(self):
        """
        Returns an unsorted 'd' array containing the waiting times 
        for all served. 
        """

        return self.__wtimes

    # end of waiting_times_all

# ------------------------------------------------------------------------------

    def waiting_times_linedup(self):
        """
        Returns an unsorted 'd' array containing the waiting times 
        only for those who had to wait in line before being served. 
        """

        wtimesshort = array('d', [])
        for wt in self.__wtimes:
            if wt != 0.0: wtimesshort.append(wt)

        return wtimesshort

    # end of waiting_times_linedup

# ------------------------------------------------------------------------------

    def line_stats(self):
        """
        Returns a dict containing line length statistics with the line lengths 
        as keys and the corresponding times as values.
        """

        # This method turns the self.__length dict around: the statistics are
        # collected with the line length as keys and the corresponding times
        # as values (there will be fewer elements in the returned dict than in
        # self.__length, of course...)

        statdict = {}
        for keytime in self.__length:
            try:
                statdict[self.__length[keytime]] += keytime
            except KeyError:
                statdict[self.__length[keytime]] = keytime

        return statdict

    # end of line_stats

# ------------------------------------------------------------------------------

    def idle_stats(self):
        """
        Returns a dict containing server statistics with the number of idle 
        servers as keys and the corresponding times as values.
        """

        # This method turns the self.__idleh dict around: the statistics are
        # collected with the number of free/idle servers in the system as keys
        # and the corresponding times as values (there will be fewer elements
        # in the returned dict than in self.__idleh, of course...)

        statdict = {}
        for keytime in self.__idleh:
            try:
                statdict[self.__idleh[keytime]] += keytime
            except KeyError:
                statdict[self.__idleh[keytime]] = keytime

        return statdict

    # end of idle_stats

# ------------------------------------------------------------------------------

    def system_stats(self):
        """
        Returns a dict containing statistics for the total number of customers 
        in the system as keys and the corresponding times as values.
        """

        # This method turns the self.__systemh dict around: the statstics are
        # collected with the number of customers in the system as keys and the
        # corresponding times as values (there will be fewer elements in the
        # returned dict than in self.__systemh, of course...)

        statdict = {}
        for keytime in self.__systemh:
            try:
                statdict[self.__systemh[keytime]] += keytime
            except KeyError:
                statdict[self.__systemh[keytime]] = keytime

        return statdict

    # end of system_stats


# ------------------------------------------------------------------------------

# end of ABCLine

# ------------------------------------------------------------------------------