Exemplo n.º 1
0
    def __init__(self, bufferSize, epsilon, delta, vectorSize, historySize=0):
        self.epsilon = epsilon
        self.delta = delta
        self.buffer = CircularList(bufferSize)  # moving average buffer
        self.models = ModelList(historySize)
        self.mask = None
        self.time = 0
        self.movingAverage = 'No Moving Average'
        self.movingAverageDistance = -1
        self.modelVectorsDistance = -1
        self.winner = 'No Winner'
        self.newWinnerIndex = -1
        self.previousWinnerIndex = -1
        self.verbosity = 0
        self.tolerance = delta
        self.addModels = 1
        self.winnerCount = 0
        self.printDistance = 0
        self.newModelVector = (None, None)
        self.mapModelVector = (None, None)
        self.regions = []  #list of region objects
        self.prevVec = None
        self.eventState = False  #keep track of start of events

        #Initialize region for state -1
        self.regions.append(
            Region(vectorSize, vectorSize, [],
                   len(self.models) - 1))
Exemplo n.º 2
0
    def __init__(self, bufferSize, epsilon, delta, vectorSize, historySize=0):
        self.epsilon = epsilon
        self.delta = delta
        self.buffer = CircularList(bufferSize) # moving average buffer
        self.models = ModelList(historySize)
        self.mask = None
        self.time = 0
        self.movingAverage = 'No Moving Average'
        self.movingAverageDistance = -1
        self.modelVectorsDistance = -1
        self.winner = 'No Winner'
        self.newWinnerIndex = -1
        self.previousWinnerIndex = -1
        self.verbosity = 0
        self.tolerance = delta
        self.addModels = 1
        self.winnerCount = 0
        self.printDistance = 0
        self.newModelVector = (None, None)
        self.mapModelVector = (None, None)
        self.regions = [] #list of region objects
        self.prevVec = None
        self.eventState = False #keep track of start of events

        #Initialize region for state -1
        self.regions.append(Region(vectorSize, vectorSize, [], len(self.models)-1))
Exemplo n.º 3
0
  def move_application(self, app):
    old_host = app.host_server
    cluster_list = self.manager.get_docker_cluster()
    circular_cluster_list = CircularList(
      self.manager.order_cluster_by_load(cluster_list))

    if app.host_server in circular_cluster_list:
      index = circular_cluster_list.index(app.host_server)
      app.host_server = circular_cluster_list[index+1].hostname
    else:
      # Assign the first one in the list if not found above
      app.host_server = circular_cluster_list[0].hostname

    self.logger.info("Moving app {app_name} from {old_host} to {new_host}".format(
      app_name=app.hostname, old_host=old_host, new_host=app.host_server))

    self.logger.info("Bootstrapping the application on the new host")
    self.start_application(app)
Exemplo n.º 4
0
    def move_application(self, app):
        old_host = app.host_server
        cluster_list = self.manager.get_docker_cluster()
        circular_cluster_list = CircularList(
            self.manager.order_cluster_by_load(cluster_list))

        if app.host_server in circular_cluster_list:
            index = circular_cluster_list.index(app.host_server)
            app.host_server = circular_cluster_list[index + 1].hostname
        else:
            # Assign the first one in the list if not found above
            app.host_server = circular_cluster_list[0].hostname

        self.logger.info(
            "Moving app {app_name} from {old_host} to {new_host}".format(
                app_name=app.hostname,
                old_host=old_host,
                new_host=app.host_server))

        self.logger.info("Bootstrapping the application on the new host")
        self.start_application(app)
Exemplo n.º 5
0
Arquivo: ravq.py Projeto: is44c/Calico
 def __init__(self, bufferSize, epsilon, delta, historySize=0):
     self.epsilon = epsilon
     self.delta = delta
     self.buffer = CircularList(bufferSize) # moving average buffer
     self.models = ModelList(historySize)
     self.mask = None 
     self.time = 0
     self.movingAverage = 'No Moving Average'
     self.movingAverageDistance = -1
     self.modelVectorsDistance = -1
     self.winner = 'No Winner'
     self.newWinnerIndex = -1
     self.previousWinnerIndex = -1
     self.verbosity = 0
     self.tolerance = delta
     self.addModels = 1
     self.winnerCount = 0
     self.printDistance = 0
     self.newModelVector = (None, None)
     self.mapModelVector = (None, None)
Exemplo n.º 6
0
Arquivo: ravq.py Projeto: is44c/Calico
 def __init__(self, bucketSize=5):
     CircularList.__init__(self)
     self.bucketSize = bucketSize
Exemplo n.º 7
0
Arquivo: ravq.py Projeto: is44c/Calico
class RAVQ:
    """
    Implements RAVQ algorithm as described in Linaker and Niklasson.
    """
    def __init__(self, bufferSize, epsilon, delta, historySize=0):
        self.epsilon = epsilon
        self.delta = delta
        self.buffer = CircularList(bufferSize) # moving average buffer
        self.models = ModelList(historySize)
        self.mask = None 
        self.time = 0
        self.movingAverage = 'No Moving Average'
        self.movingAverageDistance = -1
        self.modelVectorsDistance = -1
        self.winner = 'No Winner'
        self.newWinnerIndex = -1
        self.previousWinnerIndex = -1
        self.verbosity = 0
        self.tolerance = delta
        self.addModels = 1
        self.winnerCount = 0
        self.printDistance = 0
        self.newModelVector = (None, None)
        self.mapModelVector = (None, None)
      
    # update the RAVQ
    def input(self, vec):
        """
        Drives the ravq. For most uses, the vector categorization
        is as simple as calling ravq.input(vec) on all vec in the
        dataset. Accessing the winning model vector (after the buffer
        is full) can be done directly. Using the get commands after
        calling input will return any information necessary from the
        ravq.
        """
        if self.verbosity > 1:
            print("Step:", self.time)
        if self.verbosity > 2:
            print(vec)
        array = Numeric.array(vec, 'd')
        if self.mask == None:
            self.mask = Numeric.ones(len(array), 'd')
        self.buffer.addItem(array)
        if self.time >= len(self.buffer):
            self.process() 
        if self.verbosity > 2: print(self)
        self.time += 1
        return (self.newWinnerIndex, self.winner)

    # attribute methods
    def getNewWinner(self):
        """
        Returns boolean depending on whether or not there is a new
        winner after the last call to input.
        """
        if self.winnerCount > 0:
            return 0
        else:
            return 1
    def setMask(self, mask):
        """
        The mask serves to weight certain components of the inputs in
        the distance calculations.
        """
        self.mask = Numeric.array(mask, 'd')
    def getWinnerCount(self):
        """
        Returns the number of times the current winner has been the
        winner, ie. the number of consecutive calls to input where the
        current winner has been the winner.
        """
        return self.winnerCount
    def setVerbosity(self, value):
        """
        Determines which print statements to call.
        Debugging only.
        """
        self.verbosity = value
    def setAddModels(self, value):
        """
        Allows the RAVQ to dynamically add model vectors.
        """
        self.addModels = value
        
    # process happens once the buffer is full
    def process(self):
        """
        The RAVQ Algorithm:
        1. Calculate the average vector of all inputs in the buffer.
        2. Calculate the distance of the average from the set of inputs
        in the buffer.
        3. Calculate the distance of the model vectors from the inputs
        in the buffer.
        4. If distance in step 2 is small and distance in step 3 is large,
        add current average to list of model vectors.
        5. Calculate the winning model vector based on distance between
        each model vector and the buffer list.
        6. Update history.
        ---The metric used to calculate distance is described in
        "Sensory Flow Segmentation Using a Resource Allocating
        Vector Quantizer" by Fredrik Linaker and Lars Niklasson
        (2000).---
        """
        self.newModelVector = (None, None)
        self.setMovingAverage()
        self.setMovingAverageDistance()
        self.setModelVectorsDistance()
        if self.verbosity > 2:
            print("Moving average:", self.movingAverage)
        if self.verbosity > 1:
            print("Moving average distance: ", self.movingAverageDistance)
            print("Model vectors distance: ", self. modelVectorsDistance)
        if self.addModels:
            self.updateModelVectors()
        self.updateWinner()
    def setMovingAverage(self):
        """
        Determine moving average.
        """
        self.movingAverage = averageVector(self.buffer.contents)
    def setMovingAverageDistance(self):
        """
        How close is the moving average to the current inputs?
        """
        self.movingAverageDistance = getDistance([self.movingAverage], self.buffer.contents, self.mask)
    def setModelVectorsDistance(self):
        """
        How close are the model vectors to the current inputs?
        """
        if len(self.models) != 0:
            self.modelVectorsDistance = getDistance([v.vector for v in self.models], self.buffer.contents, self.mask)
        else:
            self.modelVectorsDistance = self.epsilon + self.delta
    def updateModelVectors(self):
        """
        Update models vectors with moving average if the moving
        average is the best model of the inputs.
        """
        if self.movingAverageDistance <= self.epsilon and \
               self.movingAverageDistance <= self.modelVectorsDistance - self.delta:
            self.models.addItem(self.movingAverage)
            name = self.models.names[-1]
            self.newModelVector = (name, self.movingAverage)
            if self.verbosity > 1:
                print('***Adding model vector***')
                print('Unique name', name)
                print('Moving avg dist', self.movingAverageDistance)
                print('Model vec dist', self.modelVectorsDistance)
            if self.verbosity > 2:
                print('New model vector', self.movingAverage)

    def updateWinner(self):
        """
        Calculate the current winner based on which model vector is
        closest to the moving average.
        """
        min = []
        for m in self.models:
            min.append(euclideanDistance(m.vector, self.movingAverage, self.mask))
        if min == []:
            self.winner = 'No Winner'
        else:
            self.previousWinnerIndex= self.newWinnerIndex
            self.newWinnerIndex = Numeric.argmin(min)
            if self.previousWinnerIndex == self.newWinnerIndex:
                self.winnerCount += 1
            else:
                self.winnerCount = 0
            winner = self.models[self.newWinnerIndex]
            winner.counter += 1
            #if winner.maxSize != -1:
            winner.addItem(self.buffer[0])
            self.winner = winner.vector
    def distanceMap(self):
        """
        Calculate distance map.
        """
        map = []
        for x, y in [(x.vector,y.vector) for x in self.models for y in self.models]:
            map.append(euclideanDistance(x,y,self.mask))
        return map

    def __str__(self):
        """
        To display ravq just call print <instance>.
        """
        s = ""
        s += "Settings:\n"
        s += "Delta: " + str(self.delta) + " Epsilon: " + str(self.epsilon) + " Buffer Size: " + str(len(self.buffer)) + "\n"
        s += "Time: " + str(self.time) + "\n"
        if self.verbosity > 0:
            s += "Moving average distance: " +  "%4.4f " % self.movingAverageDistance + "\n"
            s += "Model vectors distance: " +  "%4.4f " % self.modelVectorsDistance + "\n"
            s += "Moving average:\n"
            s += "   " + stringArray(self.movingAverage)
            s += "Last winning model vector:\n"
            s += "   " + stringArray(self.winner)
            s += self.bufferString()
        s += self.modelString()
        if self.printDistance:
            s += "Distance map:\n"
            s += self.distanceMapAsString()
        if self.verbosity > 0:
            s += str(self.models)
        return s

    def distanceMapAsString(self):
        return stringArray(self.distanceMap(), 1, len(self.models), format="%4.2f ")

    def saveRAVQToFile(self, filename):
        import pickle
        fp = open(filename, 'w')
        pickle.dump(self, fp)
        fp.close()

    def loadRAVQFromFile(self, filename):
        import pickle
        fp = open(filename, 'r')
        self = pickle.load(fp)

    # helpful string methods, see __str__ method for use.
    def modelString(self):
        s = ""
        cnt = 0
        totalCount = 0
        totalIncompatibility = 0.0
        for m in self.models:
            s += ("%4d Model: " % cnt) + stringArray(m.vector) 
            sum = 0.0
            for b in m.contents:
                sum += euclideanDistance(m.vector, b, self.mask)
            totalIncompatibility += sum
            s += "     Count: %d Buffer size: %d Incompatibility: %f\n" % (m.counter, len(m.contents), sum)
            totalCount += m.counter
            cnt += 1            
        return ("%d Model vectors:\n" % cnt) + s + "Total model vectors  : %d\nTotal mapped vectors : %d\nTotal incompatibility: %f\n" % \
               (cnt, totalCount, totalIncompatibility)
    def bufferString(self):
        s = "Buffer:\n"
        for array in self.buffer:
            s += stringArray(array)
        return s
Exemplo n.º 8
0
Arquivo: ravq.py Projeto: is44c/Calico
 def addItem(self, vector):
     tmp = CircularList(self.bucketSize)
     tmp.vector = vector
     tmp.counter = 0
     CircularList.addItem(self, tmp)
Exemplo n.º 9
0
    def create_containers(self,
                          user,
                          number,
                          formation_name,
                          cpu_shares,
                          ram,
                          port_list,
                          hostname_scheme,
                          volume_list,
                          docker_image,
                          force_host_server=None):

        f = Formation(user, formation_name)
        # Convert ram to bytes from MB
        ram = ram * 1024 * 1024

        # Get the cluster machines on each creation
        cluster_list = self.get_docker_cluster()
        circular_cluster_list = CircularList(
            self.order_cluster_by_load(cluster_list))

        # Loop for the requested amount of containers to be created
        for i in range(1, number + 1):
            # [{"host_port":ssh_host_port, "container_port":ssh_container_port}]
            ssh_host_port = 9022 + i
            ssh_container_port = 22
            host_server = circular_cluster_list[i].hostname
            hostname = '{hostname}{number}'.format(hostname=hostname_scheme,
                                                   number=str(i).zfill(3))

            # First check if we can add this host to salt.  If not exit with -1
            if self.check_salt_key_used(hostname):
                self.logger.error(
                    'Salt key is already taken for {hostname}'.format(
                        hostname=hostname))
                sys.exit(-1)

            # We are being asked to overwrite this
            if force_host_server:
                host_server = force_host_server
            validated_ports = []

            while self.check_port_used(host_server, ssh_host_port):
                ssh_host_port = ssh_host_port + 1

            for port in port_list:
                self.logger.info(
                    "Checking if port {port} on {host} is in use".format(
                        port=port, host=host_server))
                if ':' in port:
                    ports = port.split(':')

                    # Only check if the host port is free.  The container port should be free
                    while self.check_port_used(host_server, ports[0]):
                        ports[0] = int(ports[0]) + 1

                    # Add this to the validated port list
                    validated_ports.append(
                        '{host_port}:{container_port}'.format(
                            host_port=str(ports[0]),
                            container_port=str(ports[1])))
                else:
                    while self.check_port_used(host_server, port):
                        port = int(port) + 1
                    validated_ports.append(str(port))

            self.logger.info(
                'Adding app to formation {formation_name}: {hostname} cpu_shares={cpu} '
                'ram={ram} ports={ports} host_server={host_server} docker_image={docker_image}'
                .format(formation_name=formation_name,
                        hostname=hostname,
                        cpu=cpu_shares,
                        ram=ram,
                        ports=validated_ports,
                        host_server=host_server,
                        docker_image=docker_image))

            f.add_app(None, '{hostname}'.format(hostname=hostname), cpu_shares,
                      ram, validated_ports, ssh_host_port, ssh_container_port,
                      host_server, docker_image, volume_list)

        # Lets get this party started
        for app in f.application_list:
            self.start_application(app)
            #self.logger.info("Sleeping 2 seconds while the container starts")
            #time.sleep(2)
            #self.bootstrap_application(app)

        self.logger.info("Saving the formation to ETCD")
        self.save_formation_to_etcd(f)
Exemplo n.º 10
0
 def addItem(self, vector):
     tmp = CircularList(self.bucketSize)
     tmp.vector = vector
     tmp.counter = 0
     CircularList.addItem(self, tmp)
Exemplo n.º 11
0
 def __init__(self, bucketSize=5):
     CircularList.__init__(self)
     self.bucketSize = bucketSize
Exemplo n.º 12
0
class RAVQ:
    """
    Implements RAVQ algorithm as described in Linaker and Niklasson.
    """
    def __init__(self, bufferSize, epsilon, delta, vectorSize, historySize=0):
        self.epsilon = epsilon
        self.delta = delta
        self.buffer = CircularList(bufferSize)  # moving average buffer
        self.models = ModelList(historySize)
        self.mask = None
        self.time = 0
        self.movingAverage = 'No Moving Average'
        self.movingAverageDistance = -1
        self.modelVectorsDistance = -1
        self.winner = 'No Winner'
        self.newWinnerIndex = -1
        self.previousWinnerIndex = -1
        self.verbosity = 0
        self.tolerance = delta
        self.addModels = 1
        self.winnerCount = 0
        self.printDistance = 0
        self.newModelVector = (None, None)
        self.mapModelVector = (None, None)
        self.regions = []  #list of region objects
        self.prevVec = None
        self.eventState = False  #keep track of start of events

        #Initialize region for state -1
        self.regions.append(
            Region(vectorSize, vectorSize, [],
                   len(self.models) - 1))

    # update the RAVQ
    def input(self, vec, prevVec=None):
        """
        Drives the ravq. For most uses, the vector categorization
        is as simple as calling ravq.input(vec) on all vec in the
        dataset. Accessing the winning model vector (after the buffer
        is full) can be done directly. Using the get commands after
        calling input will return any information necessary from the
        ravq.
        """
        procVec = [i.value for i in vec]
        for i in range(len(vec)):
            if math.isnan(vec[i].value):
                procVec[i] = 0

        if self.verbosity > 1:
            print "Step:", self.time
        if self.verbosity > 2:
            print procVec
        array = Numeric.array(procVec, 'd')
        if self.mask == None:
            self.mask = Numeric.ones(len(array), 'd')
        self.buffer.addItem(array)
        if self.time >= len(self.buffer):
            self.process()
        if self.verbosity > 2: print self
        self.time += 1

        errs = []
        if self.prevVec == None:
            #Either curiosity is off or this is the first vector
            vec, errs = self.regions[self.newWinnerIndex + 1].inVec(procVec)
        else:
            #Process vector using neural net
            vec, errs = self.regions[self.newWinnerIndex + 1].inVecCuriosity(
                vec, self.prevVec)

        return (self.newWinnerIndex, self.winner, vec, errs)

    # attribute methods
    def getNewWinner(self):
        """
        Returns boolean depending on whether or not there is a new
        winner after the last call to input.
        """
        if self.winnerCount > 0:
            return 0
        else:
            return 1

    def setMask(self, mask):
        """
        The mask serves to weight certain components of the inputs in
        the distance calculations.
        """
        self.mask = Numeric.array(mask, 'd')

    def getWinnerCount(self):
        """
        Returns the number of times the current winner has been the
        winner, ie. the number of consecutive calls to input where the
        current winner has been the winner.
        """
        return self.winnerCount

    def setVerbosity(self, value):
        """
        Determines which print statements to call.
        Debugging only.
        """
        self.verbosity = value

    def setAddModels(self, value):
        """
        Allows the RAVQ to dynamically add model vectors.
        """
        self.addModels = value

    # process happens once the buffer is full
    def process(self):
        """
        The RAVQ Algorithm:
        1. Calculate the average vector of all inputs in the buffer.
        2. Calculate the distance of the average from the set of inputs
        in the buffer.
        3. Calculate the distance of the model vectors from the inputs
        in the buffer.
        4. If distance in step 2 is small and distance in step 3 is large,
        add current average to list of model vectors.
        5. Calculate the winning model vector based on distance between
        each model vector and the buffer list.
        6. Update history.
        ---The metric used to calculate distance is described in
        "Sensory Flow Segmentation Using a Resource Allocating
        Vector Quantizer" by Fredrik Linaker and Lars Niklasson
        (2000).---
        """
        self.newModelVector = (None, None)
        self.setMovingAverage()
        self.setMovingAverageDistance()
        self.setModelVectorsDistance()
        if self.verbosity > 2:
            print "Moving average:", self.movingAverage
        if self.verbosity > 1:
            print "Moving average distance: ", self.movingAverageDistance
            print "Model vectors distance: ", self.modelVectorsDistance
        if self.addModels:
            self.updateModelVectors()
        self.updateWinner()

    def setMovingAverage(self):
        """
        Determine moving average.
        """
        self.movingAverage = averageVector(self.buffer.contents)

    def setMovingAverageDistance(self):
        """
        How close is the moving average to the current inputs?
        """
        self.movingAverageDistance = getDistance([self.movingAverage],
                                                 self.buffer.contents,
                                                 self.mask)

    def setModelVectorsDistance(self):
        """
        How close are the model vectors to the current inputs?
        """
        if len(self.models) != 0:
            self.modelVectorsDistance = getDistance(
                [v.vector for v in self.models], self.buffer.contents,
                self.mask)
        else:
            self.modelVectorsDistance = self.epsilon + self.delta

    def updateModelVectors(self):
        """
        Update models vectors with moving average if the moving
        average is the best model of the inputs.
        """
        if self.movingAverageDistance <= self.epsilon and \
               self.movingAverageDistance <= self.modelVectorsDistance - self.delta:
            self.models.addItem(self.movingAverage)
            dims = len(self.models[0].vector)
            self.regions.append(Region(dims, dims, [], len(self.models) - 1))
            name = self.models.names[-1]
            self.newModelVector = (name, self.movingAverage)
            if self.verbosity > 1:
                print '***Adding model vector***'
                print 'Unique name', name
                print 'Moving avg dist', self.movingAverageDistance
                print 'Model vec dist', self.modelVectorsDistance
            if self.verbosity > 2:
                print 'New model vector', self.movingAverage

    def updateWinner(self):
        """
        Calculate the current winner based on which model vector is
        closest to the moving average.
        """
        min = []
        for m in self.models:
            min.append(
                euclideanDistance(m.vector, self.movingAverage, self.mask))
        if min == []:
            self.winner = 'No Winner'
        else:
            self.previousWinnerIndex = self.newWinnerIndex
            self.newWinnerIndex = Numeric.argmin(min)
            if self.previousWinnerIndex == self.newWinnerIndex:
                self.winnerCount += 1
            else:
                self.winnerCount = 0
            winner = self.models[self.newWinnerIndex]
            winner.counter += 1
            #if winner.maxSize != -1:
            winner.addItem(self.buffer[0])
            self.winner = winner.vector

    def distanceMap(self):
        """
        Calculate distance map.
        """
        map = []
        for x, y in [(x.vector, y.vector) for x in self.models
                     for y in self.models]:
            map.append(euclideanDistance(x, y, self.mask))
        return map

    def __str__(self):
        """
        To display ravq just call print <instance>.
        """
        s = ""
        s += "Settings:\n"
        s += "Delta: " + str(self.delta) + " Epsilon: " + str(
            self.epsilon) + " Buffer Size: " + str(len(self.buffer)) + "\n"
        s += "Time: " + str(self.time) + "\n"
        if self.verbosity > 0:
            s += "Moving average distance: " + "%4.4f " % self.movingAverageDistance + "\n"
            s += "Model vectors distance: " + "%4.4f " % self.modelVectorsDistance + "\n"
            s += "Moving average:\n"
            s += "   " + stringArray(self.movingAverage)
            s += "Last winning model vector:\n"
            s += "   " + stringArray(self.winner)
            s += self.bufferString()
        s += self.modelString()
        if self.printDistance:
            s += "Distance map:\n"
            s += self.distanceMapAsString()
        if self.verbosity > 0:
            s += str(self.models)
        return s

    def distanceMapAsString(self):
        return stringArray(self.distanceMap(),
                           1,
                           len(self.models),
                           format="%4.2f ")

    def saveModelsToFile(self, filename):
        fp = open(filename, 'w')
        pick = pickle.Pickler(fp)
        pick.dump(self.models)
        fp.close()

    def loadModelsFromFile(self, filename):
        fp = open(filename, 'r')
        unpick = pickle.Unpickler(fp)
        self.models = unpick.load()
        fp.close()

    # helpful string methods, see __str__ method for use.
    def modelString(self):
        s = ""
        cnt = 0
        totalCount = 0
        totalIncompatibility = 0.0
        if self.mask == None:
            self.mask = Numeric.ones(len(self.models[0].vector), 'd')
        for m in self.models:
            s += ("%4d Model: " % cnt) + stringArray(m.vector)
            sum = 0.0
            for b in m.contents:
                sum += euclideanDistance(m.vector, b, self.mask)
            totalIncompatibility += sum
            s += "     Count: %d Buffer size: %d Incompatibility: %f\n" % (
                m.counter, len(m.contents), sum)
            totalCount += m.counter
            cnt += 1
        return ("%d Model vectors:\n" % cnt) + s + "Total model vectors  : %d\nTotal mapped vectors : %d\nTotal incompatibility: %f\n" % \
               (cnt, totalCount, totalIncompatibility)

    def bufferString(self):
        s = "Buffer:\n"
        for array in self.buffer:
            s += stringArray(array)
        return s
Exemplo n.º 13
0
        print('pick up: {}'.format(pick_up))
    destination = get_destination(cup_list, pick_up, min_cup, max_cup)
    if debug:
        print('destination: {}'.format(destination))
    cup_list.insert_at(destination, pick_up)
    cup_list.rotate()

    if debug:
        print()
    return cup_list

puzzle_input = sys.stdin.read().strip()

print(puzzle_input)

cups = CircularList()
for x in puzzle_input:
    cups.add(int(x))
min_cup = min(cups)
max_cup = max(cups)

move_number = 1
while move_number <= 100:
    cups = move(move_number, cups, min_cup, max_cup, False)
    move_number += 1

print('-- final --')
print('cups: {}'.format(cups))

# For final answer, rotate head until 1 is first.
while cups._head.data != 1: