1:- module(
    2  sort_ext,
    3  [
    4    order_by2/2,   % +Spec, :Goal_0
    5    predmsort/3,   % :Compare_2, +Original, -Sorted
    6    sort_stream/2, % +In, -Out
    7    sort_stream/3  % +In, -Out, +Options
    8  ]
    9).

Supoort for sorting

*/

   15:- use_module(library(apply)).   16:- use_module(library(error)).   17:- use_module(library(process)).   18
   19:- use_module(library(dict)).   20:- use_module(library(thread_ext)).   21
   22:- meta_predicate
   23    order_by2(+, 0),
   24    predmerge(3, +, +, -),
   25    predmerge(+, 3, +, +, -, -, -),
   26    predmsort(3, +, -),
   27    predmsort(3, ?, +, ?, -).
 order_by2(+Spec:list(compound), :Goal_0) is det
   33order_by2([], Goal_0) :- !,
   34  Goal_0.
   35order_by2(Order, Goal_0) :-
   36  order_by(Order, Goal_0).
 predmsort(:Compare_2, +Original:list, -Sorted:list) is det
See also
- Like predsort/3, but using msort/2.
   44predmsort(P, L, R) :-
   45  '$skip_list'(N, L, Tail),
   46  (Tail == [] -> predmsort(P, N, L, _, R1), R = R1 ; must_be(L, list)).
   47
   48predmsort(P, 2, [X1,X2|L], L, R) :- !,
   49  call(P, Delta, X1, X2),
   50  msort_(Delta, X1, X2, R).
   51predmsort(_, 1, [X|L], L, [X]) :- !.
   52predmsort(_, 0, L, L, []) :- !.
   53predmsort(P, N, L1, L3, R) :-
   54  N1 is N // 2,
   55  plus(N1, N2, N),
   56  predmsort(P, N1, L1, L2, R1),
   57  predmsort(P, N2, L2, L3, R2),
   58  predmerge(P, R1, R2, R).
   59
   60msort_(<, X1, X2, [X1,X2]).
   61msort_(=, X1, X2, [X1,X2]).
   62msort_(>, X1, X2, [X2,X1]).
   63
   64predmerge(_, [], R, R) :- !.
   65predmerge(_, R, [], R) :- !.
   66predmerge(P, [H1|T1], [H2|T2], Result) :-
   67  call(P, Delta, H1, H2), !,
   68  predmerge(Delta, P, H1, H2, T1, T2, Result).
   69
   70predmerge(>, P, H1, H2, T1, T2, [H2|R]) :-
   71  predmerge(P, [H1|T1], T2, R).
   72predmerge(=, P, H1, H2, T1, T2, [H1,H2|R]) :-
   73  predmerge(P, T1, T2, R).
   74predmerge(<, P, H1, H2, T1, T2, [H1|R]) :-
   75  predmerge(P, T1, [H2|T2], R).
 sort_stream(+In:istream, -Out:ostream) is det
 sort_stream(+In:istream, -Out:ostream, +Options:options) is det
Arguments:
Options- The following options are supported:
buffer_size(+nonneg)
Optionally, the size of the buffer in kilobytes.
duplicates(+boolean)
Whether duplicates are allowed in the result. Default is `true'.
numeric(+boolean)
Whether numberic sort is performed. Default is `false'.
output(+atom)
The name of the output file, as processed by `absolute_file_name/[2,3]'. Default is the input file.
temporary_directory(+atom)
The directory that is used for storing intermediary results of sorting. Default is the value of setting `temporary_directory'.
threads(+positive_integer)
The number of threads that is used. Default is the number of available processors, but not larger than 8. Larger numbers have diminishing returns. Using $n$ threads increases the memory use by $\log n$.
utf8(+boolean)
Whether the environment is set to UTF-8 encoding. Default is `false'.
  120sort_stream(In, Out) :-
  121  sort_stream(In, Out, []).
  122
  123
  124sort_stream(In, Out, Options1) :-
  125  dict_select(env, Options1, [], Options2, EnvT),
  126  dict_select(utf8, Options2, false, Options3, Utf8),
  127  (Utf8 == true -> Env = EnvT ; Env = ['LC_ALL'='C'|EnvT]),
  128  maplist(sort_flag, Options3, Flags),
  129  process_create(
  130    path(sort),
  131    Flags,
  132    [env(Env),stdin(pipe(ProcIn)),stdout(pipe(Out))]
  133  ),
  134  create_detached_thread(
  135    call_cleanup(
  136      copy_stream_data(In, ProcIn),
  137      close(ProcIn)
  138    )
  139  ).
  140
  141% --buffer-size
  142sort_flag(buffer_size(Size), Flag) :-
  143  must_be(nonneg, Size),
  144  format(atom(Flag), '--buffer-size=~d', [Size]).
  145% -n, --numeric-sort
  146sort_flag(numeric(IsNumeric), '--numeric-sort') :-
  147  must_be(boolean, IsNumeric),
  148  IsNumeric == true.
  149% -o, --output
  150sort_flag(output(OutFileSpec), Flag) :-
  151  absolute_file_name(OutFileSpec, OutFile, [access(write)]),
  152  format(atom(Flag), '--output=~a', [OutFile]).
  153% --parallel
  154sort_flag(threads(NumberOfThreads), Flag) :-
  155  must_be(positive_integer, NumberOfThreads),
  156  NumberOfThreads > 0,
  157  format(atom(Flag), '--parallel=~d', [NumberOfThreads]).
  158% -T, --temporary-directory
  159sort_flag(temporary_directory(Dir), Flag) :-
  160  must_be(directory, Dir),
  161  format(atom(Flag), '--temporary-directory=~a', [Dir]).
  162% -u, --unique
  163sort_flag(duplicates(KeepDuplicates), '--unique') :-
  164  must_be(boolean, KeepDuplicates),
  165  KeepDuplicates == false