File Coverage

blib/lib/Algorithm/Networksort.pm
Criterion Covered Total %
statement 276 459 60.1
branch 87 138 63.0
condition 18 34 52.9
subroutine 30 42 71.4
pod 12 28 42.8
total 423 701 60.3


line stmt bran cond sub pod time code
1             package Algorithm::Networksort;
2              
3 5     5   53353 use 5.010001;
  5         11  
4              
5 5     5   2369 use Moose;
  5         1567411  
  5         28  
6 5     5   22575 use Moose::Exporter;
  5         16  
  5         21  
7 5     5   158 use namespace::autoclean;
  5         6  
  5         36  
8              
9 5     5   282 use Carp;
  5         6  
  5         298  
10 5     5   3164 use integer;
  5         43  
  5         18  
11              
12             #
13             # Three # for "I am here" messages, four # for variable dumps.
14             # Five # for sort tracking.
15             #
16             #use Smart::Comments ('####');
17              
18             #
19             # Export a couple of convenience functions:
20             # nwsrt(), which is shorthand for Algorithm::Networksort->new();
21             # and algorithms(), which gives a list of algorithm keys.
22             #
23             Moose::Exporter->setup_import_methods(
24             as_is => [\&nwsrt_algorithms, \&nwsrt_title, \&nwsrt],
25             );
26              
27             our $VERSION = '2.01';
28              
29             #
30             # Our one use of overload, because default
31             # printing is useful.
32             #
33             use overload
34 5     5   353 '""' => \&_stringify;
  5         7  
  5         40  
35              
36             #
37             # Names for the algorithm keys.
38             #
39             my %algname = (
40             bosenelson => "Bose-Nelson Sort",
41             batcher => "Batcher's Mergesort",
42             hibbard => "Hibbard's Sort",
43             bubble => "Bubble Sort",
44             bitonic => "Bitonic Sort",
45             oddeventrans => "Odd-Even Transposition Sort",
46             balanced => "Balanced",
47             oddevenmerge => "Batcher's Odd-Even Merge Sort",
48             );
49              
50             #
51             # Default parameters for SVG, EPS, and text graphing.
52             #
53             my %graphset = (
54             hz_sep => 12,
55             hz_margin => 18,
56             vt_sep => 12,
57             vt_margin => 21,
58             indent => 9,
59             radius => 2,
60             stroke_width => 2,
61             inputbegin => "o-",
62             inputline => "---",
63             inputcompline => "-|-",
64             inputend => "-o\n",
65             compbegin => "-^-",
66             compend => "-v-",
67             gapbegin => " ",
68             gapcompline => " | ",
69             gapnone => " ",
70             gapend => " \n",
71             );
72              
73             #
74             # Default graphing color parameters.
75             #
76             my %colorset = (
77             foreground => undef,
78             inputbegin => undef,
79             inputline => undef,
80             inputend => undef,
81             compline=> undef,
82             compbegin => undef,
83             compend => undef,
84             background => undef,
85             );
86              
87             has algorithm => (
88             isa => 'Str', is => 'ro',
89             default => 'bosenelson',
90             );
91              
92             has inputs => (
93             isa => 'Int', is => 'ro', required => 1,
94             );
95              
96             has nwid => (
97             isa => 'Str', is => 'rw', required => 0,
98             predicate => 'has_nwid',
99             );
100              
101             has comparators => (
102             isa => 'ArrayRef[ArrayRef[Int]]', is => 'rw', required => 0,
103             predicate => 'has_comparators',
104             );
105              
106             has network => (
107             isa => 'ArrayRef[ArrayRef[Int]]', is => 'rw', required => 0,
108             predicate => 'has_network',
109             );
110              
111             has ['depth', 'length'] => (
112             isa => 'Int', is => 'rw', required => 0,
113             init_arg => 0,
114             );
115              
116             has creator => (
117             isa => 'Str', is => 'ro', required => 0,
118             default => sub { return "Perl module " . __PACKAGE__ . ", " .
119             "version $VERSION";}
120             );
121              
122             has title => (
123             isa => 'Str', is => 'rw', required => 0,
124             predicate => 'has_title'
125             );
126              
127             has formats => (
128             isa => 'ArrayRef[Str]', is => 'rw', required => 0,
129             init_arg => undef,
130             );
131              
132             has grouped_format => (
133             isa => 'Str', is => 'rw', required => 0,
134             default => "%s,\n",
135             );
136              
137             has index_base => (
138             isa => 'ArrayRef[Value]', is => 'rw', required => 0,
139             );
140              
141             #
142             # Variables to track sorting statistics
143             #
144             my $swaps = 0;
145              
146             =pod
147              
148             =encoding UTF-8
149              
150             =head1 NAME
151              
152             Algorithm::Networksort - Create Sorting Networks.
153              
154             =begin html
155              
156             <svg xmlns="http://www.w3.org/2000/svg"
157             xmlns:xlink="http://www.w3.org/1999/xlink" width="105" height="61" viewbox="0 0 105 61">
158             <rect width="100%" height="100%" fill="#eeeeee" />
159             <defs>
160             <g id="I_41c1" style="fill:none; stroke-width:2" >
161             <circle style="stroke:#206068" cx="18" cy="0" r="2" />
162             <line style="stroke:#206068" x1="18" y1="0" x2="87" y2="0" />
163             <circle style="stroke:#206068" cx="87" cy="0" r="2" />
164             </g>
165              
166             <g id="C1_41c1" style="stroke-width:2" >
167             <line style="fill:#206068; stroke:#206068" x1="0" y1="0" x2="0" y2="15" />
168             <circle style="fill:#206068; stroke:#206068" cx="0" cy="0" r="2" />
169             <circle style="fill:#206068; stroke:#206068" cx="0" cy="15" r="2" />
170             </g>
171             <g id="C2_41c1" style="stroke-width:2" >
172             <line style="fill:#206068; stroke:#206068" x1="0" y1="0" x2="0" y2="30" />
173             <circle style="fill:#206068; stroke:#206068" cx="0" cy="0" r="2" />
174             <circle style="fill:#206068; stroke:#206068" cx="0" cy="30" r="2" />
175             </g>
176             </defs>
177              
178             <g id="N_41c1">
179             <use xlink:href="#I_41c1" y="8" /> <use xlink:href="#I_41c1" y="23" />
180             <use xlink:href="#I_41c1" y="38" /> <use xlink:href="#I_41c1" y="53" />
181              
182             <use xlink:href="#C1_41c1" x="27" y="8" /> <use xlink:href="#C1_41c1" x="27" y="38" />
183             <use xlink:href="#C2_41c1" x="44" y="8" /> <use xlink:href="#C2_41c1" x="61" y="23" />
184             <use xlink:href="#C1_41c1" x="78" y="23" />
185             </g>
186             </svg>
187              
188             =end html
189              
190             =head1 SYNOPSIS
191              
192             use Algorithm::Networksort;
193              
194             my $inputs = 4;
195             my $algorithm = "bosenelson";
196              
197             #
198             # Generate the sorting network (a list of comparators).
199             #
200             my $nw = Algorithm::Networksort->new(inputs =>$inputs
201             algorithm => $algorithm);
202              
203             #
204             # Print the comparator list using the default format,
205             # and print a graph of the list.
206             #
207             print $nw->formatted(), "\n";
208             print $nw->graph_text(), "\n";
209              
210             #
211             # Set up a pretty SVG image.
212             #
213             $nw->graphsettings(vt_margin => 8, vt_sep => 13, hz_sep=>15);
214             $nw->colorsettings(foreground => "#206068", background => "#c8c8c8");
215             print $nw->graph_svg();
216              
217             =head1 DESCRIPTION
218              
219             This module will create sorting networks, a sequence of comparisons
220             that do not depend upon the results of prior comparisons.
221              
222             Since the sequences and their order never change, they can be very
223             useful if deployed in hardware, or if used in software with a compiler
224             that can take advantage of parallelism. Unfortunately a sorting network cannot
225             be used for generic run-time sorting like quicksort, since the arrangement of
226             the comparisons is fixed according to the number of elements to be
227             sorted.
228              
229             This module's main purpose is to create compare-and-swap macros (or
230             functions, or templates) that one may insert into source code. It may
231             also be used to create images of the sorting networks in either encapsulated
232             postscript (EPS), scalar vector graphics (SVG), or in "ascii art" format.
233              
234             =head2 Exported Functions
235              
236             For convenience's sake, there are three exported functions to save typing
237             and inconvenient look-ups.
238              
239             =head3 nwsrt()
240              
241             Simple function to save the programmer from the agony of typing
242             C<Algorithm::Networksort-E<gt>new()>:
243              
244             use Algorithm::Networksort;
245              
246             my $nw = nwsrt(inputs => 13, algorithm => 'bitonic');
247              
248             =cut
249              
250 64     64 1 37603 sub nwsrt { return __PACKAGE__->new(@_); }
251              
252              
253             =head3 nwsrt_algorithms()
254              
255             Return a sorted list algorithm choices. Each one is a valid key for the
256             algorithm argument of Algorithm::Networksort->new(), or L<nwsrt()>.
257              
258             use Algorithm::Networksort;
259              
260             my @alg_keys = nwsrt_algorithms();
261              
262             print "The available keys for the algorithm argument are (",
263             join(", ", @alg_keys), ")\n";
264              
265             Or, for an even less likely example:
266              
267             my $inputs = 6;
268              
269             for my $al (nwsrt_algorithms())
270             {
271             my $nw = nwsrt(inputs => $inputs, algorithm => $al);
272             print $nw->title(), "\n", $nw, "\n";
273             }
274              
275             =cut
276              
277             sub nwsrt_algorithms
278             {
279 1     1 1 1096 return sort keys %algname;
280             }
281              
282             =head3 nwsrt_title
283              
284             Return a descriptive title for the network, given an algorithm's key name.
285              
286             $title = nwsrt_title($key);
287              
288             These are the titles for the available algorithms. By themselves, they provide
289             a readable list of choices for an interactive program. They are not to be
290             confused with a sorting network's title, which may be set by the programmer.
291              
292             =cut
293              
294             sub nwsrt_title
295             {
296 0     0 1 0 my $key = shift;
297            
298 0 0       0 unless (exists $algname{$key})
299             {
300 0         0 carp "Unknown name '$key'.";
301 0         0 return "";
302             }
303              
304 0         0 return $algname{$key}{title};
305             }
306              
307             =head2 Methods
308              
309             =head3 new()
310              
311             $nw1 = Algorithm::Networksort->new(inputs => $inputs,
312             algorithm => 'batcher');
313              
314             $nw2 = Algorithm::Networksort->new(inputs => $inputs,
315             algorithm => 'oddevenmerge');
316              
317             Returns an object that contains, among other things, a list of comparators that
318             can sort B<$inputs> items. The algorithm for generating the list may be chosen,
319             but by default the sorting network is generated by the Bose-Nelson algorithm.
320              
321             The choices for the B<algorithm> key are
322              
323             =over 3
324              
325             =item 'bosenelson'
326              
327             Use the Bose-Nelson algorithm to generate the network. This is the most
328             commonly implemented algorithm, recursive and simple to code.
329              
330             =item 'hibbard'
331              
332             Use Hibbard's algorithm. This iterative algorithm was developed after the
333             Bose-Nelson algorithm was published, and produces a different network
334             "... for generating the comparisons one by one in the order in which
335             they are needed for sorting," according to his article (see below).
336              
337             =item 'batcher'
338              
339             Use Batcher's Merge Exchange algorithm. Merge Exchange is a real sort, in
340             that in its usual form (for example, as described in Knuth) it can handle
341             a variety of inputs. But while sorting it always generates an identical set of
342             comparison pairs per array size, which lends itself to sorting networks.
343              
344             =item 'bitonic'
345              
346             Use Batcher's bitonic algorithm. A bitonic sequence is a sequence that
347             monotonically increases and then monotonically decreases. The bitonic sort
348             algorithm works by recursively splitting sequences into bitonic sequences
349             using so-called "half-cleaners". These bitonic sequences are then merged
350             into a fully sorted sequence. Bitonic sort is a very efficient sort and
351             is especially suited for implementations that can exploit network
352             parallelism.
353              
354             =item 'oddevenmerge'
355              
356             Use Batcher's Odd-Even Merge algorithm. This sort works in a similar way
357             to a regular merge sort, except that in the merge phase the sorted halves
358             are merged by comparing even elements separately from odd elements. This
359             algorithm creates very efficient networks in both comparators and stages.
360              
361             =item 'bubble'
362              
363             Use a naive bubble-sort/insertion-sort algorithm. Since this algorithm
364             produces more comparison pairs than the other algorithms, it is only
365             useful for illustrative purposes.
366              
367             =item 'oddeventrans'
368              
369             Use a naive odd-even transposition sort. This is a primitive sort closely
370             related to bubble sort except it is more parallel. Because other algorithms
371             are more efficient, this sort is included for illustrative purposes.
372              
373             =item 'balanced'
374              
375             This network is described in the 1983 paper "The Balanced Sorting Network"
376             by M. Dowd, Y. Perl, M Saks, and L. Rudolph. It is not a particularly
377             efficient sort but it has some interesting properties due to the fact
378             that it is constructed as a series of successive identical sub-blocks,
379             somewhat like with 'oddeventrans'.
380              
381             =item 'none'
382              
383             Do not generate a set of comparators. Instead, take the set from an
384             outside source, using the C<comparators> option.
385              
386             #
387             # Test our own 5-input network.
388             #
389             @cmptr = ([1,2], [0,2], [0,1], [3,4], [0,3], [1,4], [2,4], [1,3], [2,3]);
390              
391             $nw = Algorithm::Networksort->new(inputs => 5,
392             algorithm => 'none',
393             comparators => [@cmptr]);
394              
395             Internally, this is what L<nwsrt_best()|Algorithm::Networksort::Best/nwsrt_best()>
396             of L<Algorithm::Networksort::Best> uses.
397              
398             =back
399              
400             =cut
401              
402             sub BUILD
403             {
404 66     66 0 71792 my $self = shift;
405 66         1863 my $alg = $self->algorithm();
406 66         1514 my $inputs = $self->inputs();
407              
408 66         63 my @network;
409             my @grouped;
410              
411             #
412             # Catch errors
413             #
414 66 50       134 croak "Input size must be 2 or greater" if ($inputs < 2);
415              
416             #
417             # Providing our own-grown network?
418             #
419 66 50       124 if ($alg eq 'none')
420             {
421 0 0       0 croak "No comparators provided" unless ($self->has_comparators);
422 0         0 $self->length(scalar @{ $self->comparators });
  0         0  
423              
424             #
425             # Algorithm::Networksort::Best will set these, so
426             # only go through with this if this is a user-provided
427             # sequence of comparators.
428             #
429 0 0 0     0 unless ($self->has_network and $self->depth > 0)
430             {
431 0         0 @grouped = $self->group();
432 0         0 $self->network($self->comparators);
433 0         0 $self->depth(scalar @grouped);
434 0         0 $self->network([map { @$_ } @grouped]);
  0         0  
435 0 0       0 $self->title("Unknown $inputs-Inputs Comparator Set") unless ($self->has_title());
436             }
437              
438 0 0       0 $self->nwid("nonalgorithmic-" . sprintf("%02d", $inputs)) unless ($self->has_nwid());
439              
440 0         0 return $self;
441             }
442              
443 66 50       158 croak "Unknown algorithm '$alg'" unless (exists $algname{$alg});
444 66         1749 $self->nwid($alg . sprintf("%02d", $inputs));
445              
446 66 100       129 @network = bosenelson($inputs) if ($alg eq 'bosenelson');
447 66 100       124 @network = hibbard($inputs) if ($alg eq 'hibbard');
448 66 100       114 @network = batcher($inputs) if ($alg eq 'batcher');
449 66 100       128 @network = bitonic($inputs) if ($alg eq 'bitonic');
450 66 100       129 @network = bubble($inputs) if ($alg eq 'bubble');
451 66 100       116 @network = oddeventransposition($inputs) if ($alg eq 'oddeventrans');
452 66 100       146 @network = balanced($inputs) if ($alg eq 'balanced');
453 66 100       110 @network = oddevenmerge($inputs) if ($alg eq 'oddevenmerge');
454              
455 66 50       2039 $self->title($algname{$alg} . " for N = " . $inputs) unless ($self->has_title);
456 66         1586 $self->length(scalar @network);
457 66         1620 $self->comparators(\@network); # The 'raw' list of comparators.
458              
459             #
460             # Re-order the comparator list using the parallel grouping for
461             # the graphs. The resulting parallelism means less stalling
462             # when used in a pipeline.
463             #
464 66         166 @grouped = $self->group();
465              
466             #
467             #### @grouped
468             #
469 66         1773 $self->depth(scalar @grouped);
470 66         88 $self->network([map { @$_ } @grouped]);
  470         2047  
471              
472 66         254 return $self;
473             }
474              
475             =head3 comparators()
476              
477             The comparators in their 'raw' form, as generated by its algorithm.
478              
479             For the comparators re-ordered in such a way as to take advantage
480             of parallelism, see L<network()>.
481              
482             =head3 network()
483              
484             Returns the comparators re-ordered from the 'raw' order, to provide a
485             parallelized version of the comparator list; the best order possible to
486             prevent stalling in a CPU's pipeline.
487              
488             This is the form used when printing the sorting network using L<formats()>.
489              
490             =cut
491              
492             =head3 algorithm_name()
493              
494             Return the full text name of the algorithm, given its key name.
495              
496             =cut
497              
498             sub algorithm_name
499             {
500 0     0 1 0 my $self = shift;
501 0   0     0 my $algthm = $_[0] // $self->algorithm();
502              
503 0 0       0 return $algname{$algthm} if (defined $algthm);
504 0         0 return "";
505             }
506              
507             #
508             # @network = hibbard($inputs);
509             #
510             # Return a list of two-element lists that comprise the comparators of a
511             # sorting network.
512             #
513             # Translated from the ALGOL listed in T. N. Hibbard's article, A Simple
514             # Sorting Algorithm, Journal of the ACM 10:142-50, 1963.
515             #
516             # The ALGOL code was overly dependent on gotos. This has been changed.
517             #
518             sub hibbard
519             {
520 10     10 0 11 my $inputs = shift;
521 10         10 my @comparators;
522 10         10 my($bit, $xbit, $ybit);
523              
524             #
525             # $t = ceiling(log2($inputs - 1)); but we'll
526             # find it using the length of the bitstring.
527             #
528 10         42 my $t = unpack("B32", pack("N", $inputs - 1));
529 10         31 $t =~ s/^0+//;
530 10         10 $t = length $t;
531              
532 10         10 my $lastbit = 1 << $t;
533              
534             #
535             # $x and $y are the comparator endpoints.
536             # We begin with values of zero and one.
537             #
538 10         15 my($x, $y) = (0, 1);
539              
540 10         9 while (1 == 1)
541             {
542             #
543             # Save the comparator pair, and calculate the next
544             # comparator pair.
545             #
546             ### hibbard() top of loop:
547             #### @comparators
548             #
549 144         138 push @comparators, [$x, $y];
550              
551             #
552             # Start with a check of X and Y's respective bits,
553             # beginning with the zeroth bit.
554             #
555 144         88 $bit = 1;
556 144         88 $xbit = $x & $bit;
557 144         84 $ybit = $y & $bit;
558              
559             #
560             # But if the X bit is 1 and the Y bit is
561             # zero, just clear the X bit and move on.
562             #
563 144   100     303 while ($xbit != 0 and $ybit == 0)
564             {
565 55         35 $x &= ~$bit;
566              
567 55         35 $bit <<= 1;
568 55         36 $xbit = $x & $bit;
569 55         99 $ybit = $y & $bit;
570             }
571              
572 144 100       161 if ($xbit != 0) # and $ybit != 0
573             {
574 36         24 $y &= ~$bit;
575 36         23 next;
576             }
577              
578             #
579             # The X bit is zero if we've gotten this far.
580             #
581 108 100       126 if ($ybit == 0)
582             {
583 55         38 $x |= $bit;
584 55         26 $y |= $bit;
585 55 100       68 $y &= ~$bit if ($y > $inputs - 1);
586 55         36 next;
587             }
588              
589             #
590             # The X bit is zero, the Y bit is one, and we might
591             # return the results.
592             #
593             do
594 53         30 {
595 66 100       107 return @comparators if ($bit == $lastbit);
596              
597 56         43 $x &= ~$bit;
598 56         39 $y &= ~$bit;
599              
600 56         33 $bit <<= 1; # Next bit.
601              
602 56 100       66 if ($y & $bit)
603             {
604 19         22 $x &= ~$bit;
605 19         16 next;
606             }
607              
608 37         31 $x |= $bit;
609 37         60 $y |= $bit;
610             } while ($y > $inputs - 1);
611              
612             #
613             # No return, so loop onwards.
614             #
615 24 100       38 $bit = 1 if ($y < $inputs - 1);
616 24         19 $x &= ~$bit;
617 24         20 $y |= $bit;
618             }
619             }
620              
621             #
622             # @network = bosenelson($inputs);
623             #
624             # Return a list of two-element lists that comprise the comparators of a
625             # sorting network.
626             #
627             # The Bose-Nelson algorithm.
628             #
629             sub bosenelson
630             {
631 8     8 0 9 my $inputs = shift;
632              
633 8         14 return bn_split(0, $inputs);
634             }
635              
636             #
637             # @comparators = bn_split($i, $length);
638             #
639             # The helper function that divides the range to be sorted.
640             #
641             # Note that the work of splitting the ranges is performed with the
642             # 'length' variables. The $i variable merely acts as a starting
643             # base, and could easily have been 1 to begin with.
644             #
645             sub bn_split
646             {
647 96     96 0 51 my($i, $length) = @_;
648 96         69 my @comparators = ();
649              
650             #
651             ### bn_split():
652             #### $i
653             #### $length
654             #
655              
656 96 100       114 if ($length >= 2)
657             {
658 44         34 my $mid = $length/2;
659              
660 44         53 push @comparators, bn_split($i, $mid);
661 44         52 push @comparators, bn_split($i + $mid, $length - $mid);
662 44         55 push @comparators, bn_merge($i, $mid, $i + $mid, $length - $mid);
663             }
664              
665             #
666             ### bn_split() returns
667             #### @comparators
668             #
669 96         102 return @comparators;
670             }
671              
672             #
673             # @comparators = bn_merge($i, $length_i, $j, $length_j);
674             #
675             # The other helper function that adds comparators to the list, for a
676             # given pair of ranges.
677             #
678             # As with bn_split, the different conditions all depend upon the
679             # lengths of the ranges. The $i and $j variables merely act as
680             # starting bases.
681             #
682             sub bn_merge
683             {
684 125     125 0 87 my($i, $length_i, $j, $length_j) = @_;
685 125         95 my @comparators = ();
686              
687             #
688             ### bn_merge():
689             #### $i
690             #### $length_i
691             #### $j
692             #### $length_j
693             #
694 125 100 100     411 if ($length_i == 1 && $length_j == 1)
    100 66        
    100 100        
695             {
696 73         73 push @comparators, [$i, $j];
697             }
698             elsif ($length_i == 1 && $length_j == 2)
699             {
700 21         19 push @comparators, [$i, $j + 1];
701 21         22 push @comparators, [$i, $j];
702             }
703             elsif ($length_i == 2 && $length_j == 1)
704             {
705 4         6 push @comparators, [$i, $j];
706 4         6 push @comparators, [$i + 1, $j];
707             }
708             else
709             {
710 27         20 my $i_mid = $length_i/2;
711 27 100       34 my $j_mid = ($length_i & 1)? $length_j/2: ($length_j + 1)/2;
712              
713 27         36 push @comparators, bn_merge($i, $i_mid, $j, $j_mid);
714 27         33 push @comparators, bn_merge($i + $i_mid, $length_i - $i_mid, $j + $j_mid, $length_j - $j_mid);
715 27         31 push @comparators, bn_merge($i + $i_mid, $length_i - $i_mid, $j, $j_mid);
716             }
717              
718             #
719             ### bn_merge() returns
720             #### @comparators
721             #
722 125         138 return @comparators;
723             }
724              
725             #
726             # @network = batcher($inputs);
727             #
728             # Return a list of two-element lists that comprise the comparators of a
729             # sorting network.
730             #
731             # Batcher's sort as laid out in Knuth, Sorting and Searching, algorithm 5.2.2M.
732             #
733             sub batcher
734             {
735 8     8 0 9 my $inputs = shift;
736 8         8 my @network;
737              
738             #
739             # $t = ceiling(log2($inputs)); but we'll
740             # find it using the length of the bitstring.
741             #
742 8         26 my $t = unpack("B32", pack("N", $inputs));
743 8         24 $t =~ s/^0+//;
744 8         10 $t = length $t;
745              
746 8         10 my $p = 1 << ($t -1);
747              
748 8         13 while ($p > 0)
749             {
750 26         17 my $q = 1 << ($t -1);
751 26         21 my $r = 0;
752 26         15 my $d = $p;
753              
754 26         29 while ($d > 0)
755             {
756 57         58 for my $i (0 .. $inputs - $d - 1)
757             {
758 241 100       328 push @network, [$i, $i + $d] if (($i & $p) == $r);
759             }
760              
761 57         29 $d = $q - $p;
762 57         43 $q >>= 1;
763 57         61 $r = $p;
764             }
765 26         32 $p >>= 1;
766             }
767              
768 8         25 return @network;
769             }
770              
771              
772              
773             #
774             # @network = bitonic($inputs);
775             #
776             # Return a list of two-element lists that comprise the comparators of a
777             # sorting network.
778             #
779             # Batcher's Bitonic sort as described here:
780             # http://www.iti.fh-flensburg.de/lang/algorithmen/sortieren/bitonic/oddn.htm
781             #
782             sub bitonic
783             {
784 8     8 0 10 my $inputs = shift;
785 8         10 my @network;
786              
787 8         10 my ($sort, $merge);
788              
789             $sort = sub {
790 96     96   82 my ($lo, $n, $dir) = @_;
791              
792 96 100       124 if ($n > 1) {
793 44         41 my $m = $n/2;
794 44         69 $sort->($lo, $m, !$dir);
795 44         56 $sort->($lo + $m, $n - $m, $dir);
796 44         49 $merge->($lo, $n, $dir);
797             }
798 8         33 };
799              
800             $merge = sub {
801 252     252   191 my ($lo, $n, $dir) = @_;
802              
803 252 100       374 if ($n > 1) {
804             #
805             # $t = ceiling(log2($n - 1)); but we'll
806             # find it using the length of the bitstring.
807             #
808 104         193 my $t = unpack("B32", pack("N", $n - 1));
809 104         229 $t =~ s/^0+//;
810 104         76 $t = length $t;
811              
812 104         81 my $m = 1 << ($t - 1);
813              
814 104         143 for my $i ($lo .. $lo+$n-$m-1)
815             {
816 134 100       283 push @network, ($dir)? [$i, $i+$m]: [$i+$m, $i];
817             }
818              
819 104         147 $merge->($lo, $m, $dir);
820 104         122 $merge->($lo + $m, $n - $m, $dir);
821             }
822 8         33 };
823              
824 8         16 $sort->(0, $inputs, 1);
825              
826 8         8 return @{ make_network_unidirectional(\@network) };
  8         16  
827             }
828              
829              
830             ## This function "re-wires" a bi-directional sorting network
831             ## and turns it into a normal, uni-directional network.
832              
833             sub make_network_unidirectional
834             {
835 8     8 0 9 my ($network_ref) = @_;
836              
837 8         18 my @network = @$network_ref;
838              
839 8         19 for my $i (0..$#network) {
840 134         84 my $comparator = $network[$i];
841 134         99 my ($x, $y) = @$comparator;
842              
843 134 100       171 if ($x > $y) {
844 44         49 for my $j (($i+1)..$#network) {
845 645         386 my $j_comparator = $network[$j];
846 645         389 my ($j_x, $j_y) = @$j_comparator;
847              
848 645 100       670 $j_comparator->[0] = $y if $x == $j_x;
849 645 100       624 $j_comparator->[1] = $y if $x == $j_y;
850 645 100       618 $j_comparator->[0] = $x if $y == $j_x;
851 645 100       718 $j_comparator->[1] = $x if $y == $j_y;
852             }
853 44         53 ($comparator->[0], $comparator->[1]) = ($comparator->[1], $comparator->[0]);
854             }
855             }
856              
857 8         29 return \@network;
858             }
859              
860             #
861             # @network = bubble($inputs);
862             #
863             # Simple bubble sort network, only for comparison purposes.
864             #
865             sub bubble
866             {
867 8     8 0 9 my $inputs = shift;
868 8         10 my @network;
869              
870 8         18 for my $j (reverse 0 .. $inputs - 1)
871             {
872 52         138 push @network, [$_, $_ + 1] for (0 .. $j - 1);
873             }
874              
875 8         26 return @network;
876             }
877              
878             #
879             # @network = bubble($inputs);
880             #
881             # Simple odd-even transposition network, only for comparison purposes.
882             #
883             sub oddeventransposition
884             {
885 8     8 0 10 my $inputs = shift;
886 8         7 my @network;
887              
888             my $odd;
889              
890 8         19 for my $stage (0 .. $inputs - 1)
891             {
892 52 100       90 for (my $j = $odd ? 1 : 0; $j < $inputs - 1; $j += 2)
893             {
894 164         244 push @network, [$j, $j+1];
895             }
896              
897 52         50 $odd = !$odd;
898             }
899              
900 8         27 return @network;
901             }
902              
903             #
904             # @network = balanced($inputs);
905             #
906             # "The Balanced Sorting Network" by M. Dowd, Y. Perl, M Saks, and L. Rudolph
907             # ftp://ftp.cs.rutgers.edu/cs/pub/technical-reports/pdfs/DCS-TR-127.pdf
908             #
909             sub balanced
910             {
911 8     8 0 7 my $inputs = shift;
912 8         8 my @network;
913              
914             #
915             # $t = ceiling(log2($inputs - 1)); but we'll
916             # find it using the length of the bitstring.
917             #
918 8         31 my $t = unpack("B32", pack("N", $inputs - 1));
919 8         25 $t =~ s/^0+//;
920 8         9 $t = length $t;
921              
922 8         16 for (1 .. $t)
923             {
924 24         45 for (my $curr = 2**($t); $curr > 1; $curr /= 2)
925             {
926 76         94 for (my $i = 0; $i < 2**$t; $i += $curr)
927             {
928 216         256 for (my $j = 0; $j < $curr/2; $j++)
929             {
930 416         245 my $wire1 = $i+$j;
931 416         266 my $wire2 = $i+$curr-$j-1;
932 416 100 100     1408 push @network, [$wire1, $wire2]
933             if $wire1 < $inputs && $wire2 < $inputs;
934             }
935             }
936             }
937             }
938              
939 8         36 return @network;
940             }
941              
942             #
943             # @network = oddevenmerge($inputs);
944             #
945             # Batcher's odd-even merge sort as described here:
946             # http://www.iti.fh-flensburg.de/lang/algorithmen/sortieren/networks/oemen.htm
947             # http://cs.engr.uky.edu/~lewis/essays/algorithms/sortnets/sort-net.html
948             #
949             sub oddevenmerge
950             {
951 8     8 0 10 my $inputs = shift;
952 8         9 my @network;
953              
954             #
955             # $t = ceiling(log2($inputs - 1)); but we'll
956             # find it using the length of the bitstring.
957             #
958 8         26 my $t = unpack("B32", pack("N", $inputs - 1));
959 8         26 $t =~ s/^0+//;
960 8         11 $t = length $t;
961              
962 8         4 my ($add_elem, $sort, $merge);
963              
964             $add_elem = sub {
965 212     212   129 my ($i, $j) = @_;
966              
967 212 100 100     670 push @network, [$i, $j]
968             if $i < $inputs && $j < $inputs;
969 8         30 };
970              
971             $sort = sub {
972 136     136   88 my ($lo, $n) = @_;
973              
974 136 100       165 if ($n > 1)
975             {
976 64         47 my $m = int($n / 2);
977              
978 64         88 $sort->($lo, $m);
979 64         56 $sort->($lo + $m, $m);
980 64         67 $merge->($lo, $n, 1);
981             }
982 8         17 };
983              
984             $merge = sub {
985 176     176   114 my ($lo, $n, $r) = @_;
986              
987 176         102 my $m = int($r * 2);
988              
989 176 100       193 if ($m < $n)
990             {
991 56         72 $merge->($lo, $n, $m); # even
992 56         59 $merge->($lo + $r, $n, $m); # odd
993              
994 56         85 for (my $i=$lo + $r; $i + $r < $lo + $n; $i += $m)
995             {
996 92         84 $add_elem->($i, $i + $r);
997             }
998             }
999             else
1000             {
1001 120         118 $add_elem->($lo, $lo + $r);
1002             }
1003 8         19 };
1004              
1005 8         15 $sort->(0, 2**$t);
1006              
1007 8         20 return @network;
1008             }
1009              
1010             #
1011             # $array_ref = $nw->sort(\@array);
1012             #
1013             # Use the network of comparators to sort the elements in the
1014             # array. Returns the reference to the array, which is sorted
1015             # in-place.
1016             #
1017             # This function is for testing and statistical purposes only, as
1018             # interpreting sorting pairs ad hoc in an interpreted language is
1019             # going to be very slow.
1020             #
1021             =head3 sort()
1022              
1023             Sort an array using the network. This is meant for testing purposes
1024             only - looping around an array of comparators in order to sort an
1025             array in an interpreted language is not the most efficient mechanism
1026             for using a sorting network.
1027              
1028             This function uses the C<< <=> >> operator for comparisons.
1029              
1030             my @digits = (1, 8, 3, 0, 4, 7, 2, 5, 9, 6);
1031             my $nw = Algorithm::Networksort->new(
1032             inputs => (scalar @digits),
1033             algorithm => 'hibbard');
1034             $nw->sort(\@digits);
1035             print join(", ", @digits);
1036              
1037             =cut
1038              
1039             sub sort
1040             {
1041 15840     15840 0 92065 my $self = shift;
1042 15840         10366 my $array = $_[0];
1043              
1044 15840         353419 my $network = $self->network();
1045              
1046             #
1047             ### sort():
1048             #### $network
1049             #### $array
1050             #
1051              
1052             #
1053             # Variable $swaps is a global variable that reports back the
1054             # number of exchanges.
1055             #
1056 15840         10746 $swaps = 0;
1057 15840         15913 for my $comparator (@$network)
1058             {
1059 519164         338940 my($left, $right) = @$comparator;
1060              
1061 519164 100       644982 if (($$array[$left] <=> $$array[$right]) == 1)
1062             {
1063 91519         94558 @$array[$left, $right] = @$array[$right, $left];
1064 91519         72842 $swaps++;
1065             }
1066              
1067             #
1068             ##### @$array
1069             #
1070             }
1071              
1072 15840         14794 return $array;
1073             }
1074              
1075             =head3 statistics()
1076              
1077             Return statistics on the last sort() call. Currently only "swaps",
1078             a count of the number of exchanges, is returned.
1079              
1080             my(@d, %nw_stats);
1081             my @digits = (1, 8, 3, 0, 4, 7, 2, 5, 9, 6);
1082             my $inputs = scalar @digits;
1083             my $nw_batcher = Algorithm::Networksort->new(inputs => $inputs, algorithm => 'batcher');
1084             my $nw_bn = Algorithm::Networksort->new(inputs => $inputs, algorithm => 'bosenelson');
1085              
1086             #
1087             # List will wind up sorted, so copy it for our first test run.
1088             #
1089             @d = @digits;
1090             $nw_batcher->sort(\@d);
1091             %nw_stats = $nw_batcher->statistics();
1092             print "The Batcher Merge-Exchange network took ",
1093             $nw_stats{swaps}, " exchanges to sort the array."
1094              
1095             @d = @digits;
1096             $nw_bn->sort(\@d);
1097             %nw_stats = $nw_bn->statistics();
1098             print "The Bose-Nelson network took ",
1099             $nw_stats{swaps}, " exchanges to sort the array."
1100              
1101             =cut
1102              
1103             sub statistics
1104             {
1105 0     0 1 0 return (swaps => $swaps,
1106             );
1107             }
1108              
1109             =head2 Methods For Printing
1110              
1111             The network object by default prints itself in a grouped format; that is
1112              
1113             my $nw = nwsrt(inputs => 4, algorithm => 'bosenelson');
1114             print $nw;
1115              
1116             Will result in the output
1117              
1118             [[0,1], [2,3],
1119             [0,2], [1,3],
1120             [1,2]]
1121              
1122             =head3 formats()
1123              
1124             An array reference of format strings, for use in formatted printing (see
1125             L<formatted()>). You may use as many sprintf-style formats as you like
1126             to form your output.
1127              
1128             $nw->formats([ "swap(%d, %d) ", "if ($card[%d] < $card[%d]);\n" ]);
1129              
1130             =head3 index_base()
1131              
1132             The values to use to reference array indices in formatted printing (see
1133             L<formatted()>). By default, array indices are zero-based. To use a
1134             different index base (most commonly, one-based array indexing), use
1135             this method.
1136              
1137             $nw->index_base([1 .. $inputs]);
1138              
1139             =cut
1140              
1141             sub _dflt_formatted
1142             {
1143 1     1   1 my $self = shift;
1144 1         1 my $network = $_[0];
1145              
1146             #
1147             # Got comparators?
1148             #### $network
1149             #
1150 1 50       2 if (scalar @$network == 0)
1151             {
1152 0         0 carp "No network to format.\n";
1153 0         0 return "";
1154             }
1155              
1156 1         2 my $string = "";
1157 1         24 my $index_base = $self->index_base();
1158              
1159 1         3 for my $cmptr (@$network)
1160             {
1161 5 50       12 @$cmptr = @$index_base[@$cmptr] if (defined $index_base);
1162              
1163 5         10 $string .= "[" . join(",", @$cmptr) . "], ";
1164             }
1165              
1166 1         2 chop $string;
1167 1         2 chop $string;
1168              
1169 1         4 return $string;
1170             }
1171              
1172             #
1173             # _stringify
1174             #
1175             # Show a nicely formatted sorting network.
1176             #
1177             sub _stringify
1178             {
1179 0     0   0 my $self = shift;
1180 0         0 my @grouped = $self->group();
1181 0         0 my $string = "[";
1182              
1183 0         0 for my $grp (@grouped)
1184             {
1185 0         0 $string .= $self->_dflt_formatted($grp) . "\n";
1186             }
1187 0         0 substr($string, -1, 1) = ']'; # Overwrite the trailing "\n".
1188 0         0 return $string;
1189             }
1190              
1191             =head3 formatted()
1192              
1193             $string = $nw->formatted();
1194              
1195             Returns a formatted string that represents the list of comparators.
1196              
1197             If no formats have been provided via the L<formats()> method, the default
1198             format will be used: an array of arrays as represented in Perl.
1199              
1200             Likewise, the network sorting pairs are zero-based. If you want the
1201             pairs written out for some sequence other than 0, 1, 2, ... then you can
1202             provide that using L<inputs_base()>.
1203              
1204             B<Example 0: you want a string in the default format.>
1205              
1206             print $nw->formatted();
1207              
1208             B<Example 1: you want the output to look like the default format, but
1209             one-based instead of zero-based.>
1210              
1211             $nw->input_base([1..$inputs]);
1212             print $nw->formatted();
1213              
1214             B<Example 2: you want a simple list of SWAP macros.>
1215              
1216             $nw->formats([ "SWAP(%d, %d);\n" ]);
1217             print $nw->formatted();
1218              
1219             B<Example 3: as with example 2, but the SWAP values need to be one-based instead of zero-based.>
1220              
1221             $nw->input_base([1..$inputs]);
1222             $nw->formats([ "SWAP(%d, %d);\n" ]);
1223             print $nw->formatted();
1224              
1225             B<Example 4: you want a series of comparison and swap statements.>
1226              
1227             $nw->formats([ "if (v[%d] < v[%d]) then\n",
1228             " exchange(v, %d, %d)\nend if\n" ]);
1229             print $nw->formatted();
1230              
1231             B<Example 5: you want the default format to use letters, not numbers.>
1232              
1233             $nw->input_base( [('a'..'z')[0..$inputs]] );
1234             $nw->formats([ "[%s,%s]," ]); # Note that we're using the string flag.
1235              
1236             my $string = '[' . $nw->formatted();
1237             substr($string, -1, 1) = ']'; # Overwrite the trailing comma.
1238              
1239             print $string, "\n";
1240              
1241             =cut
1242              
1243             sub formatted
1244             {
1245 2     2 1 12 my $self = shift;
1246 2         51 my $network = $self->network();
1247              
1248             #
1249             # Got comparators?
1250             #### $network
1251             #
1252 2 50       6 if (scalar @$network == 0)
1253             {
1254 0         0 carp "No network to format.\n";
1255 0         0 return "";
1256             }
1257              
1258             #
1259             # Got formats?
1260             #
1261 2 100       46 my(@formats) = $self->formats? @{ $self->formats() }: ();
  1         23  
1262 2 100       5 unless (scalar @formats)
1263             {
1264 1         3 return '[' . $self->_dflt_formatted($network) . ']';
1265             }
1266              
1267 1         1 my $string = '';
1268 1         24 my $index_base = $self->index_base();
1269              
1270 1         3 for my $cmptr (@$network)
1271             {
1272 5 50       6 @$cmptr = @$index_base[@$cmptr] if (defined $index_base);
1273              
1274 5         6 for my $fmt (@formats)
1275             {
1276 5         10 $string .= sprintf($fmt, @$cmptr);
1277             }
1278             }
1279              
1280 1         3 return $string;
1281             }
1282              
1283             =head3 group()
1284              
1285             Takes the comparator list and returns a list of comparator lists, each
1286             sub-list representing a group of comparators that can be operate without
1287             interfering with each other, depending on what is needed for
1288             interference-free grouping.
1289              
1290             There is one option available, 'grouping', that will produce a grouping
1291             that represents parallel operations of comparators. Its values may be:
1292              
1293             =over 3
1294              
1295             =item 'graph'
1296              
1297             Group the comnparators as parallel as possible for graphing.
1298              
1299             =item 'parallel'
1300              
1301             Arrange the sequence in parallel so that it has a minimum depth. This,
1302             after flattening the lists into a single list again, is what is used to
1303             produce the sequence in L<network()>.
1304              
1305             =back
1306              
1307             The chances that you will need to use this function are slim, but the
1308             following code snippet may represent an example:
1309              
1310             my $nw = Algorithm::Networksort->new(inputs => 8, algorithm => 'batcher');
1311              
1312             print "There are ", $nw->length(),
1313             " comparators in this network, grouped into\n",
1314             $nw->depth(), " parallel operations.\n\n";
1315              
1316             print $nw, "\n";
1317              
1318             my @grouped_network = $nw->group(grouping=>'graph');
1319             print "\nThis will be graphed in ", scalar @grouped_network,
1320             " columns.\n";
1321              
1322             This will produce:
1323              
1324             There are 19 comparators in this network, grouped into 6 parallel operations.
1325              
1326             [[0,4], [1,5], [2,6], [3,7]]
1327             [[0,2], [1,3], [4,6], [5,7]]
1328             [[2,4], [3,5], [0,1], [6,7]]
1329             [[2,3], [4,5]]
1330             [[1,4], [3,6]]
1331             [[1,2], [3,4], [5,6]]
1332              
1333             This will be graphed in 11 columns.
1334              
1335             =cut
1336              
1337             sub group
1338             {
1339 66     66 1 67 my $self = shift;
1340 66         1720 my $network = $self->comparators;
1341 66         1477 my $inputs = $self->inputs;
1342 66         106 my %opts = @_;
1343              
1344 66         63 my @node_range_stack;
1345             my @node_stack;
1346 66   50     223 my $grp = $opts{grouping} // 'parallel';
1347            
1348             #
1349             # Group the comparator nodes by N.
1350             #
1351 66 50       158 if ($grp =~ /^[0-9]+$/)
1352             {
1353 0         0 my @s = @$network;
1354 0         0 while (scalar @s)
1355             {
1356 0         0 push @node_stack, [splice(@s, 0, $grp)];
1357             }
1358 0         0 return @node_stack;
1359             }
1360              
1361 66 50       200 unless ($grp =~ /^(graph|parallel)$/)
1362             {
1363 0         0 carp "Unknown option '$grp'";
1364 0         0 return undef;
1365             }
1366              
1367             #
1368             # Group the comparator nodes by columns.
1369             #
1370 66         116 for my $comparator (@$network)
1371             {
1372 1197         942 my($from, $to) = @$comparator;
1373              
1374             #
1375             # How much of a column becomes untouchable depends upon whether
1376             # we are trying to print out comparators in a single column, or
1377             # whether we are just trying to arrange comparators in a single
1378             # column without concern for overlap.
1379             #
1380 1197 50       1484 my @range = ($grp eq "parallel")?
1381             ($from, $to):
1382             ($from..$to);
1383 1197         764 my $col = scalar @node_range_stack;
1384              
1385             #
1386             # Search back through the stack of columns to see if
1387             # we can fit the comparator in an existing column.
1388             #
1389 1197         1485 while (--$col >= 0)
1390             {
1391 2161 100       1307 last if (grep{$_ != 0} @{$node_range_stack[$col]}[@range]);
  4322         5596  
  2161         1767  
1392             }
1393              
1394             #
1395             # If even the top column can't fit it in, make a
1396             # new, empty top.
1397             #
1398 1197 100       1415 if (++$col == scalar(@node_range_stack))
1399             {
1400 470         742 push @node_range_stack, [(0) x $inputs];
1401             }
1402              
1403 1197         960 @{$node_range_stack[$col]}[@range] = (1) x (scalar @range);
  1197         1099  
1404              
1405             #
1406             # Autovivification creates the [$col] array element
1407             # if it doesn't currently exist.
1408             #
1409 1197         796 push @{$node_stack[$col]}, $comparator;
  1197         1356  
1410             }
1411              
1412 66         255 return @node_stack;
1413             }
1414              
1415             #
1416             # Set up the horizontal coordinates.
1417             #
1418             sub hz_coords
1419             {
1420 0     0 0   my($columns, %grset) = @_;
1421              
1422 0           my @hcoord = ($grset{hz_margin} + $grset{indent}) x $columns;
1423              
1424 0           for my $idx (0..$columns-1)
1425             {
1426 0           $hcoord[$idx] += $idx * ($grset{hz_sep} + $grset{stroke_width});
1427             }
1428              
1429 0           return @hcoord;
1430             }
1431              
1432             #
1433             # Set up the vertical coordinates.
1434             #
1435             sub vt_coords
1436             {
1437 0     0 0   my($inputs, %grset) = @_;
1438              
1439 0           my @vcoord = ($grset{vt_margin}) x $inputs;
1440              
1441 0           for my $idx (0..$inputs-1)
1442             {
1443 0           $vcoord[$idx] += $idx * ($grset{vt_sep} + $grset{stroke_width});
1444             }
1445              
1446 0           return @vcoord;
1447             }
1448              
1449             =head2 Methods For Graphing
1450              
1451             =head3 graph_eps()
1452              
1453             Returns a string that graphs out the network's comparators. The format
1454             will be encapsulated postscript.
1455              
1456             my $nw = Algorithm::Networksort(inputs = 4, algorithm => 'bitonic');
1457              
1458             print $nw->graph_eps();
1459              
1460             =cut
1461              
1462             sub graph_eps
1463             {
1464 0     0 1   my $self = shift;
1465 0           my $network = $self->network();
1466 0           my $inputs = $self->inputs();
1467 0           my %grset = $self->graphsettings();
1468              
1469 0           my @node_stack = $self->group(grouping => 'graph');
1470 0           my $columns = scalar @node_stack;
1471              
1472             #
1473             # Set up the vertical and horizontal coordinates.
1474             #
1475 0           my @vcoord = vt_coords($inputs, %grset);
1476 0           my @hcoord = hz_coords($columns, %grset);
1477              
1478 0           my $xbound = $hcoord[$columns - 1] + $grset{hz_margin} + $grset{indent};
1479 0           my $ybound = $vcoord[$inputs - 1] + $grset{vt_margin};
1480              
1481             #
1482             # A long involved piece to create the necessary DSC, the subroutine
1483             # definitions, arrays of vertical and horizontal coordinates, and
1484             # left and right margin definitions.
1485             #
1486             my $string =
1487             qq(%!PS-Adobe-3.0 EPSF-3.0\n%%BoundingBox: 0 0 $xbound $ybound\n%%CreationDate: ) .
1488             localtime() .
1489             qq(\n%%Creator: ) . $self->creator() .
1490             qq(\n%%Title: ) . $self->title() .
1491             qq(\n%%Pages: 1\n%%EndComments\n%%Page: 1 1\n) .
1492             q(
1493             % column inputline1 inputline2 draw-comparatorline
1494             /draw-comparatorline {
1495             vcoord exch get 3 1 roll vcoord exch get
1496             3 1 roll hcoord exch get 3 1 roll 2 index exch % x1 y1 x1 y2
1497             newpath 2 copy currentlinewidth 0 360 arc gsave stroke grestore fill moveto
1498             2 copy lineto stroke currentlinewidth 0 360 arc gsave stroke grestore fill
1499             } bind def
1500              
1501             % inputline draw-inputline
1502             /draw-inputline {
1503             vcoord exch get leftmargin exch dup rightmargin exch % x1 y1 x2 y1
1504             newpath 2 copy currentlinewidth 0 360 arc moveto
1505             2 copy lineto currentlinewidth 0 360 arc stroke
1506             } bind def
1507              
1508             ) .
1509             "/vcoord [" .
1510             join("\n ", semijoin(' ', 16, @vcoord)) . "] def\n/hcoord [" .
1511             join("\n ", semijoin(' ', 16, @hcoord)) . "] def\n\n" .
1512             "/leftmargin $grset{hz_margin} def\n/rightmargin " .
1513 0           ($xbound - $grset{hz_margin}) . " def\n\n";
1514              
1515             #
1516             # Save the current graphics state, then change the default line width,
1517             # and the drawing coordinates from (0,0) = lower left to an upper left
1518             # origin.
1519             #
1520 0           $string .= "gsave\n$grset{stroke_width} setlinewidth\n0 $ybound translate\n1 -1 scale\n";
1521              
1522             #
1523             # Draw the input lines.
1524             #
1525 0           $string .= "\n%\n% Draw the input lines.\n%\n0 1 " . ($inputs-1) . " {draw-inputline} for\n";
1526              
1527             #
1528             # Draw our comparators.
1529             # Each member of a group of comparators is drawn in the same column.
1530             #
1531 0           $string .= "\n%\n% Draw the comparator lines.\n%\n";
1532 0           my $hidx = 0;
1533 0           for my $group (@node_stack)
1534             {
1535 0           for my $comparator (@$group)
1536             {
1537 0           $string .= sprintf("%d %d %d draw-comparatorline\n", $hidx, @$comparator);
1538             }
1539 0           $hidx++;
1540             }
1541              
1542 0           $string .= "showpage\ngrestore\n% End of the EPS graph.";
1543 0           return $string;
1544             }
1545              
1546             #svg.pl --background="#9494A4" --indent=15 --hz_sep=22 --hz_margin=16 --vt_margin=20 --stroke_width=4 --radius=3 --algorithm=bosenelson 4
1547              
1548             =head3 graph_svg()
1549              
1550             Returns a string that graphs out the network's comparators.
1551              
1552             $nw = Algorithm::Networksort(inputs => 4, algorithm => 'bitonic');
1553             $svg = $nw->graph_svg();
1554              
1555             The output will use the default colors and sizes, and will be enclosed between
1556             E<lt>svgE<gt> and E<lt>/svgE<gt> tags.
1557              
1558             An example of using the output in an HTML setting:
1559              
1560             $nw = nwsrt_best(name => 'floyd09'); # See Algorithm::Networksort::Best
1561              
1562             $nw->colorsettings(compbegin => '#04c', compend => '#40c');
1563             $svg = $nw->graph_svg();
1564              
1565             =begin html
1566              
1567             <p>Embedded in a web page, this will produce</p>
1568              
1569             <svg xmlns="http://www.w3.org/2000/svg"
1570             xmlns:xlink="http://www.w3.org/1999/xlink" width="278" height="154" viewbox="0 0 278 154">
1571             <defs>
1572             <g id="I_51b6" style="fill:none; stroke-width:2" >
1573             <circle style="stroke:black" cx="18" cy="0" r="2" />
1574             <line style="stroke:black" x1="18" y1="0" x2="260" y2="0" />
1575             <circle style="stroke:black" cx="260" cy="0" r="2" />
1576             </g>
1577              
1578             <g id="C1_51b6" style="stroke-width:2" >
1579             <line style="fill:black; stroke:black" x1="0" y1="0" x2="0" y2="14" />
1580             <circle style="fill:#04c; stroke:#04c" cx="0" cy="0" r="2" />
1581             <circle style="fill:#40c; stroke:#40c" cx="0" cy="14" r="2" />
1582             </g>
1583             <g id="C3_51b6" style="stroke-width:2" >
1584             <line style="fill:black; stroke:black" x1="0" y1="0" x2="0" y2="42" />
1585             <circle style="fill:#04c; stroke:#04c" cx="0" cy="0" r="2" />
1586             <circle style="fill:#40c; stroke:#40c" cx="0" cy="42" r="2" />
1587             </g>
1588             <g id="C2_51b6" style="stroke-width:2" >
1589             <line style="fill:black; stroke:black" x1="0" y1="0" x2="0" y2="28" />
1590             <circle style="fill:#04c; stroke:#04c" cx="0" cy="0" r="2" />
1591             <circle style="fill:#40c; stroke:#40c" cx="0" cy="28" r="2" />
1592             </g>
1593             <g id="C4_51b6" style="stroke-width:2" >
1594             <line style="fill:black; stroke:black" x1="0" y1="0" x2="0" y2="56" />
1595             <circle style="fill:#04c; stroke:#04c" cx="0" cy="0" r="2" />
1596             <circle style="fill:#40c; stroke:#40c" cx="0" cy="56" r="2" />
1597             </g>
1598             </defs>
1599              
1600             <g id="floyd09_51b6">
1601             <use xlink:href="#I_51b6" y="21" /> <use xlink:href="#I_51b6" y="35" />
1602             <use xlink:href="#I_51b6" y="49" /> <use xlink:href="#I_51b6" y="63" />
1603             <use xlink:href="#I_51b6" y="77" /> <use xlink:href="#I_51b6" y="91" />
1604             <use xlink:href="#I_51b6" y="105" /> <use xlink:href="#I_51b6" y="119" />
1605             <use xlink:href="#I_51b6" y="133" />
1606              
1607             <use xlink:href="#C1_51b6" x="27" y="21" /> <use xlink:href="#C1_51b6" x="27" y="63" />
1608             <use xlink:href="#C1_51b6" x="27" y="105" /> <use xlink:href="#C1_51b6" x="41" y="35" />
1609             <use xlink:href="#C1_51b6" x="41" y="77" /> <use xlink:href="#C1_51b6" x="41" y="119" />
1610             <use xlink:href="#C1_51b6" x="55" y="21" /> <use xlink:href="#C1_51b6" x="55" y="63" />
1611             <use xlink:href="#C1_51b6" x="55" y="105" /> <use xlink:href="#C3_51b6" x="69" y="21" />
1612             <use xlink:href="#C3_51b6" x="83" y="63" /> <use xlink:href="#C3_51b6" x="97" y="21" />
1613             <use xlink:href="#C3_51b6" x="111" y="35" /> <use xlink:href="#C3_51b6" x="125" y="77" />
1614             <use xlink:href="#C3_51b6" x="139" y="35" /> <use xlink:href="#C3_51b6" x="153" y="49" />
1615             <use xlink:href="#C3_51b6" x="167" y="91" /> <use xlink:href="#C3_51b6" x="181" y="49" />
1616             <use xlink:href="#C2_51b6" x="195" y="35" /> <use xlink:href="#C2_51b6" x="195" y="91" />
1617             <use xlink:href="#C4_51b6" x="209" y="49" /> <use xlink:href="#C2_51b6" x="223" y="77" />
1618             <use xlink:href="#C2_51b6" x="237" y="49" /> <use xlink:href="#C1_51b6" x="237" y="91" />
1619             <use xlink:href="#C1_51b6" x="251" y="49" />
1620             </g>
1621             </svg>
1622              
1623             =end html
1624              
1625             =cut
1626              
1627             sub graph_svg
1628             {
1629 0     0 1   my $self = shift;
1630 0           my $network = $self->network();
1631 0           my $inputs = $self->inputs();
1632 0           my %grset = $self->graphsettings();
1633              
1634             #
1635             # The 'salt' is used to ensure that the id attributes
1636             # are unique -- I got bit by this when I put two SVG
1637             # images in the same page.
1638             #
1639 0           my $salt = "_" . sprintf("%x", int(rand(0x7fff)));
1640              
1641 0           my @node_stack = $self->group(grouping => 'graph');
1642 0           my $columns = scalar @node_stack;
1643              
1644             #
1645             # Get the colorset, using the foreground color as the default color
1646             # for drawing.
1647             #
1648 0   0       my %clrset = map{$_ => ($colorset{$_} // $colorset{foreground} // 'black')} keys %colorset;
  0   0        
1649              
1650             #
1651             # Set up the vertical and horizontal coordinates.
1652             #
1653 0           my @vcoord = vt_coords($inputs, %grset);
1654 0           my @hcoord = hz_coords($columns, %grset);
1655              
1656 0           my $xbound = $hcoord[$columns - 1] + $grset{hz_margin} + $grset{indent};
1657 0           my $ybound = $vcoord[$inputs - 1] + $grset{vt_margin};
1658              
1659 0           my $right_margin = $hcoord[$columns - 1] + $grset{indent};
1660 0           my $radius = $grset{radius};
1661              
1662 0           my $string = qq(<svg xmlns="http://www.w3.org/2000/svg"\n) .
1663             qq( xmlns:xlink="http://www.w3.org/1999/xlink" ) .
1664             qq(width="$xbound" height="$ybound" viewbox="0 0 $xbound $ybound">\n) .
1665             qq( <title>) . $self->title() . qq(</title>\n) .
1666             qq( <desc>\n CreationDate: ) . localtime() .
1667             qq(\n Creator: ) . $self->creator() . qq(\n </desc>\n);
1668              
1669             #
1670             # Set a background color by inserting as the first element a <rect>
1671             # with the full size of the view and a fill of the desired color.
1672             # Use %colorset instead of %clrset, since we've just torpedoed
1673             # the background value if it was an undef.
1674             #
1675 0 0         if (defined $colorset{background})
1676             {
1677 0           my $filler = $colorset{background};
1678 0           $string .= qq( <rect width="100%" height="100%" fill="$filler" />\n);
1679             }
1680              
1681             #
1682             # Set up the input line template.
1683             #
1684 0           my $g_style = "style=\"fill:none; stroke-width:$grset{stroke_width}\"";
1685 0           my $b_style = "style=\"stroke:$clrset{inputbegin}\"";
1686 0           my $l_style = "style=\"stroke:$clrset{inputline}\"";
1687 0           my $e_style = "style=\"stroke:$clrset{inputend}\"";
1688              
1689 0           $string .=
1690             qq( <defs>\n) .
1691             qq( <!-- Define the input line template. -->\n) .
1692             qq( <g id="I$salt" $g_style >\n) .
1693             qq( <desc>Input line</desc>\n) .
1694             qq( <line $l_style x1="$grset{hz_margin}" y1="0" x2="$right_margin" y2="0" />\n) .
1695             qq( <circle $b_style cx="$grset{hz_margin}" cy="0" r="$radius" />\n) .
1696             qq( <circle $e_style cx="$right_margin" cy="0" r="$radius" />\n) .
1697             qq( </g>\n\n);
1698              
1699             #
1700             # Set up the comparator templates.
1701             #
1702 0           $string .= qq( <!-- Define the comparator lines, which vary in length. -->\n);
1703              
1704 0           $g_style = "style=\"stroke-width:$grset{stroke_width}\"";
1705              
1706 0           my @cmptr = (0) x $inputs;
1707 0           for my $comparator (@$network)
1708             {
1709 0           my($from, $to) = @$comparator;
1710 0           my $clen = $to - $from;
1711 0 0         if ($cmptr[$clen] == 0)
1712             {
1713 0           my $endpoint = $vcoord[$to] - $vcoord[$from];
1714 0           $cmptr[$clen] = 1;
1715              
1716             #
1717             # Color the components in the group individually.
1718             #
1719 0           $b_style = "style=\"fill:$clrset{compbegin}; stroke:$clrset{compbegin}\"";
1720 0           $l_style = "style=\"fill:$clrset{compline}; stroke:$clrset{compline}\"";
1721 0           $e_style = "style=\"fill:$clrset{compend}; stroke:$clrset{compend}\"";
1722              
1723 0           $string .=
1724             qq( <g id="C$clen$salt" $g_style >\n) .
1725             qq( <desc>Comparator size $clen</desc>\n) .
1726             qq( <line $l_style x1="0" y1="0" x2="0" y2="$endpoint" />\n) .
1727             qq( <circle $b_style cx="0" cy="0" r="$radius" />\n) .
1728             qq( <circle $e_style cx="0" cy="$endpoint" r="$radius" />\n) .
1729             qq( </g>\n);
1730             }
1731             }
1732              
1733 0           $string .= qq( </defs>\n\n);
1734              
1735             #
1736             # End of definitions. Draw the input lines as a group.
1737             #
1738 0           $string .= qq( <g id=") . $self->nwid() . qq($salt">\n);
1739 0           $string .= qq( <!-- Draw the input lines. -->\n);
1740 0           $string .= qq( <use xlink:href="#I$salt" y="$vcoord[$_]" />\n) for (0..$inputs-1);
1741              
1742             #
1743             # Draw our comparators.
1744             # Each member of a group of comparators is drawn in the same column.
1745             #
1746 0           $string .= qq(\n <!-- Draw the comparator lines. -->\n);
1747 0           my $hidx = 0;
1748 0           for my $group (@node_stack)
1749             {
1750 0           my $h = $hcoord[$hidx++];
1751              
1752 0           for my $comparator (@$group)
1753             {
1754 0           my($from, $to) = @$comparator;
1755 0           my $clen = $to - $from;
1756 0           my $v = $vcoord[$from];
1757              
1758 0           $string .= qq( <!-- [$from,$to] -->) .
1759             qq(<use xlink:href="#C$clen$salt" x="$h" y="$v" />\n);
1760             }
1761             }
1762              
1763 0           $string .= qq( </g>\n</svg>\n);
1764 0           return $string;
1765             }
1766              
1767             =head3 graph_text()
1768              
1769             Returns a string that graphs out the network's comparators in plain text.
1770              
1771             my $nw = Algorithm::Networksort(inputs = 4, algorithm => 'bitonic');
1772              
1773             print $nw->graph_text();
1774              
1775             This will produce
1776              
1777             o--^-----^--^--o
1778             | | |
1779             o--v--^--|--v--o
1780             | |
1781             o--^--v--|--^--o
1782             | | |
1783             o--v-----v--v--o
1784              
1785              
1786             =cut
1787              
1788             sub graph_text
1789             {
1790 0     0 1   my $self = shift;
1791 0           my $network = $self->network();
1792 0           my $inputs = $self->inputs();
1793 0           my %txset = $self->graphsettings();
1794              
1795 0           my @node_stack = $self->group(grouping => 'graph');
1796 0           my @inuse_nodes;
1797              
1798             #
1799             # Set up a matrix of the begin and end points found in each column.
1800             # This will tell us where to draw our comparator lines.
1801             #
1802 0           for my $group (@node_stack)
1803             {
1804 0           my @node_column = (0) x $inputs;
1805              
1806 0           for my $comparator (@$group)
1807             {
1808 0           my($from, $to) = @$comparator;
1809 0           @node_column[$from, $to] = (1, -1);
1810             }
1811 0           push @inuse_nodes, [splice @node_column, 0];
1812             }
1813              
1814             #
1815             # Print that network.
1816             #
1817 0           my $column = scalar @node_stack;
1818 0           my @column_line = (0) x $column;
1819 0           my $string = "";
1820              
1821 0           for my $row (0..$inputs-1)
1822             {
1823             #
1824             # Begin with the input line...
1825             #
1826 0           $string .= $txset{inputbegin};
1827              
1828 0           for my $col (0..$column-1)
1829             {
1830 0           my @node_column = @{$inuse_nodes[$col]};
  0            
1831              
1832 0 0         if ($node_column[$row] == 0)
    0          
1833             {
1834 0 0         $string .= $txset{($column_line[$col] == 1)?
1835             'inputcompline': 'inputline'};
1836             }
1837             elsif ($node_column[$row] == 1)
1838             {
1839 0           $string .= $txset{compbegin};
1840             }
1841             else
1842             {
1843 0           $string .= $txset{compend};
1844             }
1845 0           $column_line[$col] += $node_column[$row];
1846             }
1847              
1848 0           $string .= $txset{inputend};
1849              
1850             #
1851             # Now print the space in between input lines.
1852             #
1853 0 0         if ($row != $inputs-1)
1854             {
1855 0           $string .= $txset{gapbegin};
1856              
1857 0           for my $col (0..$column -1)
1858             {
1859 0 0         $string .= $txset{($column_line[$col] == 0)?
1860             'gapnone': 'gapcompline'};
1861             }
1862              
1863 0           $string .= $txset{gapend};
1864             }
1865             }
1866              
1867 0           return $string;
1868             }
1869              
1870             =head3 colorsettings()
1871              
1872             Sets the colors of the graph parts, currently for SVG output only.
1873              
1874             The parts are named.
1875              
1876             my %old_colors = $nw->colorsettings(inputbegin => "#c04", inputend => "#c40");
1877              
1878             =over 4
1879              
1880             =item 'inputbegin'
1881              
1882             Opening of input line.
1883              
1884             =item 'inputline'
1885              
1886             The input line.
1887              
1888             =item 'inputend'
1889              
1890             Closing of the input line.
1891              
1892             =item 'compbegin'
1893              
1894             Opening of the comparator.
1895              
1896             =item 'compline'
1897              
1898             The comparator line.
1899              
1900             =item 'compend'
1901              
1902             Closing of the comparator line.
1903              
1904             =item 'foreground'
1905              
1906             Default color for the graph as a whole.
1907              
1908             =item 'background'
1909              
1910             Color of the background. Currently unimplemented in SVG.
1911              
1912             =back
1913              
1914             All parts not named are reset to 'undef', and will be colored with the
1915             default 'foreground' color. The foreground color itself has a default
1916             value of 'black'. The one exception is the 'background' color, which
1917             has no default color at all.
1918              
1919             =cut
1920              
1921             sub colorsettings
1922             {
1923 0     0 1   my $self = shift;
1924 0           my %settings = @_;
1925 0           my %old_settings;
1926              
1927 0 0         return %colorset if (scalar @_ == 0);
1928              
1929 0           for my $k (keys %settings)
1930             {
1931             #
1932             # If it's a real part to color, save the
1933             # old value, then set it.
1934             #
1935 0 0         if (exists $colorset{$k})
1936             {
1937 0           $old_settings{$k} = $colorset{$k};
1938 0           $colorset{$k} = $settings{$k};
1939             }
1940             else
1941             {
1942 0           carp "colorsettings(): Unknown key '$k'";
1943             }
1944             }
1945              
1946 0           return %old_settings;
1947             }
1948              
1949             =head3 graphsettings()
1950              
1951             Alter the graphing settings, be it pixel measurements
1952             or ascii-art characters.
1953              
1954             #
1955             # Set hz_margin, saving its old value for later.
1956             #
1957             my %old_gset = $nw->graphsettings(hz_margin => 12);
1958              
1959             =head4 Options for graph_svg() and graph_eps():
1960              
1961             SVG measurements are in pixels.
1962              
1963             =over 3
1964              
1965             =item 'hz_margin
1966              
1967             I<Default value: 18.>
1968             The horizontal spacing between the edges of the graphic and the
1969             sorting network.
1970              
1971             =item 'hz_sep
1972              
1973             I<Default value: 12.>
1974             The spacing separating the horizontal lines (the input lines).
1975              
1976             =item 'indent'
1977              
1978             I<Default value: 9.>
1979             The indention between the start of the input lines and the placement of
1980             the first comparator. The same value spaces the placement of the final
1981             comparator and the end of the input lines.
1982              
1983             =item 'radius'
1984              
1985             I<Default value: 2.>
1986             Radii of the circles used to end the comparator and input lines.
1987              
1988             =item 'stroke_width
1989              
1990             I<Default value: 2.>
1991             Width of the lines used to define comparators and input lines.
1992              
1993             =item 'vt_margin
1994              
1995             I<Default value: 21.>
1996             The vertical spacing between the edges of the graphic and the sorting network.
1997              
1998             =item 'vt_sep
1999              
2000             I<Default value: 12.>
2001             The spacing separating the vertical lines (the comparators).
2002              
2003             =back
2004              
2005             =head4 Options for graph_text():
2006              
2007             =over 3
2008              
2009             =item 'inputbegin'
2010              
2011             I<Default value: "o-".>
2012             The starting characters for the input line.
2013              
2014             =item 'inputline'
2015              
2016             I<Default value: "---".>
2017             The characters that make up an input line.
2018              
2019             =item 'inputcompline'
2020              
2021             I<Default value: "-|-".>
2022             The characters that make up an input line that has a comparator crossing
2023             over it.
2024              
2025             =item 'inputend'
2026              
2027             I<Default value: "-o\n".>
2028             The characters that make up the end of an input line.
2029              
2030             =item 'compbegin'
2031              
2032             I<Default value: "-^-".>
2033             The characters that make up an input line with the starting point of
2034             a comparator.
2035              
2036             =item 'compend'
2037              
2038             I<Default value: "-v-".>
2039             The characters that make up an input line with the end point of
2040             a comparator.
2041              
2042             =item 'gapbegin'
2043              
2044             I<Default value: " " (two spaces).>
2045             The characters that start the gap between the input lines.
2046              
2047             =item 'gapcompline'
2048              
2049             I<Default value: " | " (space vertical bar space).>
2050             The characters that make up the gap with a comparator passing through.
2051              
2052             =item 'gapnone'
2053              
2054             I<Default value: " " (three spaces).>
2055             The characters that make up the space between the input lines.
2056              
2057             =item 'gapend'
2058              
2059             I<Default value: " \n" (two spaces and a newline).>
2060             The characters that end the gap between the input lines.
2061              
2062             =back
2063              
2064             =cut
2065              
2066             sub graphsettings
2067             {
2068 0     0 1   my $self = shift;
2069 0           my %settings = @_;
2070 0           my %old_settings;
2071              
2072 0 0         return %graphset if (scalar @_ == 0);
2073              
2074 0           for my $k (keys %settings)
2075             {
2076             #
2077             # If it's a real part to graph, save the
2078             # old value, then set it.
2079             #
2080 0 0         if (exists $graphset{$k})
2081             {
2082 0           $old_settings{$k} = $graphset{$k};
2083 0           $graphset{$k} = $settings{$k};
2084             }
2085             else
2086             {
2087 0           carp "graphsettings(): Unknown key '$k'";
2088             }
2089             }
2090              
2091 0           return %old_settings;
2092             }
2093              
2094             #
2095             # @newlist = semijoin($expr, $itemcount, @list);
2096             #
2097             # $expr - A string to be used in a join() call.
2098             # $itemcount - The number of items in a list to be joined.
2099             # It may be negative.
2100             # @list - The list
2101             #
2102             # Create a new list by performing a join on I<$itemcount> elements at a
2103             # time on the original list. Any leftover elements from the end of the
2104             # list become the last item of the new list, unless I<$itemcount> is
2105             # negative, in which case the first item of the new list is made from the
2106             # leftover elements from the front of the list.
2107             #
2108             sub semijoin
2109             {
2110 0     0 0   my($jstr, $itemcount, @oldlist) = @_;
2111 0           my(@newlist);
2112              
2113 0 0 0       return @oldlist if ($itemcount <= 1 and $itemcount >= -1);
2114              
2115 0 0         if ($itemcount > 0)
2116             {
2117 0           push @newlist, join $jstr, splice(@oldlist, 0, $itemcount)
2118             while @oldlist;
2119             }
2120             else
2121             {
2122 0           $itemcount = -$itemcount;
2123 0           unshift @newlist, join $jstr, splice(@oldlist, -$itemcount, $itemcount)
2124             while $itemcount <= @oldlist;
2125 0 0         unshift @newlist, join $jstr, splice( @oldlist, 0, $itemcount)
2126             if @oldlist;
2127             }
2128              
2129 0           return @newlist;
2130             }
2131              
2132             1;
2133             __END__
2134              
2135             =head1 ACKNOWLEDGMENTS
2136              
2137             L<Doug Hoyte|https://github.com/hoytech> provided the code for the bitonic,
2138             odd-even merge, odd-even transposition, balanced, and bubble sort algorithms,
2139             and the idea for what became the L<statistics()> method.
2140              
2141             L<Morwenn|https://github.com/Morwenn> found documentation errors and networks
2142             that went into L<Algorithm::Networksort::Best>.
2143              
2144             =head1 SEE ALSO
2145              
2146             =head2 Bose and Nelson's algorithm.
2147              
2148             =over 3
2149              
2150             =item
2151              
2152             Bose and Nelson, "A Sorting Problem", Journal of the ACM, Vol. 9, 1962, pp. 282-296.
2153              
2154             =item
2155              
2156             Joseph Celko, "Bose-Nelson Sort", Doctor Dobb's Journal, September 1985.
2157              
2158             =item
2159              
2160             Frederick Hegeman, "Sorting Networks", The C/C++ User's Journal, February 1993.
2161              
2162             =item
2163              
2164             Joe Celko, I<Joe Celko's SQL For Smarties> (third edition). Implementing Bose-Nelson sorting network in SQL.
2165              
2166             This material isn't in either the second or fourth edition of the book.
2167              
2168             =item
2169              
2170             Joe Celko, I<Joe Celko's Thinking in Sets: Auxiliary, Temporal, and Virtual Tables in SQL>.
2171              
2172             The sorting network material removed from the third edition of I<SQL For Smarties> seems to have been moved to this book.
2173              
2174             =back
2175              
2176             =head2 Hibbard's algorithm.
2177              
2178             =over 3
2179              
2180             =item
2181              
2182             T. N. Hibbard, "A Simple Sorting Algorithm", Journal of the ACM Vol. 10, 1963, pp. 142-50.
2183              
2184             =back
2185              
2186             =head2 Batcher's Merge Exchange algorithm.
2187              
2188             =over 3
2189              
2190             =item
2191              
2192             Code for Kenneth Batcher's Merge Exchange algorithm was derived from Knuth's
2193             The Art of Computer Programming, Vol. 3, section 5.2.2.
2194              
2195             =back
2196              
2197             =head2 Batcher's Bitonic algorithm
2198              
2199             =over 3
2200              
2201             =item
2202              
2203             Kenneth Batcher, "Sorting Networks and their Applications", Proc. of the
2204             AFIPS Spring Joint Computing Conf., Vol. 32, 1968, pp. 307-3114. A PDF of
2205             this article may be found at L<http://www.cs.kent.edu/~batcher/sort.pdf>.
2206              
2207             The paper discusses both the Odd-Even Merge algorithm and the Bitonic algorithm.
2208              
2209             =item
2210              
2211             Dr. Hans Werner Lang has written a detailed discussion of the bitonic
2212             sort algorithm here:
2213             L<http://www.iti.fh-flensburg.de/lang/algorithmen/sortieren/bitonic/bitonicen.htm>
2214              
2215             =item
2216              
2217             T. H. Cormen, E. E. Leiserson, R. L. Rivest, Introduction to Algorithms,
2218             first edition, McGraw-Hill, 1990, section 28.3.
2219              
2220             =item
2221              
2222             T. H. Cormen, E. E. Leiserson, R. L. Rivest, C. Stein, Introduction to Algorithms,
2223             2nd edition, McGraw-Hill, 2001, section 27.3.
2224              
2225             =back
2226              
2227             =head2 Algorithm discussion
2228              
2229             =over 3
2230              
2231             =item
2232              
2233             Donald E. Knuth, The Art of Computer Programming, Vol. 3: (2nd ed.)
2234             Sorting and Searching, Addison Wesley Longman Publishing Co., Inc.,
2235             Redwood City, CA, 1998.
2236              
2237             =item
2238              
2239             Kenneth Batcher's web site (L<http://www.cs.kent.edu/~batcher/>) lists
2240             his publications, including his paper listed above.
2241              
2242             =back
2243              
2244             =head1 AUTHOR
2245              
2246             John M. Gamble may be found at B<jgamble@cpan.org>
2247              
2248             =cut