#!/usr/bin/env python3
"""Parallel quicksort demo for Parallel Python.

Website: https://www.parallelpython.com
License: Apache-2.0 (see LICENSE and NOTICE)

Shows recursive fan-out: as the recursion descends (depth controlled by
*n*), the remaining chunks are submitted to the job server to be sorted
inline on a worker.  Main collects the mixed list of ints and pending
jobs at the end.

Usage: python quicksort.py [ncpus]
    [ncpus] - the number of workers to run in parallel,
    if omitted it will be set to the number of processors in the system
"""

import random
import sys

import pp


def quicksort(a, n=-1, srv=None):
    """Sort *a*, fanning out to *srv* until depth *n* is exhausted."""
    if len(a) <= 1:
        return a
    if n:
        return quicksort([x for x in a if x < a[0]], n - 1, srv) \
            + [a[0]] \
            + quicksort([x for x in a[1:] if x >= a[0]], n - 1, srv)
    else:
        # Submit this chunk to be sorted inline on a worker.  The job runs
        # quicksort(a) with the defaults (n=-1 -> pure inline recursion).
        return [srv.submit(quicksort, (a,))]


def main():
    print("Usage: python quicksort.py [ncpus]")
    print("    [ncpus] - the number of workers to run in parallel,")
    print("    if omitted it will be set to the number of processors in the system")

    # tuple of all parallel python servers to connect with
    # ppservers = ("*",)          # auto-discover
    # ppservers = ("10.0.0.1",)  # static IP
    ppservers = ()

    if len(sys.argv) > 1:
        ncpus = int(sys.argv[1])
        # Creates jobserver with ncpus workers
        job_server = pp.Server(ncpus, ppservers=ppservers)
    else:
        # Creates jobserver with automatically detected number of workers
        job_server = pp.Server(ppservers=ppservers)

    print("Starting pp with", job_server.get_ncpus(), "workers")

    n = 100000
    input_list = [random.randint(0, 100000) for _ in range(n)]

    # set n to a positive integer to create 2**n PP jobs,
    # or to -1 to avoid using PP.

    # 32 PP jobs
    depth = 5

    # no PP
    # depth = -1

    outputraw = quicksort(input_list, depth, job_server)

    output = []
    for x in outputraw:
        if callable(x):
            output.extend(x())
        else:
            output.append(x)

    print("first 30 numbers in increasing order:", output[:30])

    job_server.print_stats()
    job_server.destroy()


if __name__ == "__main__":
    main()
