# some sorts
import sys
#sys.setrecursionlimit(1000000)

def select(L):
    """Produce copy of L in sorted order by selection sort

    >>> select([3, 1, 2])
    [1, 2, 3]
    """
    L1 = L[:]
    for i in range(len(L1)):
        m = min(L1[i:])
        j = L1.index(m,i)
        L1[i], L1[j] = L1[j], L1[i]
    return L1


def quick(L):
    if len(L) > 1:
        pivot = L[0] # there are much better ways of choosing the pivot!
        smaller_than_pivot = [x for x in L[1:] if x < pivot]
        larger_than_pivot = [x for x in L[1:] if x >= pivot]
        return quick(smaller_than_pivot) + [pivot] + quick(larger_than_pivot)
    else:
        return L
    


def merge2(L1:list, L2:list) -> list:
    """return merge of L1 and L2
    NOTE: modifies L1 and L2

    >>> merge2([1, 3, 5], [2, 4, 6])
    [1, 2, 3, 4, 5, 6]
    >>> merge2([1, 2, 3], [0, 4, 5])
    [0, 1, 2, 3, 4, 5]
    >>> merge2([0], [1, 2, 3, 4])
    [0, 1, 2, 3, 4]
    """
    L = []
    L1.reverse()
    L2.reverse()
    while L1 and L2:
        if L1[-1] < L2[-1]:
            L.append(L1.pop())
        else:
            L.append(L2.pop())
    L1.reverse()
    L2.reverse()
    return L + L1 + L2


def merge(L1:list, L2:list) -> list:
    """return merge of L1 and L2
    NOTE: modifies L1 and L2

    >>> merge([1, 3, 5], [2, 4, 6])
    [1, 2, 3, 4, 5, 6]
    >>> merge([1, 2, 3], [0, 4, 5])
    [0, 1, 2, 3, 4, 5]
    >>> merge([0], [1, 2, 3, 4])
    [0, 1, 2, 3, 4]
    """
    decreasing_from_largest = []
    while L1 and L2:
        if L1[-1] > L2[-1]:
            decreasing_from_largest.append(L1.pop())
        else:
            decreasing_from_largest.append(L2.pop())
    decreasing_from_largest.reverse() 
    return L1 + L2 + decreasing_from_largest


def merge_sort(L):
    """Produce copy of L in non-decreasing order

    >>> merge_sort([1, 5, 3, 4, 2])
    [1, 2, 3, 4, 5]
    """
    if len(L) < 2 :
        return L
    else :
        left_sublist = L[:len(L)//2]
        right_sublist = L[len(L)//2:]
        return merge2(merge_sort(left_sublist), 
                      merge_sort(right_sublist))
    



def count_sort(L):
    """Works for any list of integers, but will be very slow if the integers are a lot larger than len(L).
    See radix sort or bucket sort for something serious"""
    count_list = [0, ] * (max(L) + 1)
    for i in L:
        count_list[i] += 1

    L1 = [] # blank slate!
    for i in range(len(count_list)):
        L1.extend([i]*count_list[i])    
    return L1



if __name__== '__main__':
    import doctest
    doctest.testmod()
    from time import time, clock    

    if not ('L' in dir()):
        from random import shuffle, Random
        r = Random("my seed")    
        maxint = 1000       
        L = [r.randint(0,maxint-1) for i in range(maxint)]
        #L = [r.random() for i in range(maxint)]
        #L = list(range(maxint))    
        #shuffle(L)
        print("\nRandom list generated")        
    else:
        print("\nRandom list L from environment REUSED")

    print("\nFOR {} ITEMS:".format(maxint))

    sys.setrecursionlimit(maxint + 100)    
    start = time()
    select(L)
    print("{} - select sort".format(time() - start)) 

    sys.setrecursionlimit(maxint + 100)
    start = time()
    Lsorted = quick(L)
    print("{} - quick sort".format(time() - start)), 
    
    start = time()
    quick(Lsorted)
    print("{} - quick sort on already-sorted list".format(\
        time() - start))
    
    start = time()
    merge_sort(L)
    print("{} - merge sort".format(time() - start))
    
    L2 = L[:] # let built-in sort in place...
    start = time()
    L2.sort()
    print("{} - built-in sort".format(time() - start)) 
    
    
    if isinstance(L[0],int):
        start = time()
        count_sort(L)
        print("{} - count_sort".format(time() - start))
        
        pass
    
    
    
    
