class Supermarket(object):
    """Supermarket object for simulating queuing strategies with a collection of customers."""
    def __init__(self, n_checkouts, checkout_capacity, staging_capacity):
        self.checkouts = CheckoutManager(n_checkouts, checkout_capacity, staging_capacity)
        self._enqueue_strategy = self.checkouts.enqueue_random
        self._leftovers = 0.0
        self._customer_index = 0

    def _set_strategy(self, strategy):
        """Sets the strategy by which customers will choose which checkout queue to join."""
        if strategy is "Random":
            self._enqueue_strategy = self.checkouts.enqueue_random
        elif strategy is "Shortest":
            self._enqueue_strategy = self.checkouts.enqueue_shortest
        elif strategy is "Staging":
            self._enqueue_strategy = self.checkouts.enqueue_staging

    def simulate(self, n_customers, customers_per_minute, strategy):
        """Runs the supermarket simulation on the given set of customers. Customers are added to the checkout queues at
        the given rate. The number of minutes taken to process all customers is returned."""
        self._set_strategy(strategy)
        customers = [Customer(random.uniform(1, 5)) for _ in xrange(n_customers)]
        minutes = 0
        while self._active_customers(customers):
            self._add_customers(customers, customers_per_minute)            # add new customers at set rate
            self.checkouts.update()                                         # update checkouts and customers
            for customer in customers:
                customer.update()
            minutes += 1
        return minutes, customers

    def _active_customers(self, customers):
        """Checks if there is any customers left to process or currently being processed"""
        return self._customer_index < len(customers) or self.checkouts.has_item()

    def _add_customers(self, customers, customers_per_minute):
        """Calculates how many customers to begin queuing on the current iteration. Calculation allows for whole and
        floating point numbers, e.g. 2 = 2 customers per minute, 0.5 = 1 customer every 2 minutes."""
        self._leftovers += customers_per_minute - int(customers_per_minute)  # add decimal part to 'leftovers' registry
        n_customers = int(customers_per_minute) + int(self._leftovers)       # add any 'whole' customers in 'leftovers'
        self._leftovers -= int(self._leftovers)                              # remove 'whole' customers from 'leftovers'

        for _ in xrange(n_customers):
            if self._customer_index < len(customers):
                self._enqueue_strategy(customers[self._customer_index])
                self._customer_index += 1
 def __init__(self, n_checkouts, checkout_capacity, staging_capacity):
     self.checkouts = CheckoutManager(n_checkouts, checkout_capacity, staging_capacity)
     self._enqueue_strategy = self.checkouts.enqueue_random
     self._leftovers = 0.0
     self._customer_index = 0