sort.pony

primitive Sort[A: Seq[B] ref, B: Comparable[B] #read]
  """
  Implementation of dual-pivot quicksort.  It operates in-place on the provided Seq, using 
  a small amount of additional memory. The nature of the element-realation is expressed via 
  the supplied comparator.

  (The following is paraphrased from [Wikipedia](https://en.wikipedia.org/wiki/Quicksort).)

  Quicksort is a common implementation of a sort algorithm which can sort items of any type 
  for which a "less-than" relation (formally, a total order) is defined. 

  On average, the algorithm takes O(n log n) comparisons to sort n items. In the worst case, 
  it makes O(n2) comparisons, though this behavior is rare.  Multi-pivot implementations 
  (of which dual-pivot is one) make efficient use of modern processor caches.

  ## Example program
  The following takes an reverse-alphabetical array of Strings ("third", "second", "first"), 
  and sorts it in place alphabetically using the default String Comparator.

  It outputs:

    first
    second
    third

  ```pony
     use "collections"

     actor Main 
       new create(env:Env) => 
         let array = [ "third"; "second"; "first" ]
         let sorted_array = Sort[Array[String], String](array)
         for e in sorted_array.values() do
           env.out.print(e) // prints "first \n second \n third"
         end
    ```
  """
  fun apply(a: A): A^ =>
    """
    Sort the given seq.
    """
    try _sort(a, 0, a.size().isize() - 1)? end
    a

  fun _sort(a: A, lo: ISize, hi: ISize) ? =>
    if hi <= lo then return end
    // choose outermost elements as pivots
    if a(lo.usize())? > a(hi.usize())? then _swap(a, lo, hi)? end
    (var p, var q) = (a(lo.usize())?, a(hi.usize())?)
    // partition according to invariant
    (var l, var g) = (lo + 1, hi - 1)
    var k = l
    while k <= g do
      if a(k.usize())? < p then
        _swap(a, k, l)?
        l = l + 1
      elseif a(k.usize())? >= q then
        while (a(g.usize())? > q) and (k < g) do g = g - 1 end
        _swap(a, k, g)?
        g = g - 1
        if a(k.usize())? < p then
          _swap(a, k, l)?
          l = l + 1
        end
      end
      k = k + 1
    end
    (l, g) = (l - 1, g + 1)
    // swap pivots to final positions
    _swap(a, lo, l)?
    _swap(a, hi, g)?
    // recursively sort 3 partitions
    _sort(a, lo, l - 1)?
    _sort(a, l + 1, g - 1)?
    _sort(a, g + 1, hi)?

  fun _swap(a: A, i: ISize, j: ISize) ? =>
    a(j.usize())? = a(i.usize())? = a(j.usize())?