# L-18 MCS 275 Mon 20 Feb 2017 : qsarray.py
"""
Quicksort on an array of numbers.
"""

from array import array as Array

def partition(arr, first, last):
    """
    Partitions arr[first:last] using as pivot
    the middle item x.  On return is (i, j, x):
    i > j, all items in arr[i:last] are >= x,
    all items in arr[first:j+1] are <= x.
    """
    pivot = arr[(first+last)//2]
    i = first
    j = last-1
    while i <= j:
        while arr[i] < pivot:
            i = i+1
        while arr[j] > pivot:
            j = j-1
        if i < j:
            (arr[i], arr[j]) = (arr[j], arr[i])
        if i <= j:
            (i, j) = (i+1, j-1)
    return (i, j, pivot)

def check_partition(arr, first, last, i, j, pivot):
    """
    Prints the result of the partition
    for a visible check on the postconditions.
    """
    print('i = %d, j = %d, x = %d' % (i, j, pivot))
    print('arr[%d:%d] =' % (first, j+1), arr[first:j+1], '<=', pivot)
    print('arr[%d:%d] =' % (i, last), arr[i:last], '>=', pivot)

def recursive_quicksort(data, first, last, verbose=True):
    """
    Sorts the array data in increasing order.
    If verbose, then extra output is written.
    """
    (i, j, pivot) = partition(data, first, last)
    if verbose:
        check_partition(data, first, last, i, j, pivot)
    if j > first:
        recursive_quicksort(data, first, j+1, verbose)
    if i < last-1:
        recursive_quicksort(data, i, last, verbose)

def iterative_quicksort(nbrs, verbose=True):
    """
    The iterative version of quicksort
    uses a stack of indices in nbrs.
    """
    stk = []
    stk.insert(0, (0, len(nbrs)))
    while stk != []:
        if verbose:
            print('S =', stk)
        (first, last) = stk.pop(0)
        (i, j, pivot) = partition(nbrs, first, last)
        if verbose:
            check_partition(nbrs, first, last, i, j, pivot)
        if i < last-1:
            stk.insert(0, (i, last))
        if j > first:
            stk.insert(0, (first, j+1))

def main():
    """
    Generates a random array of integers
    and applies quicksort.
    """
    low = int(input('Give lower bound : '))
    upp = int(input('Give upper bound : '))
    nbr = int(input('How many numbers ? '))
    ans = input('Extra output ? (y/n) ')
    from random import randint
    nums = [randint(low, upp) for _ in range(nbr)]
    data = Array('i', nums)
    from copy import deepcopy
    numscopy = deepcopy(nums)
    print('A =', data)
    recursive_quicksort(data, 0, nbr, ans == 'y')
    print('A =', data)
    print('Iteratively on same data')
    data = Array('i', numscopy)
    print('A =', data)
    iterative_quicksort(data, ans == 'y')
    print('A =', data)

if __name__ == "__main__":
    main()
