#!/usr/bin/perl -w
# 演算法: quick sort
# 作者: 洪朝貴 http://www.cyut.edu.tw/~ckhung
# 授權: public domain (但如果您要將我的程式改寫成有用的大程式,
#	強烈建議將您的版本施以 GPL)
# 執行方式: ./qsort -g -p left
#     其中 -g 為 debug 模式, 用以顯示過程
#          -p 指定 pivot 選取方式, 後面可以是 left, right,
#             mo3 (median of 3), rand (亂數)

use Getopt::Std;
use strict;

my (%opts);
my (%pick_pivot) = ( # function table for picking pivot
    "left" => sub { return $_[1]; },
    "right" => sub { return $_[2]; },
    "rand" => sub { my ($data, $i, $j) = @_; return $i + rand($j+1-$i); },
    "mo3" => \&mo3,
);

sub mo3 {
    my ($data, $i, $j) = @_;
    return $i if ($j-$i <= 1);
    my ($k) = int(($i+$j) / 2);
    return $data->[$i] < $data->[$j] ?
		$data->[$j] < $data->[$k] ? $j :
		    $data->[$i] < $data->[$k] ? $k : $i	:
		$data->[$j] > $data->[$k] ? $j :
		    $data->[$i] < $data->[$k] ? $i : $k;
}

sub dump_array {
    my ($data, $left, $right) = @_;
    print "   " x ($left+1);
    for (my $k=$left; $k<=$right; ++$k) {
	printf "%3d", $data->[$k];
    }
    print "\n";
}

sub partition {
    # always assuming the leftmost element to be the pivot
    my ($data, $left, $right) = @_;
    my ($pivot) = $data->[$left];
    my ($i, $j) = ($left, $right+1);
    dump_array($data, $left, $right) if ($opts{g});	# debug
    while (1) {
	while ($data->[++$i] < $pivot) {}
	while ($data->[--$j] > $pivot) {}
	if ($opts{g}) {     # debug
	    my ($mark) = "   " x ($right - $left + 2);
	    substr($mark, ($i-$left+1)*3, 3) = "  >";
	    substr($mark, ($j-$left+1)*3, 3) = "  <";
	    substr($mark, ($j-$left+1)*3, 3) = "  X"
		if $i == $j;
	    print "   " x $left, "$mark\n";
	}
	return $j if $i >= $j;
	@{$data}[$i,$j] = @{$data}[$j,$i];
    }
}

sub qsort {
    my ($data, $left, $right) = @_;
    return if $right <= $left;
    my ($pi) = & { $pick_pivot{$opts{p}} } ($data, $left, $right);
    if ($opts{g}) {
	dump_array($data, $left, $right);
	print "   " x ($pi+1), "  p\n" 
    }
    @{$data}[$pi, $left] = @{$data}[$left, $pi];
    my ($k) = partition($data, $left, $right);
    @{$data}[$k, $left] = @{$data}[$left, $k];
    qsort($data, $left, $k-1);
    qsort($data, $k+1, $right);
}

sub pusage {
    print STDERR "usage: $0 [-g] [-p pivot]\n",
	"where pivot may be one of: ", join(" ", sort keys %pick_pivot), "\n";
}

getopts('gp:', \%opts);
if (defined $opts{p}) {
    if (not defined $pick_pivot{$opts{p}}) {
	pusage();
	exit;
    }
} else {
    $opts{p} = "left";
}

my (@a);
@a = qw(15 62 16 25 65 18 20  3 14  8 93 84 62 18 81 51 47 80);
#@a = qw(8 6 1 3 5 2 4 7 9 3 5 4 8 3 1 0 6 7 3);
#@a = qw(6 7 3 8 6 1 3 5 2 4 6 7 9);

push @a, 999; # sentinel
print "     pivot: $opts{p}\n\n" if ($opts{g});	# debug
qsort(\@a, 0, $#a-1);
print "   ", "---" x ($#a + 1), "\n";
dump_array(\@a, 0, $#a-1);
print "\n\n";
