/*
* qsort.oad - quicksort implementation. Two versions - one with a comparison
* function, the other relying on built-in comparison operators
*
* Copyright (c) 2023 Ross Cunniff
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
proc qsort;
proc main()
{
var i, sorted;
const cmp = proc(a,b) { return (a==b) ? 0 : (a < b) ? -1 : 1; };
sorted = qsort(cmp, {"3", "4", "2", "3", "1"});
"sorted = {";
for (i = 0; i < sorted.length(); i++) {
if (i > 0) ",";
" \"", sorted[i], "\"";
}
" }\n";
sorted = qsort({3, 4, 2, 3, 1});
"sorted = {";
for (i = 0; i < sorted.length(); i++) {
if (i > 0) ",";
" ", sorted[i];
}
" }\n";
}
proc qsort()
{
const quicksort_cmp = proc(cmp, a, lo, hi)
{
const partition = proc(cmp, a, lo, hi) {
// Select the median of 3 values using the provided cmp proc
const med3 = proc(cmp,a,b,c) {
if (cmp(a,b) < 0) { // ? a ? b ?
if (cmp(b,c) < 0) return b; // a b c
else if (cmp(a,c) < 0) return c; // a c b
else return a; // c a b
}
else { // ? b ? a ?
if (cmp(b,c) > 0) return b; // c b a
else if (cmp(a,c) > 0) return c; // b c a
else return a; // b a c
}
};
var i, j;
// The pivot is the median of the left, middle, and right values
var pivot = med3(cmp, a[lo], a[(lo+hi)/2], a[hi]);
// Walk the array from the outside in, swapping
// items less than the pivot with those greater than
// the pivot. Original Hoare algorithm.
i = lo - 1;
j = hi + 1;
while (1) {
do i++; while (cmp(a[i], pivot) < 0);
do j--; while (cmp(a[j], pivot) > 0);
if (i >= j) return j;
var tmp = a[i]; a[i] = a[j]; a[j] = tmp;
}
};
// Idiom for recursive nested procedures. A nested procedure
// does not have access to its name in the enclosing procedure's
// symbol table, so assign the (proc) value to an appropriately
// named variable
var quicksort_cmp = (proc);
if (lo < hi) {
var p = partition(cmp, a, lo, hi);
quicksort_cmp(cmp, a, lo, p);
quicksort_cmp(cmp, a, p+1, hi);
}
};
const quicksort = proc(a, lo, hi)
{
const partition = proc(a, lo, hi) {
// Select the median of 3 values using comparison operators
const med3 = proc(a,b,c) {
if (a < b) { // ? a ? b ?
if (b < c) return b; // a b c
else if (a < c) return c; // a c b
else return a; // c a b
}
else { // ? b ? a ?
if (b > c) return b; // c b a
else if (a > c) return c; // b c a
else return a; // b a c
}
};
var i, j;
// The pivot is the median of the left, middle, and right values
var pivot = med3(a[lo], a[(lo+hi)/2], a[hi]);
// Walk the array from the outside in, swapping
// items less than the pivot with those greater than
// the pivot. Original Hoare algorithm.
i = lo - 1;
j = hi + 1;
while (1) {
do i++; while (a[i] < pivot);
do j--; while (a[j] > pivot);
if (i >= j) return j;
var tmp = a[i]; a[i] = a[j]; a[j] = tmp;
}
};
// Idiom for recursive nested procedures. A nested procedure
// does not have access to its name in the enclosing procedure's
// symbol table, so assign the (proc) value to an appropriately
// named variable
var quicksort = (proc);
if (lo < hi) {
var p = partition(a, lo, hi);
quicksort(a, lo, p);
quicksort(a, p+1, hi);
}
};
// Create a copy of an array without creating copies of its
// contents (copy the contents by reference)
const arrcopy = proc(arr)
{
var n = arr.length();
var res = new Array(n);
for (var i = 0; i < n; i++) {
res[i] = arr[i];
}
return res.pack();
};
var cmp, arr;
using namespace oadl;
switch (nargs()) {
case 1 :
arr = arrcopy(arg(0));
quicksort(arr, 0, arr.length()-1);
case 2 :
cmp = arg(0);
arr = arrcopy(arg(1));
quicksort_cmp(cmp, arr, 0, arr.length()-1);
default :
throw ArgCheck;
}
return arr;
}