Sorting algorithms/Patience sort: Difference between revisions
Content deleted Content added
Line 1,814: | Line 1,814: | ||
{{out}} |
{{out}} |
||
<pre>$ patscc -O3 -DATS_MEMALLOC_LIBC patience_sort_task.dats && ./a.out |
<pre>$ patscc -O3 -DATS_MEMALLOC_LIBC patience_sort_task.dats && ./a.out |
||
unsorted 22 15 98 82 22 4 58 70 80 38 49 48 46 54 93 8 54 2 72 84 86 76 53 37 90 |
|||
sorted 2 4 8 15 22 22 37 38 46 48 49 53 54 54 58 70 72 76 80 82 84 86 90 93 98</pre> |
|||
===A patience sort for arrays of non-linear elements (second version)=== |
|||
{{trans|Fortran}} |
|||
This version of the sort (which I derived from the first) has a more primitive "core" implementation, and a wrapper around that. The "core" requires that the user pass workspace to it (much as Fortran 77 procedures often do). The wrapper uses stack storage for the workspaces, if the sorted subarray is small; otherwise it uses malloc. One may be interested in contrasting the branch that uses stack storage with the branch that uses malloc. |
|||
<lang ats>(* A version of the patience sort that uses arrays passed to it as its |
|||
workspace, and returns the results in an array passed to it. |
|||
This way, the arrays could be reused between calls, or easily put |
|||
on the stack if they are not too large, yet still allocated if they |
|||
are larger than that. |
|||
Notice that the work arrays both start *and finish* as |
|||
uninitialized storage. *) |
|||
(*------------------------------------------------------------------*) |
|||
#include "share/atspre_staload.hats" |
|||
(* ================================================================ *) |
|||
(* Interface declarations that really should be moved to a .sats *) |
|||
(* file. *) |
|||
stadef patience_sort_index (ifirst : int, len : int, i : int) = |
|||
len == 0 || (ifirst <= i && i < ifirst + len) |
|||
typedef patience_sort_index (ifirst : int, len : int, i : int) = |
|||
[patience_sort_index (ifirst, len, i)] size_t i |
|||
typedef patience_sort_index (ifirst : int, len : int) = |
|||
[i : int] patience_sort_index (ifirst, len, i) |
|||
stadef patience_sort_link (ifirst : int, len : int, i : int) = |
|||
0 <= i && i <= len |
|||
typedef patience_sort_link (ifirst : int, len : int, i : int) = |
|||
[patience_sort_link (ifirst, len, i)] size_t i |
|||
typedef patience_sort_link (ifirst : int, len : int) = |
|||
[i : int] patience_sort_link (ifirst, len, i) |
|||
(* patience_sort$lt : the order predicate for patience sort. *) |
|||
extern fn {a : t@ype} |
|||
patience_sort$lt (x : a, y : a) :<> bool |
|||
local |
|||
typedef index_t (ifirst : int, len : int) = |
|||
patience_sort_index (ifirst, len) |
|||
typedef link_t (ifirst : int, len : int) = |
|||
patience_sort_link (ifirst, len) |
|||
in |
|||
extern fn {a : t@ype} |
|||
patience_sort_given_workspaces |
|||
{ifirst, len : int | 0 <= ifirst} |
|||
{n : int | ifirst + len <= n} |
|||
{power : int | len <= power} |
|||
{n_piles : int | len <= n_piles} |
|||
{n_links : int | len <= n_links} |
|||
{n_winv : int | 2 * power <= n_winv} |
|||
{n_winl : int | 2 * power <= n_winl} |
|||
(pf_exp2 : [exponent : nat] EXP2 (exponent, power) | |
|||
arr : &RD(array (a, n)), |
|||
ifirst : size_t ifirst, |
|||
len : size_t len, |
|||
power : size_t power, |
|||
piles : &array (link_t (ifirst, len)?, n_piles) >> _, |
|||
links : &array (link_t (ifirst, len)?, n_links) >> _, |
|||
winvals : &array (link_t (ifirst, len)?, n_winv) >> _, |
|||
winlinks : &array (link_t (ifirst, len)?, n_winl) >> _, |
|||
sorted : &array (index_t (ifirst, len)?, len) |
|||
>> array (index_t (ifirst, len), len)) |
|||
:<!wrt> void |
|||
extern fn {a : t@ype} |
|||
patience_sort_with_its_own_workspaces |
|||
{ifirst, len : int | 0 <= ifirst} |
|||
{n : int | ifirst + len <= n} |
|||
(arr : &RD(array (a, n)), |
|||
ifirst : size_t ifirst, |
|||
len : size_t len, |
|||
sorted : &array (index_t (ifirst, len)?, len) |
|||
>> array (index_t (ifirst, len), len)) |
|||
:<!wrt> void |
|||
end |
|||
overload patience_sort with patience_sort_given_workspaces |
|||
overload patience_sort with patience_sort_with_its_own_workspaces |
|||
extern fn {tk : tkind} |
|||
next_power_of_two |
|||
{i : pos} |
|||
(i : g1uint (tk, i)) |
|||
:<> [k : int | i <= k; k < 2 * i] |
|||
[n : nat] |
|||
@(EXP2 (n, k) | g1uint (tk, k)) |
|||
(* ================================================================ *) |
|||
(* What follows is implementation and belongs in .dats files. *) |
|||
(*------------------------------------------------------------------*) |
|||
(* |
|||
In the following implementation of next_power_of_two: |
|||
* I implement it as a template for all types of kind g1uint. This |
|||
includes dependent forms of uint, usint, ulint, ullint, size_t, |
|||
and yet more types in the prelude; also whatever others one may |
|||
create. |
|||
* I prove the result is not less than the input. |
|||
* I prove the result is less than twice the input. |
|||
* I prove the result is a power of two. This last proof is |
|||
provided in the form of an EXP2 prop. |
|||
* I do NOT return what number two is raised to (though I easily |
|||
could have). I leave that number "existentially defined". In |
|||
other words, I prove only that some such non-negative number |
|||
exists. |
|||
*) |
|||
implement {tk} |
|||
next_power_of_two {i} (i) = |
|||
let |
|||
(* This is not the fastest implementation, although it does verify |
|||
its own correctness. *) |
|||
val one : g1uint (tk, 1) = g1u2u 1u |
|||
fun |
|||
loop {j : pos | j < i} .<i + i - j>. |
|||
(pf : [n : nat] EXP2 (n, j) | |
|||
j : g1uint (tk, j)) |
|||
:<> [k : int | i <= k; k < 2 * i] |
|||
[n : nat] |
|||
@(EXP2 (n, k) | g1uint (tk, k)) = |
|||
let |
|||
val j2 = j + j |
|||
in |
|||
if i <= j2 then |
|||
@(EXP2ind pf | j2) |
|||
else |
|||
loop (EXP2ind pf | j2) |
|||
end |
|||
in |
|||
if i = one then |
|||
@(EXP2bas () | one) |
|||
else |
|||
loop (EXP2bas () | one) |
|||
end |
|||
(*------------------------------------------------------------------*) |
|||
extern praxi {a : vt@ype} |
|||
array_uninitize_without_doing_anything |
|||
{n : int} |
|||
(arr : &array (INV(a), n) >> array (a?, n), |
|||
asz : size_t n) |
|||
:<prf> void |
|||
(*------------------------------------------------------------------*) |
|||
stadef index_t (ifirst : int, len : int, i : int) = |
|||
patience_sort_index (ifirst, len, i) |
|||
typedef index_t (ifirst : int, len : int, i : int) = |
|||
patience_sort_index (ifirst, len, i) |
|||
typedef index_t (ifirst : int, len : int) = |
|||
patience_sort_index (ifirst, len) |
|||
stadef link_t (ifirst : int, len : int, i : int) = |
|||
patience_sort_link (ifirst, len, i) |
|||
typedef link_t (ifirst : int, len : int, i : int) = |
|||
patience_sort_link (ifirst, len, i) |
|||
typedef link_t (ifirst : int, len : int) = |
|||
patience_sort_link (ifirst, len) |
|||
fn {a : t@ype} |
|||
find_pile {ifirst, len : int} |
|||
{n : int | ifirst + len <= n} |
|||
{num_piles : nat | num_piles <= len} |
|||
{n_piles : int | len <= n_piles} |
|||
{q : pos | q <= len} |
|||
(ifirst : size_t ifirst, |
|||
arr : &RD(array (a, n)), |
|||
num_piles : size_t num_piles, |
|||
piles : &RD(array (link_t (ifirst, len), n_piles)), |
|||
q : size_t q) |
|||
:<> [i : pos | i <= num_piles + 1] |
|||
size_t i = |
|||
(* |
|||
Bottenbruch search for the leftmost pile whose top is greater than |
|||
or equal to the next value dealt by "deal". |
|||
References: |
|||
* H. Bottenbruch, "Structure and use of ALGOL 60", Journal of |
|||
the ACM, Volume 9, Issue 2, April 1962, pp.161-221. |
|||
https://doi.org/10.1145/321119.321120 |
|||
The general algorithm is described on pages 214 and 215. |
|||
* https://en.wikipedia.org/w/index.php?title=Binary_search_algorithm&oldid=1062988272#Alternative_procedure |
|||
*) |
|||
if num_piles = i2sz 0 then |
|||
i2sz 1 |
|||
else |
|||
let |
|||
macdef lt = patience_sort$lt<a> |
|||
prval () = lemma_g1uint_param ifirst |
|||
prval () = prop_verify {0 <= ifirst} () |
|||
fun |
|||
loop {j, k : nat | j <= k; k < num_piles} |
|||
.<k - j>. |
|||
(arr : &RD(array (a, n)), |
|||
piles : &array (link_t (ifirst, len), n_piles), |
|||
j : size_t j, |
|||
k : size_t k) |
|||
:<> [i : pos | i <= num_piles + 1] |
|||
size_t i = |
|||
if j = k then |
|||
begin |
|||
if succ j <> num_piles then |
|||
succ j |
|||
else |
|||
let |
|||
val piles_j = piles[j] |
|||
val () = $effmask_exn assertloc (piles_j <> g1u2u 0u) |
|||
val x1 = arr[pred q + ifirst] |
|||
and x2 = arr[pred piles_j + ifirst] |
|||
in |
|||
if x2 \lt x1 then |
|||
succ (succ j) |
|||
else |
|||
succ j |
|||
end |
|||
end |
|||
else |
|||
let |
|||
typedef index (i : int) = [0 <= i; i < n] size_t i |
|||
typedef index = [i : int] index i |
|||
stadef i = j + ((k - j) / 2) |
|||
val i : size_t i = j + ((k - j) / g1u2u 2u) |
|||
val piles_j = piles[j] |
|||
val () = $effmask_exn assertloc (piles_j <> g1u2u 0u) |
|||
val x1 = arr[pred q + ifirst] |
|||
and x2 = arr[pred piles_j + ifirst] |
|||
in |
|||
if x2 \lt x1 then |
|||
loop (arr, piles, i + 1, k) |
|||
else |
|||
loop (arr, piles, j, i) |
|||
end |
|||
in |
|||
loop (arr, piles, g1u2u 0u, pred num_piles) |
|||
end |
|||
fn {a : t@ype} |
|||
deal {ifirst, len : int} |
|||
{n : int | ifirst + len <= n} |
|||
(ifirst : size_t ifirst, |
|||
len : size_t len, |
|||
arr : &RD(array (a, n)), |
|||
piles : &array (link_t (ifirst, len)?, len) |
|||
>> array (link_t (ifirst, len), len), |
|||
links : &array (link_t (ifirst, len)?, len) |
|||
>> array (link_t (ifirst, len), len)) |
|||
:<!wrt> [num_piles : int | num_piles <= len] |
|||
size_t num_piles = |
|||
let |
|||
prval () = lemma_g1uint_param ifirst |
|||
prval () = lemma_g1uint_param len |
|||
typedef link_t (i : int) = link_t (ifirst, len, i) |
|||
typedef link_t = link_t (ifirst, len) |
|||
val zero : size_t 0 = g1u2u 0u |
|||
val one : size_t 1 = g1u2u 1u |
|||
val link_nil : link_t 0 = g1u2u 0u |
|||
fun |
|||
loop {q : pos | q <= len + 1} |
|||
{m : nat | m <= len} |
|||
.<len + 1 - q>. |
|||
(arr : &RD(array (a, n)), |
|||
q : size_t q, |
|||
piles : &array (link_t, len) >> _, |
|||
links : &array (link_t, len) >> _, |
|||
m : size_t m) |
|||
:<!wrt> [num_piles : nat | num_piles <= len] |
|||
size_t num_piles = |
|||
if q = succ (len) then |
|||
m |
|||
else |
|||
let |
|||
val i = find_pile {ifirst, len} (ifirst, arr, m, piles, q) |
|||
(* We have no proof the number of elements will not exceed |
|||
storage. However, we know it will not, because the number |
|||
of piles cannot exceed the size of the input. Let us get |
|||
a "proof" by runtime check. *) |
|||
val () = $effmask_exn assertloc (i <= len) |
|||
in |
|||
links[pred q] := piles[pred i]; |
|||
piles[pred i] := q; |
|||
if i = succ m then |
|||
loop {q + 1} (arr, succ q, piles, links, succ m) |
|||
else |
|||
loop {q + 1} (arr, succ q, piles, links, m) |
|||
end |
|||
in |
|||
array_initize_elt<link_t> (piles, len, link_nil); |
|||
array_initize_elt<link_t> (links, len, link_nil); |
|||
loop (arr, one, piles, links, zero) |
|||
end |
|||
fn {a : t@ype} |
|||
k_way_merge {ifirst, len : int} |
|||
{n : int | ifirst + len <= n} |
|||
{num_piles : pos | num_piles <= len} |
|||
{power : int | len <= power} |
|||
(pf_exp2 : [exponent : nat] EXP2 (exponent, power) | |
|||
arr : &RD(array (a, n)), |
|||
ifirst : size_t ifirst, |
|||
len : size_t len, |
|||
num_piles : size_t num_piles, |
|||
power : size_t power, |
|||
piles : &array (link_t (ifirst, len), len) >> _, |
|||
links : &RD(array (link_t (ifirst, len), len)), |
|||
winvals : &array (link_t (ifirst, len)?, 2 * power) |
|||
>> _, |
|||
winlinks : &array (link_t (ifirst, len)?, 2 * power) |
|||
>> _, |
|||
sorted : &array (index_t (ifirst, len)?, len) |
|||
>> array (index_t (ifirst, len), len)) |
|||
:<!wrt> void = |
|||
(* |
|||
k-way merge by tournament tree. |
|||
See Knuth, volume 3, and also |
|||
https://en.wikipedia.org/w/index.php?title=K-way_merge_algorithm&oldid=1047851465#Tournament_Tree |
|||
However, I store a winners tree instead of the recommended losers |
|||
tree. If the tree were stored as linked nodes, it would probably |
|||
be more efficient to store a losers tree. However, I am storing |
|||
the tree as an array, and one can find an opponent quickly by |
|||
simply toggling the least significant bit of a competitor's array |
|||
index. |
|||
*) |
|||
let |
|||
prval () = lemma_g1uint_param ifirst |
|||
prval () = lemma_g1uint_param len |
|||
typedef link_t (i : int) = link_t (ifirst, len, i) |
|||
typedef link_t = link_t (ifirst, len) |
|||
val link_nil : link_t 0 = g1u2u 0u |
|||
typedef index_t (i : int) = index_t (ifirst, len, i) |
|||
typedef index_t = index_t (ifirst, len) |
|||
val [total_external_nodes : int] |
|||
@(_ | total_external_nodes) = next_power_of_two num_piles |
|||
prval () = prop_verify {num_piles <= total_external_nodes} () |
|||
stadef total_nodes = (2 * total_external_nodes) - 1 |
|||
val total_nodes : size_t total_nodes = |
|||
pred (g1u2u 2u * total_external_nodes) |
|||
(* We will ignore index 0 of the winners tree arrays. *) |
|||
stadef winners_size = total_nodes + 1 |
|||
val winners_size : size_t winners_size = succ total_nodes |
|||
(* An exercise for the reader is to write a proof that |
|||
winners_size <= 2 * power, so one can get rid of the |
|||
runtime assertion here: *) |
|||
val () = $effmask_exn assertloc (winners_size <= 2 * power) |
|||
prval @(winvals_left, winvals_right) = |
|||
array_v_split {link_t?} {..} {2 * power} {winners_size} |
|||
(view@ winvals) |
|||
prval () = view@ winvals := winvals_left |
|||
prval @(winlinks_left, winlinks_right) = |
|||
array_v_split {link_t?} {..} {2 * power} {winners_size} |
|||
(view@ winlinks) |
|||
prval () = view@ winlinks := winlinks_left |
|||
val () = array_initize_elt<link_t> (winvals, winners_size, |
|||
link_nil) |
|||
val () = array_initize_elt<link_t> (winlinks, winners_size, |
|||
link_nil) |
|||
(* - - - - - - - - - - - - - - - - - - - - - - - - - - *) |
|||
(* Record which pile a winner will have come from. *) |
|||
fun |
|||
init_pile_links |
|||
{i : nat | i <= num_piles} |
|||
.<num_piles - i>. |
|||
(winlinks : &array (link_t, winners_size), |
|||
i : size_t i) |
|||
:<!wrt> void = |
|||
if i <> num_piles then |
|||
begin |
|||
winlinks[total_external_nodes + i] := succ i; |
|||
init_pile_links (winlinks, succ i) |
|||
end |
|||
val () = init_pile_links (winlinks, g1u2u 0u) |
|||
(* - - - - - - - - - - - - - - - - - - - - - - - - - - *) |
|||
(* The top of each pile becomes a starting competitor. *) |
|||
fun |
|||
init_competitors |
|||
{i : nat | i <= num_piles} |
|||
.<num_piles - i>. |
|||
(winvals : &array (link_t, winners_size), |
|||
piles : &array (link_t, len), |
|||
i : size_t i) |
|||
:<!wrt> void = |
|||
if i <> num_piles then |
|||
begin |
|||
winvals[total_external_nodes + i] := piles[i]; |
|||
init_competitors (winvals, piles, succ i) |
|||
end |
|||
val () = init_competitors (winvals, piles, g1u2u 0u) |
|||
(* - - - - - - - - - - - - - - - - - - - - - - - - - - *) |
|||
(* Discard the top of each pile. *) |
|||
fun |
|||
discard_tops {i : nat | i <= num_piles} |
|||
.<num_piles - i>. |
|||
(piles : &array (link_t, len), |
|||
links : &array (link_t, len), |
|||
i : size_t i) |
|||
:<!wrt> void = |
|||
if i <> num_piles then |
|||
let |
|||
val link = piles[i] |
|||
(* None of the piles should have been empty. *) |
|||
val () = $effmask_exn assertloc (link <> g1u2u 0u) |
|||
in |
|||
piles[i] := links[pred link]; |
|||
discard_tops (piles, links, succ i) |
|||
end |
|||
val () = discard_tops (piles, links, g1u2u 0u) |
|||
(* - - - - - - - - - - - - - - - - - - - - - - - - - - *) |
|||
(* How to play a game. *) |
|||
fn |
|||
play_game {i : int | 2 <= i; i <= total_nodes} |
|||
(arr : &RD(array (a, n)), |
|||
winvals : &array (link_t, winners_size), |
|||
i : size_t i) |
|||
:<> [iwinner : pos | iwinner <= total_nodes] |
|||
size_t iwinner = |
|||
let |
|||
macdef lt = patience_sort$lt<a> |
|||
fn |
|||
find_opponent {i : int | 2 <= i; i <= total_nodes} |
|||
(i : size_t i) |
|||
:<> [j : int | 2 <= j; j <= total_nodes] |
|||
size_t j = |
|||
let |
|||
(* The prelude contains bitwise operations only for |
|||
non-dependent unsigned integer. We will not bother to |
|||
add them ourselves, but instead go back and forth |
|||
between dependent and non-dependent. *) |
|||
val i0 = g0ofg1 i |
|||
val j0 = g0uint_lxor<size_kind> (i0, g0u2u 1u) |
|||
val j = g1ofg0 j0 |
|||
(* We have no proof the opponent is in the proper |
|||
range. Create a "proof" by runtime checks. *) |
|||
val () = $effmask_exn assertloc (g1u2u 2u <= j) |
|||
val () = $effmask_exn assertloc (j <= total_nodes) |
|||
in |
|||
j |
|||
end |
|||
val j = find_opponent i |
|||
val winner_i = winvals[i] |
|||
and winner_j = winvals[j] |
|||
in |
|||
if winner_i = link_nil then |
|||
j |
|||
else if winner_j = link_nil then |
|||
i |
|||
else |
|||
let |
|||
val i1 = pred winner_i + ifirst |
|||
and i2 = pred winner_j + ifirst |
|||
prval () = lemma_g1uint_param i1 |
|||
prval () = lemma_g1uint_param i2 |
|||
in |
|||
if arr[i2] \lt arr[i1] then j else i |
|||
end |
|||
end |
|||
(* - - - - - - - - - - - - - - - - - - - - - - - - - - *) |
|||
fun |
|||
build_tree {istart : pos | istart <= total_external_nodes} |
|||
.<istart>. |
|||
(arr : &RD(array (a, n)), |
|||
winvals : &array (link_t, winners_size), |
|||
winlinks : &array (link_t, winners_size), |
|||
istart : size_t istart) |
|||
:<!wrt> void = |
|||
if istart <> 1 then |
|||
let |
|||
fun |
|||
play_initial_games |
|||
{i : int | istart <= i; i <= (2 * istart) + 1} |
|||
.<(2 * istart) + 1 - i>. |
|||
(arr : &RD(array (a, n)), |
|||
winvals : &array (link_t, winners_size), |
|||
winlinks : &array (link_t, winners_size), |
|||
i : size_t i) |
|||
:<!wrt> void = |
|||
if i <= pred (istart + istart) then |
|||
let |
|||
val iwinner = play_game (arr, winvals, i) |
|||
and i2 = i / g1u2u 2u |
|||
in |
|||
winvals[i2] := winvals[iwinner]; |
|||
winlinks[i2] := winlinks[iwinner]; |
|||
play_initial_games (arr, winvals, winlinks, |
|||
succ (succ i)) |
|||
end |
|||
in |
|||
play_initial_games (arr, winvals, winlinks, istart); |
|||
build_tree (arr, winvals, winlinks, istart / g1u2u 2u) |
|||
end |
|||
val () = build_tree (arr, winvals, winlinks, total_external_nodes) |
|||
(* - - - - - - - - - - - - - - - - - - - - - - - - - - *) |
|||
fun |
|||
replay_games {i : pos | i <= total_nodes} |
|||
.<i>. |
|||
(arr : &RD(array (a, n)), |
|||
winvals : &array (link_t, winners_size), |
|||
winlinks : &array (link_t, winners_size), |
|||
i : size_t i) |
|||
:<!wrt> void = |
|||
if i <> g1u2u 1u then |
|||
let |
|||
val iwinner = play_game (arr, winvals, i) |
|||
and i2 = i / g1u2u 2u |
|||
in |
|||
winvals[i2] := winvals[iwinner]; |
|||
winlinks[i2] := winlinks[iwinner]; |
|||
replay_games (arr, winvals, winlinks, i2) |
|||
end |
|||
fun |
|||
merge {isorted : nat | isorted <= len} |
|||
{p_sorted : addr} |
|||
.<len - isorted>. |
|||
(pf_sorted : !array_v (index_t?, p_sorted, |
|||
len - isorted) |
|||
>> array_v (index_t, p_sorted, |
|||
len - isorted) | |
|||
arr : &RD(array (a, n)), |
|||
piles : &array (link_t, len), |
|||
links : &array (link_t, len), |
|||
winvals : &array (link_t, winners_size), |
|||
winlinks : &array (link_t, winners_size), |
|||
p_sorted : ptr p_sorted, |
|||
isorted : size_t isorted) |
|||
:<!wrt> void = |
|||
(* This function not only fills in the "sorted" array, but |
|||
transforms it from "uninitialized" to "initialized". *) |
|||
if isorted <> len then |
|||
let |
|||
prval @(pf_elem, pf_rest) = array_v_uncons pf_sorted |
|||
val winner = winvals[1] |
|||
val () = $effmask_exn assertloc (winner <> link_nil) |
|||
val () = !p_sorted := pred winner + ifirst |
|||
(* Move to the next element in the winner's pile. *) |
|||
val ilink = winlinks[1] |
|||
val () = $effmask_exn assertloc (ilink <> link_nil) |
|||
val inext = piles[pred ilink] |
|||
val () = (if inext <> link_nil then |
|||
piles[pred ilink] := links[pred inext]) |
|||
(* Replay games, with the new element as a competitor. *) |
|||
val i = (total_nodes / g1u2u 2u) + ilink |
|||
val () = $effmask_exn assertloc (i <= total_nodes) |
|||
val () = winvals[i] := inext |
|||
val () = replay_games (arr, winvals, winlinks, i) |
|||
val () = merge (pf_rest | |
|||
arr, piles, links, winvals, winlinks, |
|||
ptr_succ<index_t> p_sorted, succ isorted) |
|||
prval () = pf_sorted := array_v_cons (pf_elem, pf_rest) |
|||
in |
|||
end |
|||
else |
|||
let |
|||
prval () = pf_sorted := |
|||
array_v_unnil_nil{index_t?, index_t} pf_sorted |
|||
in |
|||
end |
|||
val () = merge (view@ sorted | |
|||
arr, piles, links, winvals, winlinks, |
|||
addr@ sorted, i2sz 0) |
|||
prval () = |
|||
array_uninitize_without_doing_anything<link_t> |
|||
(winvals, winners_size) |
|||
prval () = |
|||
array_uninitize_without_doing_anything<link_t> |
|||
(winlinks, winners_size) |
|||
prval () = view@ winvals := |
|||
array_v_unsplit (view@ winvals, winvals_right) |
|||
prval () = view@ winlinks := |
|||
array_v_unsplit (view@ winlinks, winlinks_right) |
|||
in |
|||
end |
|||
implement {a} |
|||
patience_sort_given_workspaces |
|||
{ifirst, len} {n} {power} |
|||
{n_piles} {n_links} {n_winv} {n_winl} |
|||
(pf_exp2 | arr, ifirst, len, power, |
|||
piles, links, winvals, winlinks, |
|||
sorted) = |
|||
let |
|||
prval () = lemma_g1uint_param ifirst |
|||
prval () = lemma_g1uint_param len |
|||
typedef index_t = index_t (ifirst, len) |
|||
typedef link_t = link_t (ifirst, len) |
|||
in |
|||
if len = i2sz 0 then |
|||
let |
|||
prval () = view@ sorted := |
|||
array_v_unnil_nil{index_t?, index_t} (view@ sorted) |
|||
in |
|||
end |
|||
else |
|||
let |
|||
prval @(piles_left, piles_right) = |
|||
array_v_split {link_t?} {..} {n_piles} {len} (view@ piles) |
|||
prval () = view@ piles := piles_left |
|||
prval @(links_left, links_right) = |
|||
array_v_split {link_t?} {..} {n_links} {len} (view@ links) |
|||
prval () = view@ links := links_left |
|||
prval @(winvals_left, winvals_right) = |
|||
array_v_split {link_t?} {..} {n_winv} {2 * power} |
|||
(view@ winvals) |
|||
prval () = view@ winvals := winvals_left |
|||
prval @(winlinks_left, winlinks_right) = |
|||
array_v_split {link_t?} {..} {n_winl} {2 * power} |
|||
(view@ winlinks) |
|||
prval () = view@ winlinks := winlinks_left |
|||
val num_piles = |
|||
deal {ifirst, len} {n} (ifirst, len, arr, piles, links) |
|||
prval () = lemma_g1uint_param num_piles |
|||
val () = $effmask_exn assertloc (num_piles <> i2sz 0) |
|||
val () = |
|||
k_way_merge {ifirst, len} {n} {..} {power} |
|||
(pf_exp2 | arr, ifirst, len, num_piles, power, |
|||
piles, links, winvals, winlinks, |
|||
sorted) |
|||
prval () = |
|||
array_uninitize_without_doing_anything<link_t> |
|||
(piles, len) |
|||
prval () = |
|||
array_uninitize_without_doing_anything<link_t> |
|||
(links, len) |
|||
prval () = view@ piles := |
|||
array_v_unsplit (view@ piles, piles_right) |
|||
prval () = view@ links := |
|||
array_v_unsplit (view@ links, links_right) |
|||
prval () = view@ winvals := |
|||
array_v_unsplit (view@ winvals, winvals_right) |
|||
prval () = view@ winlinks := |
|||
array_v_unsplit (view@ winlinks, winlinks_right) |
|||
in |
|||
end |
|||
end |
|||
(* ================================================================ *) |
|||
(* An interface that provides the workspaces. If the subarray to *) |
|||
(* sort is small enough, stack storage will be used. *) |
|||
#define LEN_THRESHOLD 128 |
|||
#define WINNERS_SIZE 256 |
|||
prval () = prop_verify {WINNERS_SIZE == 2 * LEN_THRESHOLD} () |
|||
local |
|||
prval pf_exp2 = EXP2bas () (* 1*) |
|||
prval pf_exp2 = EXP2ind pf_exp2 (* 2 *) |
|||
prval pf_exp2 = EXP2ind pf_exp2 (* 4 *) |
|||
prval pf_exp2 = EXP2ind pf_exp2 (* 8 *) |
|||
prval pf_exp2 = EXP2ind pf_exp2 (* 16 *) |
|||
prval pf_exp2 = EXP2ind pf_exp2 (* 32 *) |
|||
prval pf_exp2 = EXP2ind pf_exp2 (* 64 *) |
|||
prval pf_exp2 = EXP2ind pf_exp2 (* 128 *) |
|||
in |
|||
prval pf_exp2_for_stack_storage = pf_exp2 |
|||
end |
|||
implement {a} |
|||
patience_sort_with_its_own_workspaces |
|||
{ifirst, len} {n} (arr, ifirst, len, sorted) = |
|||
let |
|||
prval () = lemma_g1uint_param ifirst |
|||
prval () = lemma_g1uint_param len |
|||
typedef link_t = link_t (ifirst, len) |
|||
fn |
|||
sort {ifirst, len : int | 0 <= ifirst} |
|||
{n : int | ifirst + len <= n} |
|||
{power : int | len <= power} |
|||
{n_piles : int | len <= n_piles} |
|||
{n_links : int | len <= n_links} |
|||
{n_winv : int | 2 * power <= n_winv} |
|||
{n_winl : int | 2 * power <= n_winl} |
|||
(pf_exp2 : [exponent : nat] EXP2 (exponent, power) | |
|||
arr : &RD(array (a, n)), |
|||
ifirst : size_t ifirst, |
|||
len : size_t len, |
|||
power : size_t power, |
|||
piles : &array (link_t (ifirst, len)?, n_piles) >> _, |
|||
links : &array (link_t (ifirst, len)?, n_links) >> _, |
|||
winvals : &array (link_t (ifirst, len)?, n_winv) >> _, |
|||
winlinks : &array (link_t (ifirst, len)?, n_winl) >> _, |
|||
sorted : &array (index_t (ifirst, len)?, len) |
|||
>> array (index_t (ifirst, len), len)) |
|||
:<!wrt> void = |
|||
patience_sort_given_workspaces<a> |
|||
{ifirst, len} {n} {power} |
|||
{n_piles} {n_links} {n_winv} {n_winl} |
|||
(pf_exp2 | arr, ifirst, len, power, piles, links, |
|||
winvals, winlinks, sorted) |
|||
in |
|||
if len <= i2sz LEN_THRESHOLD then |
|||
let |
|||
var piles : array (link_t?, LEN_THRESHOLD) |
|||
var links : array (link_t?, LEN_THRESHOLD) |
|||
var winvals : array (link_t?, WINNERS_SIZE) |
|||
var winlinks : array (link_t?, WINNERS_SIZE) |
|||
in |
|||
sort (pf_exp2_for_stack_storage | |
|||
arr, ifirst, len, i2sz LEN_THRESHOLD, |
|||
piles, links, winvals, winlinks, sorted) |
|||
end |
|||
else |
|||
let |
|||
val @(pf_piles, pfgc_piles | p_piles) = |
|||
array_ptr_alloc<link_t> len |
|||
val @(pf_links, pfgc_links | p_links) = |
|||
array_ptr_alloc<link_t> len |
|||
val @(pf_exp2 | power) = next_power_of_two<size_kind> len |
|||
val @(pf_winvals, pfgc_winvals | p_winvals) = |
|||
array_ptr_alloc<link_t> (power + power) |
|||
val @(pf_winlinks, pfgc_winlinks | p_winlinks) = |
|||
array_ptr_alloc<link_t> (power + power) |
|||
macdef piles = !p_piles |
|||
macdef links = !p_links |
|||
macdef winvals = !p_winvals |
|||
macdef winlinks = !p_winlinks |
|||
in |
|||
sort (pf_exp2 | |
|||
arr, ifirst, len, power, piles, links, |
|||
winvals, winlinks, sorted); |
|||
array_ptr_free (pf_piles, pfgc_piles | p_piles); |
|||
array_ptr_free (pf_links, pfgc_links | p_links); |
|||
array_ptr_free (pf_winvals, pfgc_winvals | p_winvals); |
|||
array_ptr_free (pf_winlinks, pfgc_winlinks | p_winlinks) |
|||
end |
|||
end |
|||
(* ================================================================ *) |
|||
(* A demonstration program. *) |
|||
fn {a : t@ype} |
|||
find_length {n : int} |
|||
(lst : list (a, n)) |
|||
:<> [m : int | m == n] size_t m = |
|||
let |
|||
prval () = lemma_list_param lst |
|||
in |
|||
g1i2u (length lst) |
|||
end |
|||
implement |
|||
main0 () = |
|||
let |
|||
implement |
|||
patience_sort$lt<int> (x, y) = |
|||
x < y |
|||
val example_list = |
|||
$list (22, 15, 98, 82, 22, 4, 58, 70, 80, 38, 49, 48, 46, 54, |
|||
93, 8, 54, 2, 72, 84, 86, 76, 53, 37, 90) |
|||
val ifirst = i2sz 10 |
|||
val [len : int] len = find_length example_list |
|||
#define ARRSZ 100 |
|||
val () = assertloc (i2sz 10 + len <= ARRSZ) |
|||
var arr : array (int, ARRSZ) |
|||
val () = array_initize_elt<int> (arr, i2sz ARRSZ, 0) |
|||
prval @(pf_left, pf_right) = |
|||
array_v_split {int} {..} {ARRSZ} {10} (view@ arr) |
|||
prval @(pf_middle, pf_right) = |
|||
array_v_split {int} {..} {90} {len} pf_right |
|||
val p = ptr_add<int> (addr@ arr, 10) |
|||
val () = array_copy_from_list<int> (!p, example_list) |
|||
prval pf_right = array_v_unsplit (pf_middle, pf_right) |
|||
prval () = view@ arr := array_v_unsplit (pf_left, pf_right) |
|||
typedef index_t = patience_sort_index (10, len) |
|||
var sorted : array (index_t, ARRSZ) |
|||
val () = array_initize_elt<index_t> (sorted, i2sz ARRSZ, |
|||
g1u2u 10u) |
|||
prval @(sorted_left, sorted_right) = |
|||
array_v_split {index_t} {..} {ARRSZ} {len} (view@ sorted) |
|||
prval () = view@ sorted := sorted_left |
|||
val () = patience_sort<int> (arr, i2sz 10, len, sorted) |
|||
prval () = view@ sorted := |
|||
array_v_unsplit (view@ sorted, sorted_right) |
|||
var i : [i : nat | i <= len] size_t i |
|||
in |
|||
print! ("unsorted "); |
|||
for (i := i2sz 0; i <> len; i := succ i) |
|||
print! (" ", arr[i2sz 10 + i]); |
|||
println! (); |
|||
print! ("sorted "); |
|||
for (i := i2sz 0; i <> len; i := succ i) |
|||
print! (" ", arr[sorted[i]]); |
|||
println! () |
|||
end |
|||
(*------------------------------------------------------------------*)</lang> |
|||
{{out}} |
|||
<pre>$ patscc -O3 -DATS_MEMALLOC_LIBC patience_sort_task_provided_storage.dats && ./a.out |
|||
unsorted 22 15 98 82 22 4 58 70 80 38 49 48 46 54 93 8 54 2 72 84 86 76 53 37 90 |
unsorted 22 15 98 82 22 4 58 70 80 38 49 48 46 54 93 8 54 2 72 84 86 76 53 37 90 |
||
sorted 2 4 8 15 22 22 37 38 46 48 49 53 54 54 58 70 72 76 80 82 84 86 90 93 98</pre> |
sorted 2 4 8 15 22 22 37 38 46 48 49 53 54 54 58 70 72 76 80 82 84 86 90 93 98</pre> |