class Node:
    def __init__(self, initdata):
        self._data = initdata
        self._next = None

    def getData(self):
        return self._data

    def getNext(self):
        return self._next

    def setData(self, newdata):
        self._data = newdata

    def setNext(self, newnext):
        self._next = newnext

class UnorderedList:
    def __init__(self):
        self._head = None
        self._tail = None
        self._len = 0

    def __str__(self):
        output = "[ "
        ptr = self._head
        while ptr != None:
            output += str(ptr.getData()) + " "
            ptr = ptr.getNext()
        return output + "]"

    def __iter__(self):
        cursor = self._head
        while True:
            if cursor is None:
                raise StopIteration
            yield cursor.getData()
            cursor = cursor.getNext()

    def __getitem__(self, index):
        if index < 0 or index >= self._len:
            print ("Index out of range.")
            return None
        cursor = self._head
        for i in range(index):
            cursor = cursor.getNext()
        return cursor.getData()

    def isEmpty(self):
        return self._len == 0

    def add(self, item):
        temp = Node(item)
        temp.setNext(self._head)
        self._head = temp
        if self._tail == None:
            self._tail = self._head
        self._len += 1

    def addToEnd(self, item):
        temp = Node(item)
        if self._head == None:
            self._head = temp
            self._tail = temp
        else:
            self._tail.setNext(temp)
            self._tail = temp
        self._len += 1

    def removeFirst(self):
        if not self.isEmpty():
            temp = self._head
            self._head = self._head.getNext()
            return temp.getData()
            self._len -+ 1

    def search(self, item):
        current = self._head
        while current != None:
            if current.getData() == item:
                return True
            else:
                current = current.getNext()
        return False

    def listElements(self):
        lst = []
        ptr = self._head
        while ptr != None:
            lst.append(ptr.getData())
            ptr = ptr.getNext()
        return lst

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

# Radix Sort

def binsToList (listOfLists):
    lst = []
    for bin in listOfLists:
        lst = lst + bin.listElements()
    return lst

def printList(lst, lineCount):
    print ("[ ", end = " ")
    count = -1
    for item in lst:
        count += 1
        if count < lineCount:
            print ('%3d' % item, end = " ")
        else:
            print ("\n  ", item, end = " ")
            count = 0
    print (" ]")

def printRadixBins(listOfLists):
    count = 0
    for bin in listOfLists:
        print (count, ": ", end = "")
        bin.printList()
        count += 1

def getEmptyBins():
    # This returns a list of 10 empty linked lists
    # I tried [ UnorderedList() ] * 10, but this gives 10 pointers to the same list.
    return [ UnorderedList(), UnorderedList(), UnorderedList(), UnorderedList(), UnorderedList(),
             UnorderedList(), UnorderedList(), UnorderedList(), UnorderedList(), UnorderedList() ]

def computeBin(num, rnd):
    if rnd == 0:
        return num % 10
    else:
        if num < (10 ** rnd):
            return 0
        else:
            # We need integer division here because the 
            # result will be an index into a list.
            shiftnum = num // (10 ** rnd)
            return shiftnum % 10

def radixSort(input, maxLen):
    inputList = input
    for rnd in range(maxLen):
        bins = getEmptyBins()
        for num in inputList:
            index = computeBin(num, rnd)
            bins[index].addToEnd(num)
        # I just put this in for testing.
        print ("\n\nAfter round " + str(rnd) + ":")
        printRadixBins(bins)
        inputList = binsToList(bins)
    return inputList


# import random
# input = [ random.randint(0, 999) for i in range(200) ]
# print ("The input list is: ")
# printList(input, 20)
# output = radixSort(input, 3)
# print ("\nThe sorted list is: ")
# printList(output, 20)

