<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">% \iffalse meta-comment
%
% Copyright (C) 2013 by Robert J Lee
%
% This file may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, either
% version 1.2 of this license or (at your option) any later
% version. The latest version of this license is in:
%
% http://www.latex-project.org/lppl.txt
%
% and version 1.2 or later is part of all distributions of
% LaTeX version 1999/12/01 or later.
%
% \fi
%
% \iffalse
%&lt;package&gt;\NeedsTeXFormat{LaTeX2e}[1999/12/01]
%&lt;package&gt;\ProvidesPackage{arraysort}
%&lt;package&gt;   [2013/09/04 v1.0 sorting arrayjobx arrays]
%
%&lt;*driver&gt;
\documentclass{ltxdoc}
\usepackage[comparestr,comparenum,randompart]{arraysort}
\EnableCrossrefs
\usepackage{indentfirst}
\usepackage{showexpl}
\usepackage{multido}
\newbool{excludelongtest}
\setbool{excludelongtest}{true} % change to false for slow test
\CodelineIndex
\RecordChanges
\begin{document}
  \DocInput{arraysort.dtx}
\end{document}
%&lt;/driver&gt;
% \fi
%
% \CharacterTable
%  {Upper-case    \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
%   Lower-case    \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
%   Digits        \0\1\2\3\4\5\6\7\8\9
%   Exclamation   \!     Double quote  \"     Hash (number) \#
%   Dollar        \$     Percent       \%     Ampersand     \&amp;
%   Acute accent  \'     Left paren    \(     Right paren   \)
%   Asterisk      \*     Plus          \+     Comma         \,
%   Minus         \-     Point         \.     Solidus       \/
%   Colon         \:     Semicolon     \;     Less than     \&lt;
%   Equals        \=     Greater than  \&gt;     Question mark \?
%   Commercial at \@     Left bracket  \[     Backslash     \\
%   Right bracket \]     Circumflex    \^     Underscore    \_
%   Grave accent  \`     Left brace    \{     Vertical bar  \|
%   Right brace   \}     Tilde         \~}
%
% \CheckSum{236}
%
% \changes{v1.0}{2013/09/04}{Initial version}
%
% \GetFileInfo{arraysort.sty}
%
% \DoNotIndex{\let, \edef, \xdef, \relax, \newcommand}
% \DoNotIndex{\csname, \endcsname, \ifcsname}
% \DoNotIndex{\OR, \if, \else, \fi, \equal, \ifthenelse, \iftoggle}
% \DoNotIndex{\arabic, \addtocounter, \value, \setcounter, \stepcounter}
% \DoNotIndex{\g@addto@macro, \string}
%
% 
%\def\IsPositive#1{% http://www.tex.ac.uk/cgi-bin/texfaq2html?label=isitanum
%  TT\fi
%  \ifcat_\ifnum0&lt;0#1 _\else A\fi
%}
%
% \newenvironment{exg}[1]{\pagebreak[3]\begin{center}
% \line(1,0){250}
% \end{center}\nopagebreak~\par\nopagebreak\textit{Example:~~\texttt{#1}}\par\vspace{-1em}}{\begin{center}
% \line(1,0){250}
% \end{center}\delarray{A}\noindent}
%
% \title{The Arraysort Package\thanks{This document corresponds to \textsf{Arraysort}~\fileversion, dated \filedate.}}
% \author{Robert J Lee \\ latex@rjlee.homelinux.org}
%
% \maketitle
%
% \begin{abstract}
%
% The |arraysort| package allows the user to sort an array (defined
% with the |arrayjobx| package), or a portion of such an array,
% without using external files or commands or requiring a second run
% of \LaTeX.
%
% Basic comparators are provided for sorting by ASCII-code or for
% sorting numeric values. Options to tweak performance of the sort are
% also provided, should they be needed.
%
% \end{abstract}
%
% \section*{Introduction}
%
% This package implements an in-place Quick Sort algorithm for
% \LaTeX. Quick-Sort is a recursive and highly configurable algorithm
% for sorting of arrays.
%
%
% \section{Usage}
%
% The |arraysort| package requires the |arrayjobx| package, which
% provides several methods to define an array of values. This docment
% assumes that you are familiar with |arrayjobx|. A number of examples
% are provided to sort a small array named $A$; the package can sort
% much larger arrays than those shown including arrays whose contents
% are stored unexpanded (although they will be expanded when comparing
% them using the provided comparators).
%
% \subsection*{Sorting by Text}
%
% \DescribeMacro{\sortArray}
%
% To sort the first |10| elemnets of $A$, you can simply write:
% |\sortArray{|10|}{|$A$|}|
%
% |\sortArray| takes two mandatory parameters: the first is the 
% number of elements to sort, the second is the name of the array.
%
% \iffalse
%&lt;*example&gt;
% \fi
\begin{exg}{$\backslash$sortArray with words}\begin{LTXexample}
\newarray{A}
\readarray{A}{The&amp;Quick&amp;Brown&amp;%
Fox&amp;Jumps&amp;Over&amp;the&amp;Lazy&amp;Dog}
\sortArray{9}{A}
\A(1) \A(2) \A(3) \A(4) \A(5)
\A(6) \A(7) \A(8) \A(9)
\end{LTXexample}\end{exg}
% \iffalse
%&lt;/example&gt;
% \fi
% Note that the default is to sort by character code order, so the
% lower-case ``the'' is sorted after the words starting with
% upper-case letters. This is a limitation of the default sorting
% method, but other ways of sorting are possible.
% 
% \subsection*{Sorting Numbers}
%
% The default sorting method can have surprising results when sorting
% arrays of numbers:
% \iffalse
%&lt;*example&gt;
% \fi
\begin{exg}{$\backslash$sortArray with numbers}\begin{LTXexample}
\newarray{A}
\readarray{A}{78&amp;4&amp;85&amp;1&amp;28&amp;6}
\sortArray{6}{A}
\A(1) \A(2) \A(3) \A(4) \A(5) \A(6)
\end{LTXexample}\end{exg}
% \iffalse
%&lt;/example&gt;
% \fi
% Here, the numbers are still sorted in dictionary order,
% which was probably not the intent, as the number~28 would usually be
% sorted after the number~6, even though the first digit of~28 is
% smaller than the first digit of~6.
%
% To solve this problem, an alternative comparator can be used:
% \iffalse
%&lt;*example&gt;
% \fi
\begin{exg}{$\backslash$sortArray with |arraysortcomparenum|}\begin{LTXexample}
\newarray{A}
\readarray{A}{78&amp;4&amp;85&amp;1&amp;28&amp;6}
\sortArray[arraysortcomparenum]{6}{A}
\A(1) \A(2) \A(3) \A(4) \A(5) \A(6)
\end{LTXexample}\end{exg}
% \iffalse
%&lt;/example&gt;
% \fi
% The |arraysortcomparenum| comparator is passed as the first optional
% argument; this option sorts by numerical order in the intuitive
% way --- but it will error if a value in the array is not a number.
%
% This option requires additional package options; see section below.
%
% \subsection*{Sorting part of an array}
%
% A second optional argument is accepted for the start of the range to
% be sorted. So you can easily sort only a segment of an array:
% \iffalse
%&lt;*example&gt;
% \fi
\begin{exg}{$\backslash$sortArray with range}\begin{LTXexample}
\newarray{A}
\readarray{A}{P&amp;Y&amp;F&amp;G&amp;%
A&amp;O&amp;E&amp;U&amp;I&amp;D&amp;H&amp;T&amp;N&amp;S&amp;%
Q&amp;J&amp;K&amp;X&amp;B&amp;M&amp;W&amp;V&amp;Z}
\sortArray[arraysortcomparestr][8]{21}{A}
\A(1)\A(2)\A(3)\A(4)\A(5)\A(6)\A(7)%
\textit{\A(8)\A(9)\A(10)\A(11)\A(12)%
\A(13)\A(14)\A(15)\A(16)}\A(17)\A(18)%
\A(19)\A(20)\A(21)\A(22)\A(23)
\end{LTXexample}\end{exg}
% \iffalse
%&lt;/example&gt;
% \fi
% The start of the range must be the second optional parameter, so you
% need to specify the comparator as well. The default comparator is
% |arraysortcomparestr| as shown here.
% 
% The example shows sorting the italicised region of 8--21 letters,
% originally arranged in the relatively jumbled order of the Dvorak
% keyboard.
%
% \subsection*{Sorting by Custom Order}
%
% While it is useful to sort an array into numbers or alphabetically,
% it is possible to sort an array into any order. To do this, you need
% to write a \textit{comparator}; this is a macro that is passed 2
% values from the array and evaluates which should appear first.
%
% A custom comparotor macro can be passed by appending its name as the
% optional argument is passed at the end of the |\sortarray| macro
% call.
%
% Your comparator macro must set the value of two toggles:
%
% \DescribeMacro{arraysortresult}
% Set |arraysortresult| to true if the first parameter is less than
% the second (\textit{ie} if the parameters are presented in sorted
% order).
%
% \DescribeMacro{arraysortresequal}
% |arraysortresequal| should be set by a comparator if both values are
% equal. It is not necessary to set |arraysortresult| if
% |arraysortresequal| is set to true.
%
% Both toggles can be set using the macros |\toggletrue{|\textit{togglename}|}|
% and |\togglefalse{|\textit{togglename}|}| defined in the |etoolbox|
% package, where \textit{togglename} is the name of the toggle to be
% set or cleared.
%
% The name of the comparator is passed to the sort algorithm as the
% first optional parameter; the leading $\backslash$ should be
% omitted.
%
% \iffalse
%&lt;*example&gt;
% \fi
\begin{exg}{$\backslash$sortArray with custom comparator}\begin{LTXexample}
\newcommand{\cmpnumbersfirst}[2]{%
  \edef\cmpA{#1}%
  \edef\cmpB{#2}%
  \if\IsPositive{\cmpA}%
    \if\IsPositive{\cmpB}%
      \arraysortcomparenum{\cmpA}{\cmpB}%
    \else%
      \togglefalse{arraysortresequal}%
      \toggletrue{arraysortresult}%
    \fi%
  \else%
    \if\IsPositive{\cmpB}%
      \togglefalse{arraysortresequal}%
      \togglefalse{arraysortresult}%
    \else%
      \arraysortcomparestr{\cmpA}{\cmpB}%
    \fi%
  \fi%
}
\newarray{A}
\readarray{A}{apple&amp;2&amp;rabbits&amp;12&amp;-4}
\sortArray[cmpnumbersfirst]{5}{A}
\A(1) \A(2) \A(3) \A(4) \A(5)
\end{LTXexample}\end{exg}
% \iffalse
%&lt;/example&gt;
% \fi
% This example uses the following definition of |\IsPositive| from
% the |cite| package\footnote{See
% \texttt{http://www.tex.ac.uk/cgi-bin/texfaq2html?label=isitanum}}
%
% \begin{verbatim}
% \def\IsPositive#1{%
%   TT\fi
%   \ifcat_\ifnum0&lt;0#1 _\else A\fi
% }
% \end{verbatim}
%
% This custom sort places positive integers first, in numerical order,
% then everything else in default order.
%
% The |\cmpnumbersfirst| macro tests each parameter to see if is a
% positive number. If both parameters are positive integers, then it delegates
% to the |arraysortcomparenum| macro so that integers are sorted in
% sequence. If both parameters are not positive integers then the
% default sort is used. Otherwise, |arraysortresult| is set to true if
% |#1| is a positive integer and |#2| is not, or false if it is the
% other way around; this guarantees that positive numbers are sorted first.
%
% \textsc{tip:} Most comparators will fully expand their arguments
% only once per comparison, to ensure that the sorting order remains
% appropriate. That is the reason for the |\edef| in the above
% example, which expands and copies the parameter into a temporary
% macro. This is useless when passing individual string arguments, as
% in this example, but prevents unstable behaviour when arguments
% could change when reevaluated, such as when a macro contains the
% current time or a pseudorandom value.
%
% \subsection*{Changing the Partitioning Scheme}
%
% The partitioning scheme does not affect the final sorting order
% (unless you write your own that does not use the comparator argument)
% but may affect how long it takes \LaTeX\ to sort your array. In
% general it is recommended to use the default scheme, unless you are
% sorting a very large array and find the
% performance is unacceptable.
%
% To change the partitioning scheme, an optional argument can be added
% to the end of the |\sortArray| macro, thus:
% \iffalse
%&lt;*example&gt;
% \fi
\begin{exg}{$\backslash$sortArray with custom
    partition}\begin{LTXexample}
\newarray{A}
\readarray{A}{e&amp;d&amp;c&amp;b&amp;a}
\sortArray{5}{A}%
       [sortArrayPartitionRand]
\A(1) \A(2) \A(3) \A(4) \A(5)
\end{LTXexample}\end{exg}
% \iffalse
%&lt;/example&gt;
% \fi
% Here, the writer of the package knows that $A$ is not randomised
% before it is sorted, so uses the |sortArrayPartitionRand| to
% partition the array at random. This avoids the worst-case
% performance of the sorting algorithm.
%
% The performance of each partitioning method is discussed in detail
% where it is defined; you should also read the section on the
% in-place quick-sort algorithm to understand the purpose of each
% algorithm.
%
% The partition name is just the name of a macro, so you can easily
% write your own. It must take four parameters:
% \begin{enumerate}
% \item The name of a comparator macro, described above
% \item The start index (inclusive) of the array segment to be partitioned
% \item The end index (inclusive) of the array segment to be
% partitioned
% \item The name of the array to be partitioned
% \end{enumerate}
%
% The macro should generate no output as it may be called multiple
% times with different array segments to partition. It should set
% |arraysort@partpos| to the current value of the partition element.
%
% Values that are equal to the partition value may be sorted into
% either segment. A sorting algorithm that retains the relative order
% of equal values is known as a \textit{stable sorting algorithm}; if
% this is required, then the partitioning algorithm must
% retain the relative position of each element in each sub-array,
% not just those which are equal to the pivot. \textbf{The supplied
% partitioning algorithms make no claim to be a stable sort},
% and stable sort semantics of the partitioning algorithm should
% \textbf{not be relied on for future versions}.
%
% In general, it is sufficient to identify the partition element
% within the array and swap it with the first element, then use
% |\sortArrayPartitionFirst|
%
% \textsc{Note}: It is important to ensure that all elements are
% expanded only once during the partition, as it is theoretically
% possible for a macro to expand to different values each time it is
% expanded.
%
% \subsection*{All Package Options}
%
% Package options are passed on the |\usepackage| line near the top of
% your document. A comma-separated list may be supplied, like this:
%
% \verb!\usepackage[comparestr,comparenum,randompart]{arraysort}!
%
% \DescribeMacro{comparestr}
% The |comparestr| option requires the |pdftexcmds| package to be
% installed and to run |pdflatex|. It is currently the default sort
% option, so you must either supply the |comparestr| option or specify
% a comparator explicitly.
%
% \DescribeMacro{comparenum}
% The |comparenum| option defines the |arraysortcomparenum|
% comparator, which allows you to sort arrays comparing numbers by
% numeric value instead of by name.
%
% \DescribeMacro{randompart}
% The |randompart| macro requires the |lcg| package to be
% installed. It defines the |sortArrayPartitionRand| option to
% partition arrays using a pseudorandom sequence.
% This option repeatedly calls |\reinitrand|, which resets the value
% of the pseudorandom sequence as well as the maximum and minimum
% values generated, so you should take care if using the |lcg| package
% outwith this package. At minimum, you should call |\reinitrand|
% yourself after every sort, and possibly within macros if |\rand| is
% used. Be aware that this will output whitespace unless care is taken
% to consume it before it is output. |lcg| will output warnings about
% reusing an existing counter; these can be safely ignored as
% |sortArrayPartitionRand| intentionally reuses the counter to prevent
% exhaustion of counters with large sorts.
%
% \pagebreak
% \section{Method: The In-Place Quick-Sort Algorithm}
%
% Quick-sort is a practical example of a recursive algorithm.
%
% \subsubsection*{Definition of terms:}
%
% \begin{description}
% \item[Partition] A single element from the array.
% \item[Segment] A contiguous group of elements from the array.
% \end{description}
%
% The general approach is to divide the array into two smaller arrays,
% then sort each smaller array in turn.
%
% The inital array segment is the entire array.
%
% The basic steps are:
%
% \begin{enumerate}
% \item Determine $A_P$, the index of the partition element within the
% current array segment. In practice, this must be done in constant
% time~[$O(1)$] or the sort becomes slow.
% \item Partition the array into two array segments, separated by a
% partition, in linear time~[$O(N)$].
% \item Quick-sort the first array segment
% \item Quick-sort the second array segment
% \end{enumerate}
%
% The first step is choosing the partition element, $A_P$. This may be
% any element from the array segment, although the fastest results are
% achieved by selecting the median value. Many algorithms exist to
% perform this ``best guess''.
%
% The next step is partitioning. Other than the partition element,
% every element in the array is iterated over in turn. Any value less
% than the partition value~$P$ is moved to the left of the partition
% (lower index than $A_P$), and any value greater than the partition
% is moved to its right (higher index than $A_P$).
%
% So, if there are $N$ elements in an array~$A$, the original array is
% given by:
% \begin{eqnarray*}
% &amp; A_0 \dots A_n &amp;
% \end{eqnarray*}
%
% After the partitioning stage, the partitioned array is given by:
% \begin{eqnarray*}
% &amp; A_0 \dots  A_{(P-1)} , A_P , A_{(P+1)} \dots  A_N &amp;
% \end{eqnarray*}
% where $A_P$ is the partition and $P$ is the index of the
% partition.
%
% $A_0 \dots  A_{(P-1)}$ defines the first array segment to be
% sorted and $A_{(P+1)} \dots  A_N$ defines the second.
%
% Each array segment is considered to be sorted if it contains one
% element or less; otherwise, each  are presented to the entire
% quick-sort algorithm to be sorted.
%
% Each iteration through the algorithm divides the array into smaller
% segments, each of which is always sorted relative to the other
% segments. Once the segment size is as small as one element, the
% entire array is sorted.
%
% \section{Possible Future Improvements}
%
% \begin{itemize}
% \item It should be possible to sort macro contents by their
% unexpanded values
% \item It may also be possible to sort macro contents in a
% case-insensitive manner (depending on language). Note that sorting
% mixed-language, mixed-alphabet content in a standard-complient
% manner is not always possible.
% \item Further speed improvemnts are possible; in particular, it is
% often faster to defer to a lower-overhead $O(n^2)$ sorting algorithm
% when the number of array segment elements is smaller than some threshold
% value (quick-sort is inefficient at sorting small arrays, but
% often produces many of them to be sorted).
% \item More sorting and partitining options. I am undecided about
% passing options \textit{versus}\ simply defining multiple macros;
% certainly the chance of a name collision is very small with the
% naming convention used so far.
% \item Support partition values not in the array:
% \end{itemize}
%
% \noindent Some implementations of quicksort allow for a partition value $P$
% that does not correspond to a value in the array, divding the array
% into:
% \begin{eqnarray*}s
% &amp; A_0 \dots\/A_i , A_{(i+1)} \dots\ A_N &amp;
% \end{eqnarray*}
% \noindent where $i$ is arbitrary. This is usually less efficient, as there is
% an additional value to be sorted with each iteration and hence a
% greater number of iterations in the best case.
%
% In some cases, it is not possible to know the best partition value
% within an array segment, or a single partition may not be applicable
% (such as an array containing only two distinct values); however, 
% there may be some knowledge of the array's distribution. In the best
% case, a pre-calculated median of the array's contents might already be
% available prior to sorting, which would be the ideal partition value
% for the first iteration but would be unlikely to be a value in the
% array, let alone a value of known position.
%
% This would likely require significant changes to the algorithm.
%
% \ifexcludelongtest\else
% \section{Large-Scale Sorting}
% 
%
%  \makeatletter
% \iffalse
%&lt;*example&gt;
% \fi
\begin{exg}{$\backslash$sortArray on a large 
scale}\begin{LTXexample}
\newarray{A}
\expandarrayelementtrue
\reinitrand[counter=rand,quiet=y]
\newcommand{\asize}{10000}
\multido{\i=1+1}{\asize}{
 \rand
 \A(\i)={\arabic{rand}}
}

\textbf{Before sort:}

\multido{\i=1+1}{50}{
 \A(\i)
}\dots

\sortArray[arraysortcomparenum]{%
  \asize}{A}

\line(1,0){100}

\textbf{After sort:}

\multido{\i=1+1}{50}{
 \A(\i)
}\dots

\end{LTXexample}
This example uses the |multido| and |lcg| packages\end{exg}
% \iffalse
%&lt;/example&gt;
% \fi
% \fi
%
% \section{Implementation}
%\StopEventually{\PrintChanges \pagebreak[4] \PrintIndex}
%
% \begin{macro}{\arraysort@extrapkgs}
% The \LaTeX\ package-option support does not allow conditional
% includes of packages. So, instead, we build up the required
% |\RequiresPackage| statements inside the |\arraysort@extrapkgs|
% macro.
%    \begin{macrocode}
\newcommand*{\arraysort@extrapkgs}{}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{comparestr}
%    \begin{macrocode}
\DeclareOption{comparestr}{
  \g@addto@macro{\arraysort@extrapkgs}{
    \RequirePackage{pdftexcmds}% for comparison. TODO: use compare.sty optionally
  }
%    \end{macrocode}
% \begin{macro}{\arraysortcomparestr}
% Called with two arguments, guaranteed to be re-evaluatable.
% must set arraysortresequal if arguments are considered equal, otherwise
% must set arraysortresult true if |#2| is to be sorted after |#1|, otherwise
% must set both flags false.
%
% Basic ASCII-like comparison
%    \begin{macrocode}
  \newcommand*{\arraysortcomparestr}[2]{%
    \protected@edef\arraysort@left{#1}%
    \protected@edef\arraysort@right{#2}%
    \arraysort@comparestr%
  }
%    \end{macrocode}
%
% The following macro performs the comparison. The parameters must (it
% seems) be passed by macro as passing by parameter |#1| and |#2| did
% not cause the expected results, hence the extra macro.
%    \begin{macrocode}
  \newcommand*{\arraysort@comparestr}{%
    \protected@edef\arraysort@compresult{\pdf@strcmp{\arraysort@left}{\arraysort@right}}%
    \ifthenelse{\equal{\arraysort@compresult}{0}}{%
      \toggletrue{arraysortresequal}%
    }{%
      \togglefalse{arraysortresequal}%
      \ifthenelse{\equal{\arraysort@compresult}{-1}}{%
        \toggletrue{arraysortresult}% #2 &gt; #1
      }{%
        \togglefalse{arraysortresult}% #2 &lt; #1
      }%
    }%
  }
}
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \begin{macro}{comparenum}
%    \begin{macrocode}
% Numeric comparison, as |\arraysortcomparestr| but used with arrays compairng numbers
\DeclareOption{comparenum}{
%    \end{macrocode}
% \begin{macro}{\arraysortcomparenum}
%    \begin{macrocode}
  \newcommand*{\arraysortcomparenum}[2]{%
    \ifthenelse{\equal{#1}{#2}}{%
      \toggletrue{arraysortresequal}%
    }{%
      \togglefalse{arraysortresequal}%
      \ifthenelse{#2 &gt; #1}{%
        \toggletrue{arraysortresult}%
      }{%
        \togglefalse{arraysortresult}%
      }%
    }%
  }
}
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% All partitioning algorithms should complete in O(1) time; that is,
% they should not iterate over the array, or do anything that takes
% longer the more elements there are.
%
% \begin{macro}{\sortArrayPartitionMed}
%
% Partition the segment consisting of indexes |#2|--|#3| (inclusive)
% of array named |#4|, using comparator |#1|
%
% Use the median of the first, last and middle values. While this has
% extra overhead compared to sortArrayPartitionFirst, it is guaranteed
% to avoid the worst-case performance of that method. If the array is
% randomly shuffled prior to sorting, this usually offers the best
% performance. This is the default method.
%
% Performance depends on the comparison macro.
%
% May not work well if there are many duplicate values.
%    \begin{macrocode}
\newcommand*{\sortArrayPartitionMed}[4]{%
  \setcounter{arraysort@temp1}{(#2 + #3) / 2}%
  \edef\arraysort@midpos{\arabic{arraysort@temp1}}%
  \testarray{#4}(#2)\protected@edef\arraysort@left{\temp@macro}%
  \testarray{#4}(\arraysort@midpos)\protected@edef\arraysort@mid{\temp@macro}%
  \testarray{#4}(#3)\protected@edef\arraysort@right{\temp@macro}%
  \csname#1\endcsname{\arraysort@left}{\arraysort@mid}%
  \iftoggle{arraysortresequal}{%
%    \end{macrocode}
% left $=$ mid
% if any two are the same, there can be no median, so may as well
% leave alone
%    \begin{macrocode}
  }{%
%    \end{macrocode}
% left $\ne$ mid
%    \begin{macrocode}
    \iftoggle{arraysortresult}{%
%    \end{macrocode}
% left $&lt;$ mid
%    \begin{macrocode}
      \csname#1\endcsname{\arraysort@left}{\arraysort@right}%      
      \iftoggle{arraysortresequal}{%
%    \end{macrocode}
% $(\mbox{left} = \mbox{right}) &lt; \mbox{mid}$
%    \begin{macrocode}
      }{%
        \iftoggle{arraysortresult}{%
%    \end{macrocode}
% left $&lt;$ mid, left $&lt;$ right
%    \begin{macrocode}
          \csname#1\endcsname{\arraysort@mid}{\arraysort@right}%      
          \iftoggle{arraysortresequal}{%
%    \end{macrocode}
% $\mbox{left} &lt; (\mbox{mid} = \mbox{right})$
%    \begin{macrocode}
          }{%
            \iftoggle{arraysortresult}{%
%    \end{macrocode}
% $\mbox{left} &lt; \mbox{mid} &lt; \mbox{right}$
%    \begin{macrocode}
              \arraysort@swap{#4}{#2}{\arraysort@midpos}%
            }{% 
%    \end{macrocode}
% $\mbox{left} &lt; \mbox{right} &lt; \mbox{mid}$
%    \begin{macrocode}
              \arraysort@swap{#4}{#2}{#3}%
            }%
          }%
        }{%
%    \end{macrocode}
% left $&lt;$ mid, left $&gt;$ right
%
% left is already in the middle; leave alone
%    \begin{macrocode}
        }%
      }%
    }{%
%    \end{macrocode}
% left $&gt;$ mid
%    \begin{macrocode}
      \csname#1\endcsname{\arraysort@mid}{\arraysort@right}%        
      \iftoggle{arraysortresequal}{%
%    \end{macrocode}
% $\mbox{left} &gt; (\mbox{mid} = \mbox{right})$
%    \begin{macrocode}
      }{%
        \iftoggle{arraysortresult}{%
%    \end{macrocode}
% $\mbox{left} &gt; \mbox{right} &gt; \mbox{mid}$
%    \begin{macrocode}
          \arraysort@swap{#4}{#2}{#3}%
%    \end{macrocode}
% swap right \&amp; left, so left is median
%    \begin{macrocode}
        }{%
%    \end{macrocode}
% $\mbox{left} &gt; \mbox{mid} &gt; \mbox{right}$
%
% swap right \&amp; mid, so left is median
%    \begin{macrocode}
          \arraysort@swap{#4}{#2}{\arraysort@midpos}%
        }%
      }%
    }%
  }%
  \sortArrayPartitionFirst{#1}{#2}{#3}{#4}%
}
%    \end{macrocode}
%
% \begin{macro}{\sortArrayPartitionRand}
%
% Partition the sub-array
% consisting of indexes |#2|--|#3| (inclusive) of array named |#4|,
% using comparator |#1|
%
% Use the lcg package to generate a (pseudo)-random partition
% value. This should perform reasonably well most of the time, and you
% can simply re-run LaTeX if the performance is unacceptable.
%
% Caution: this macro will re-initialise the LCG package.
% \begin{macro}{randompart}
%    \begin{macrocode}
\DeclareOption{randompart}{
  \g@addto@macro{\arraysort@extrapkgs}{
%    \end{macrocode}
% Store for later execution the fact that we will need tho |lcg| package
% for random numbers
%    \begin{macrocode}
    \RequirePackage[quiet]{lcg}
  }
  \newcommand*{\sortArrayPartitionRand}[4]{%
%    \end{macrocode}
% It is necessary to change the start and end values of the sequence; the only way
% to do this is by reinitialising |lcg|. There are 2 possible
% problems; firstly, reinitrand outputs whitespace; and secondly it
% prints out a warning about a re-used counter. It's actually best to re-use
% the counter, but there's no way to silence the warning.
%    \begin{macrocode}
    \reinitrand[counter=arraysort@temp1,first=#2,last=#3,quiet=y]%
    \rand%
    \arraysort@swap{#4}{#2}{\arabic{arraysort@temp1}}%
    \sortArrayPartitionFirst{#1}{#2}{#3}{#4}%
  }
}
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\sortArrayPartitionMid}
%
% Partition the sub-array
% consisting of indexes |#2|--|#3| (inclusive) of array named |#4|,
% using comparator |#1|
%
% This implementation uses the middle value in the array segment. This is
% generally the best option if you don't know anything about the
% array's contents; in particular, it offers reasonable speed when
% attempting to re-sort previously-sorted ararys. 
%    \begin{macrocode}
\newcommand*{\sortArrayPartitionMid}[4]{%
  \setcounter{arraysort@temp1}{(#2 + #3) / 2}%
  \arraysort@swap{#4}{#2}{\arabic{arraysort@temp1}}%
  \sortArrayPartitionFirst{#1}{#2}{#3}{#4}%
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\sortArrayPartitionFirst}
% Partition the array segment
% consisting of indexes |#2|--|#3| (inclusive) of array named |#4|,
% using comparator |#1|
%
% This implementation uses the first value in the array segment. This is
% fastest in theory, but only if the array is pre-shuffled. This has
% the worst performance when attempting to sort an already-sorted
% array.
%    \begin{macrocode}
\newcommand*{\sortArrayPartitionFirst}[4]{%
  \setcounter{arraysort@partpos}{#2}%
  \setcounter{arraysort@temp1}{#2 + 1}%
  \setcounter{arraysort@endpos}{#3 + 1}%
  \arraysort@repeats{arraysort@temp1}{\value{arraysort@temp1}}{\value{arraysort@endpos}}{1}{%
    \testarray{#4}(\arabic{arraysort@temp1})%
%    \end{macrocode}
% if the value $A_{\mbox{temp1}}$ is less than partition $A_P$,
% decrement the partition counter by 1 and swap.
%
%  |\let| copies without expanding:
%    \begin{macrocode}
    \let\arraysort@cur\temp@macro%
    \testarray{#4}(\arabic{arraysort@partpos})%
%    \end{macrocode}
% Expand the macros only once just in case they would be different
% on subsequent expansion:
%    \begin{macrocode}
    \protected@edef\arraysort@left{\arraysort@cur}%
    \protected@edef\arraysort@right{\temp@macro}%
    \csname#1\endcsname{\arraysort@left}{\arraysort@right}% #2 = cur, #3 = partition
    \setcounter{arraysort@temp2}{\value{arraysort@partpos} + 1}%
    \iftoggle{arraysortresequal}{% #2 = #3
%    \end{macrocode}
% Must be moved before pivot
% Swap $A_P$ with $A_{P+1}$ then swap (the new) $A_P$ with
% current ($A_{\mbox{temp2}}=A_{P+1}$)
%    \begin{macrocode}
      \arraysort@swap{#4}{\arabic{arraysort@partpos}}{\arabic{arraysort@temp2}}%
      \arraysort@swap{#4}{\arabic{arraysort@partpos}}{\arabic{arraysort@temp1}}%
%    \end{macrocode}
% Increment partition; otherwise the next non-equal pivot will break
%    \begin{macrocode}
      \stepcounter{arraysort@partpos}%
    }{%
      \iftoggle{arraysortresult}{% #3 &gt; #2
        \ifthenelse{\equal{\arabic{arraysort@temp2}}{\arabic{arraysort@temp1}}}{%
%    \end{macrocode}
% Just swap part with current value; they are adjacent
%    \begin{macrocode}
          \arraysort@swap{#4}{\arabic{arraysort@partpos}}{\arabic{arraysort@temp1}}%
        }{%
%    \end{macrocode}
% Swap $A_P$ with $A_{P+1}$ then swap (the new) $A_P$ with
% current ($\mbox{temp}_2=A_{p+1}$)
%    \begin{macrocode}
          \arraysort@swap{#4}{\arabic{arraysort@partpos}}{\arabic{arraysort@temp2}}%
          \arraysort@swap{#4}{\arabic{arraysort@partpos}}{\arabic{arraysort@temp1}}%
        }%
%    \end{macrocode}
% Increment partition; one more in left array segment
%    \begin{macrocode}
        \stepcounter{arraysort@partpos}%
      }{%
%    \end{macrocode}
% $A_{|arraysort@cur|} &gt; A_P$ and already after it; leave it alone
%    \begin{macrocode}
      }%
    }%
  }%
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\ProcessOptions}
% This processes the package include options, defining whichever of
% the above macros the user has asked for, and adding a list of any
% optional packages into |\arraysort@extrapkgs|.
%
%    \begin{macrocode}
\ProcessOptions\relax
%    \end{macrocode}
%\end{macro}
%
% Package includes for required packages:
%
% For loading the arrays that we will sort
%    \begin{macrocode}
\RequirePackage{arrayjobx}
%    \end{macrocode}
% For easier syntax on counter operations
%    \begin{macrocode}
\RequirePackage{calc}
%    \end{macrocode}
% For comparisons
%    \begin{macrocode}
\RequirePackage{ifthen}
%    \end{macrocode}
% Toggles etc.
%    \begin{macrocode}
\RequirePackage{etoolbox}
%    \end{macrocode}
% To declare macros with multiple optional arguments (\textit{ie}
% |\sortArray|)
%    \begin{macrocode}
\RequirePackage{xargs}
%    \end{macrocode}
% For partitioning
%    \begin{macrocode}
\RequirePackage{macroswap}
%    \end{macrocode}
% now process any conditional includes
%    \begin{macrocode}
\arraysort@extrapkgs
%    \end{macrocode}
%
% Now we are done including packages, so discard the macro:
%    \begin{macrocode}
\let\arraysort@extrapkgs\relax
%    \end{macrocode}
%
%
% \DescribeMacro{\sortArray}
% Sort the elements at index |#2|--|#3| of array named |#4|, using
% comparator |#1|.
%
% |#5| is the partitioning algorithm to use.
%
% \textit{eg} |\sortArray[1]{3}{ABC}|
%
% Defined using the |xargs| package
%    \begin{macrocode}
\newcommandx*\sortArray[5][1=arraysortcomparestr,2=1,5=sortArrayPartitionMed]{%
  \ifcsname#1\endcsname%
  \ifthenelse{#2&gt;0}{%
    \ifthenelse{#3&gt;#2}{%
      \ifcsname total@#4\endcsname%
        \arraysort@sort{#1}{#2}{#3}{#4}{#5}%
      \else%
        \PackageError{arraysort}{Cannot sort unknown array #4}{}%
      \fi%
    }{%
    \PackageError{arraysort}{Cannot sort; to index #3 greater than from index #2}{}%
    }%
  }{%
    \PackageError{arraysort}{Cannot sort; Invalid from index #2}{}%
  }%
  \else%
    \PackageError{arraysort}{Cannot sort by undefined comparator #1}{}%
  \fi%
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\arraysort@sort}
% As |\sortArray|, except that it doesn't validate its parameters,
% hence the |@| in the name (signifying an internal macro).
%
% Use with caution as error messages may be misleading.
%
% Sort the elements at index |#2|--|#3| of array named |#4|, using
% comparator |#1|
%
% |#5| is the partitioning algorithm to use.
%    \begin{macrocode}
\newcommand*{\arraysort@sort}[5]{%
  \csname#5\endcsname{#1}{#2}{#3}{#4}%
%    \end{macrocode}
% Keep the position on the local stack!
%    \begin{macrocode}
  \edef\arraysort@partition{\value{arraysort@partpos}}% 
  \setcounter{arraysort@temp1}{\arraysort@partition - 1}%
  \ifthenelse{#2 = \value{arraysort@temp1} \OR #2 &gt; \value{arraysort@temp1}}{%
  }{%
    \edef\arraysort@to{\arabic{arraysort@temp1}}%
    \arraysort@sort{#1}{#2}{\arraysort@to}{#4}{#5}%
  }%
  \setcounter{arraysort@temp1}{\arraysort@partition + 1}%
  \ifthenelse{\value{arraysort@temp1} = #3 \OR #3 &lt; \value{arraysort@temp1}}{%
  }{%
    \edef\arraysort@from{\arabic{arraysort@temp1}}%
    \arraysort@sort{#1}{\arraysort@from}{#3}{#4}{#5}%
  }%
}
%    \end{macrocode}
% \end{macro}
%
%
% Counters used for sorting
%
% current position of the partition
%    \begin{macrocode}
\newcounter{arraysort@partpos}
%    \end{macrocode}
% current position in loop
%    \begin{macrocode}
\newcounter{arraysort@temp1}
%    \end{macrocode}
% partition position $+1$
%    \begin{macrocode}
\newcounter{arraysort@temp2}
%    \end{macrocode}
% used for partitioning
%    \begin{macrocode}
\newcounter{arraysort@endpos}
%    \end{macrocode}
%
% Toggles used by the comparator macro
%
% set by comparison if $|#1| &lt; |#2|$
%    \begin{macrocode}
\newtoggle{arraysortresult}
%    \end{macrocode}
% set by comparison if $|#1| = |#2|$
%    \begin{macrocode}
\newtoggle{arraysortresequal}
%    \end{macrocode}
%
% \begin{macro}{\arrayort@repeats}
%
% Don't use |\whiledo| here because it uses up TeX's capacity, 
% so rolling own basic repeat loop...
%
% For counter |#1| from |#2| to |#3| step |#4|, do |#5|
%    \begin{macrocode}
\newcommand*{\arraysort@repeats}[5]{%
  \setcounter{#1}{#2}%
  \ifthenelse{\equal{\value{#1}}{#3}}{%
  }{%
    #5%
    \addtocounter{#1}{#4}%
    \arraysort@repeats{#1}{\arabic{#1}}{#3}{#4}{#5}%
  }%
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\arraysort@swap}
% Globally swap array values |#1(#2)| with |#1(#3)|
%
% \textit{ie} \\ |#1| is the macro name \\
% |#2| and |#3| are the numeic indexes of the array elements to be
% swapped.
%    \begin{macrocode}
\newcommand\arraysort@swap[3]{%
%    \end{macrocode}
% |arrayjobx| does not provide a way to assign an array element to the
% contents of another element (or macro) without expanding it. This macro simply
% swaps the definitions of the two macros used internally by |arrayjobx|:
%    \begin{macrocode}
  \gmacroswap{#1#2\string~}{#1#3\string~}%
}
%    \end{macrocode}
% \end{macro}
%
% That is all
% \Finale
\endinput
</pre></body></html>