File Coverage

lib/Math/Combinatorics.pm
Criterion Covered Total %
statement 289 322 89.7
branch 63 82 76.8
condition 21 32 65.6
subroutine 22 25 88.0
pod 19 19 100.0
total 414 480 86.2


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Math::Combinatorics - Perform combinations and permutations on lists
4              
5             =head1 SYNOPSIS
6              
7             Available as an object oriented API.
8              
9             use Math::Combinatorics;
10              
11             my @n = qw(a b c);
12             my $combinat = Math::Combinatorics->new(count => 2,
13             data => [@n],
14             );
15              
16             print "combinations of 2 from: ".join(" ",@n)."\n";
17             print "------------------------".("--" x scalar(@n))."\n";
18             while(my @combo = $combinat->next_combination){
19             print join(' ', @combo)."\n";
20             }
21              
22             print "\n";
23              
24             print "permutations of 3 from: ".join(" ",@n)."\n";
25             print "------------------------".("--" x scalar(@n))."\n";
26             while(my @permu = $combinat->next_permutation){
27             print join(' ', @permu)."\n";
28             }
29              
30             output:
31              
32             Or available via exported functions 'permute', 'combine', and 'factorial'.
33              
34             use Math::Combinatorics;
35              
36             my @n = qw(a b c);
37             print "combinations of 2 from: ".join(" ",@n)."\n";
38             print "------------------------".("--" x scalar(@n))."\n";
39             print join("\n", map { join " ", @$_ } combine(2,@n)),"\n";
40             print "\n";
41             print "permutations of 3 from: ".join(" ",@n)."\n";
42             print "------------------------".("--" x scalar(@n))."\n";
43             print join("\n", map { join " ", @$_ } permute(@n)),"\n";
44              
45              
46             Output:
47              
48             combinations of 2 from: a b c
49             ------------------------------
50             a b
51             a c
52             b c
53              
54             permutations of 3 from: a b c
55             ------------------------------
56             a b c
57             a c b
58             b a c
59             b c a
60             c a b
61             c b a
62              
63             Output from both types of calls is the same, but the object-oriented approach consumes
64             much less memory for large sets.
65              
66             =head1 DESCRIPTION
67              
68             Combinatorics is the branch of mathematics studying the enumeration, combination,
69             and permutation of sets of elements and the mathematical relations that characterize
70             their properties. As a jumping off point, refer to:
71              
72             http://mathworld.wolfram.com/Combinatorics.html
73              
74             This module provides a pure-perl implementation of nCk, nCRk, nPk, nPRk, !n and n!
75             (combination, multiset, permutation, string, derangement, and factorial, respectively).
76             Functional and object-oriented usages allow problems such as the following to be solved:
77              
78             =over
79              
80             =item combine - nCk
81              
82             http://mathworld.wolfram.com/Combination.html
83              
84             "Fun questions to ask the pizza parlor wait staff: how many possible combinations
85             of 2 toppings can I get on my pizza?".
86              
87             =item derange - !n
88              
89             http://mathworld.wolfram.com/Derangement.html
90              
91             "A derangement of n ordered objects, denoted !n, is a permutation in which none of the
92             objects appear in their "natural" (i.e., ordered) place."
93              
94             =item permute - nPk
95              
96             http://mathworld.wolfram.com/Permutation.html
97              
98             "Master Mind Game: ways to arrange pieces of different colors in a
99             certain number of positions, without repetition of a color".
100              
101             =back
102              
103             Object-oriented usage additionally allows solving these problems by calling L
104             with a B vector:
105              
106             =over
107              
108             =item string - nPRk
109              
110             http://mathworld.wolfram.com/String.html
111              
112             "Morse signals: diferent signals of 3 positions using the two symbols - and .".
113              
114             $o = Math::Combinatorics->new( count=>3 , data=>[qw(. -)] , frequency=>[3,3] );
115             while ( my @x = $o->next_multiset ) {
116             my $p = Math::Combinatorics->new( data=>\@x , frequency=>[map{1} @x] );
117             while ( my @y = $p->next_string ) {
118             #do something
119             }
120             }
121              
122             =item multiset/multichoose - nCRk
123              
124             http://mathworld.wolfram.com/Multiset.html
125              
126             "ways to extract 3 balls at once of a bag with 3 black and 3 white balls".
127              
128             $o = Math::Combinatorics->new( count=>3 , data=>[qw(white black)] , frequency=>[3,3] );
129             while ( my @x = $o->next_multiset ) {
130             #do something
131             }
132              
133             =back
134              
135             =head2 EXPORT
136              
137             the following export tags will bring a single method into the caller's
138             namespace. no symbols are exported by default. see pod documentation below for
139             method descriptions.
140              
141             combine
142             derange
143             multiset
144             permute
145             string
146             factorial
147              
148             =head1 AUTHOR
149              
150             Allen Day , with algorithmic contributions from Christopher Eltschka and
151             Tye.
152              
153             Copyright (c) 2004-2005 Allen Day. All rights reserved. This program is free software; you
154             can redistribute it and/or modify it under the same terms as Perl itself.
155              
156             =head1 ACKNOWLEDGEMENTS
157              
158             A sincere thanks to everyone for helping to make this a better module. After initial
159             development I've only had time to accept patches and improvements. Math::Combinatorics
160             continues to be developed and improved by the community. Contributors of note include:
161              
162             For adding new features: Carlos Rica, David Coppit, Carlos Segre, Lyon Lemmens
163              
164             For bug reports: Ying Yang, Joerg Beyer, Marc Logghe, Yunheng Wang,
165             Torsten Seemann, Gerrit Haase, Joern Behre, Lyon Lemmens, Federico Lucifredi
166              
167             =head1 BUGS / TODO
168              
169             Report them to the author.
170              
171             * Need more extensive unit tests.
172              
173             * tests for new()'s frequency argment
174              
175             * A known bug (more of a missing feature, actually) does not allow parameterization of k
176             for nPk in permute(). it is assumed k == n. L for details. You can work
177             around this by making calls to both L and L
178              
179             * Lots of really interesting stuff from Mathworld.Wolfram.com. MathWorld rocks! Expect
180             to see implementation of more concepts from their site, e.g.:
181              
182             http://mathworld.wolfram.com/BellNumber.html
183             http://mathworld.wolfram.com/StirlingNumberoftheSecondKind.html
184             http://mathworld.wolfram.com/Word.html
185              
186             * Other combinatorics stuff
187             http://en.wikipedia.org/wiki/Catalan_number
188             http://en.wikipedia.org/wiki/Stirling_number
189              
190             =head1 SEE ALSO
191              
192             L
193              
194             L
195              
196             L (alas misnamed, it actually returns permutations on a string).
197              
198             http://perlmonks.thepen.com/29374.html
199              
200             http://groups.google.com/groups?selm=38568F79.13680B86%40physik.tu-muenchen.de&output=gplain
201              
202              
203             =cut
204              
205             package Math::Combinatorics;
206              
207 3     3   162407 use strict;
  3         8  
  3         115  
208 3     3   16 use Data::Dumper;
  3         6  
  3         13169  
209             require Exporter;
210              
211             our @ISA = qw(Exporter);
212             our @EXPORT = qw( combine derange factorial permute );
213             our $VERSION = '0.09';
214              
215             =head1 EXPORTED FUNCTIONS
216              
217             =head2 combine()
218              
219             Usage : my @combinations = combine($k,@n);
220             Function: implements nCk (n choose k), or n!/(k!*(n-k!)).
221             returns all unique unorderd combinations of k items from set n.
222             items in n are assumed to be character data, and are
223             copied into the return data structure (see "Returns" below).
224             Example : my @n = qw(a b c);
225             my @c = combine(2,@n);
226             print join "\n", map { join " ", @$_ } @c;
227             # prints:
228             # b c
229             # a c
230             # a b
231             Returns : a list of arrays, where each array contains a unique combination
232             of k items from n
233             Args : a list of items to be combined
234             Notes : data is internally assumed to be alphanumeric. this is necessary
235             to efficiently generate combinations of large sets. if you need
236             combinations of non-alphanumeric data, or on data
237             C would not be appropriate, use the
238             object-oriented API. See L and the B option.
239              
240             Identical items are assumed to be non-unique. That is, calling
241             C
242             L
243              
244             =cut
245              
246             sub combine {
247 2     2 1 6721 my($k,@n) = @_;
248              
249 2         6 my @result = ();
250              
251 2         17 my $c = __PACKAGE__->new(data => [@n], count => $k);
252 2         9 while(my(@combo) = $c->next_combination){
253 10         41 push @result, [@combo];
254             }
255              
256 2         25 return @result;
257             }
258              
259             =head2 derange()
260              
261             Usage : my @deranges = derange(@n);
262             Function: implements !n, a derangement of n items in which none of the
263             items appear in their originally ordered place.
264             Example : my @n = qw(a b c);
265             my @d = derange(@n);
266             print join "\n", map { join " ", @$_ } @d;
267             # prints:
268             # a c b
269             # b a c
270             # b c a
271             # c a b
272             # c b a
273             Returns : a list of arrays, where each array contains a derangement of
274             k items from n (where k == n).
275             Args : a list of items to be deranged.
276             Note : k should really be parameterizable. this will happen
277             in a later version of the module. send me a patch to
278             make that version come out sooner.
279             Notes : data is internally assumed to be alphanumeric. this is necessary
280             to efficiently generate combinations of large sets. if you need
281             combinations of non-alphanumeric data, or on data
282             C would not be appropriate, use the
283             object-oriented API. See L, and the B option.
284              
285             =cut
286              
287             sub derange {
288 1     1 1 279 my(@n) = @_;
289              
290 1         3 my @result = ();
291              
292 1         4 my $c = __PACKAGE__->new(data => [@n]);
293 1         5 while(my(@derange) = $c->next_derangement){
294 9         44 push @result, [@derange];
295             }
296              
297 1         18 return @result;
298             }
299              
300             =head2 next_derangement()
301              
302             Usage : my @derangement = $c->next_derangement();
303             Function: get derangements for @data.
304             Returns : returns a permutation of items from @data (see L),
305             where none of the items appear in their natural order. repeated calls
306             retrieve all unique derangements of @data elements. a returned empty
307             list signifies all derangements have been iterated.
308             Args : none.
309              
310             =cut
311              
312             sub next_derangement {
313 20     20 1 62 my $self = shift;
314 20         38 my $data = $self->data();
315              
316 20         45 my $cursor = $self->_permutation_cursor();
317 20         27 my $values = @$cursor;
318 20 100       42 if($self->{pin}){
319 2         4 $self->{pin} = 0;
320              
321 2         2 my $i;
322 2         8 for ($i = 1; $i < $values; $i += 2) {
323 4         7 $$cursor[$i - 1] = $i;
324 4         12 $$cursor[$i] = $i - 1;
325             }
326 2 50       12 if ($values % 2 != 0) {
327 0         0 $$cursor[$values - 1] = $values - 3;
328 0         0 $$cursor[$values - 2] = $values - 1;
329             }
330 2         57 goto RESULT;
331             }
332             else {
333 18         19 my $values = @$cursor;
334 18         17 my $i;
335             my @found; # stores for each element if it has been found previously
336 18         36 for ($i = 0; $i < $values; $i++) { $found[$i] = 0 }
  72         139  
337 18         17 my $e;
338 18         21 my $elemfound = 0;
339 18         38 for ($i = $values - 1; $i > -1; $i--) {
340 56         64 $found[$$cursor[$i]] = 1;
341 56 100       86 if ($i > $values - 3) { # $values-1 or $values-2
342 36 100       58 if ($i == $values - 2) {
343             #print "i=$i (values-2)\n";##
344 18         29 $e = $$cursor[$i + 1];
345 18 100 100     73 if ($e > $$cursor[$i] && $e != $i
      66        
346             && $$cursor[$i] != $i + 1) {
347 4         6 $$cursor[$i + 1] = $$cursor[$i];
348 4         5 $$cursor[$i] = $e;
349             #print "!\n";##
350 4         113 goto RESULT;
351             }
352             }
353 32         68 next;
354             }
355 20         42 for ($e = $$cursor[$i] + 1; $e < $values; $e++) {
356 22 100 100     83 if ($found[$e] && $e != $i) {
357 12         13 $elemfound = 1;
358 12         17 last;
359             }
360             }
361 20 100       39 last if ($elemfound);
362             }
363 14 100       53 if ($elemfound) {
364 12         16 $$cursor[$i] = $e;
365 12         18 $found[$e] = 0;
366 12         13 $i++;
367 12         13 my $j;
368             my @elems;
369 12         24 for ($j = 0; $j < $values; $j++) {
370 48 100       98 if ($found[$j]) { push(@elems, $j) }
  28         55  
371             }
372 12         26 for ($j = 0; $j < @elems; $j++) {
373 24 50       44 if ($elems[$j] != $i) {
    0          
374             # if the next is the last and it will be wrong:
375 24 100 100     91 if ($j + 2 == @elems
376             && $elems[$j + 1] == $i + 1) {
377             # interchange them:
378 4         5 $$cursor[$i] = $elems[$j + 1];
379 4         5 $$cursor[$i + 1] = $elems[$j];
380 4         4 last;
381             }
382 20         31 $$cursor[$i] = $elems[$j];
383             }
384             elsif ($j + 1 < @elems) {
385             # use the next element:
386 0         0 $$cursor[$i] = $elems[$j + 1];
387 0         0 $elems[$j + 1] = $elems[$j];
388             }
389 0         0 else { die() }
390 20         41 $i++;
391             }
392 12         225 goto RESULT;
393             }
394 2         9 return ();
395             }
396 18         19 RESULT:
397             # map cursor to data array
398             my @result;
399 18         30 foreach my $c (@$cursor){
400 72         70 push @result, $${ $data->[$c] };
  72         148  
401             }
402 18         91 return @result;
403             }
404              
405             =head2 factorial()
406              
407             Usage : my $f = factorial(4); #returns 24, or 4*3*2*1
408             Function: calculates n! (n factorial).
409             Returns : undef if n is non-integer or n < 0
410             Args : a positive, non-zero integer
411             Note : this function is used internally by combine() and permute()
412              
413             =cut
414              
415             sub factorial {
416 0     0 1 0 my $n = shift;
417 0 0 0     0 return undef unless $n >= 0 and $n == int($n);
418              
419 0         0 my $f;
420              
421 0         0 for($f = 1 ; $n > 0 ; $n--){
422 0         0 $f *= $n
423             }
424              
425 0         0 return $f;
426             }
427              
428             =head2 permute()
429              
430             Usage : my @permutations = permute(@n);
431             Function: implements nPk (n permute k) (where k == n), or n!/(n-k)!
432             returns all unique permutations of k items from set n
433             (where n == k, see "Note" below). items in n are assumed to
434             be character data, and are copied into the return data
435             structure.
436             Example : my @n = qw(a b c);
437             my @p = permute(@n);
438             print join "\n", map { join " ", @$_ } @p;
439             # prints:
440             # b a c
441             # b c a
442             # c b a
443             # c a b
444             # a c b
445             # a b c
446             Returns : a list of arrays, where each array contains a permutation of
447             k items from n (where k == n).
448             Args : a list of items to be permuted.
449             Note : k should really be parameterizable. this will happen
450             in a later version of the module. send me a patch to
451             make that version come out sooner.
452             Notes : data is internally assumed to be alphanumeric. this is necessary
453             to efficiently generate combinations of large sets. if you need
454             combinations of non-alphanumeric data, or on data
455             C would not be appropriate, use the
456             object-oriented API. See L, and the B option.
457              
458             Identical items are assumed to be non-unique. That is, calling
459             C
460             L
461              
462             =cut
463              
464             sub permute {
465 1     1 1 1210 my(@n) = @_;
466              
467 1         4 my @result = ();
468              
469 1         10 my $c = __PACKAGE__->new(data => [@n]);
470 1         6 while(my(@permu) = $c->next_permutation){
471 24         112 push @result, [@permu];
472             }
473              
474 1         18 return @result;
475             }
476              
477             =head1 CONSTRUCTOR
478              
479             =cut
480              
481             =head2 new()
482              
483             Usage : my $c = Math::Combinatorics->new( count => 2, #treated as int
484             data => [1,2,3,4] #arrayref or anonymous array
485             );
486             Function: build a new Math::Combinatorics object.
487             Returns : a Math::Combinatorics object
488             Args : count - required for combinatoric functions/methods. number of elements to be
489             present in returned set(s).
490             data - required for combinatoric B permutagenic functions/methods. this is the
491             set elements are chosen from. B: this array is modified in place; make
492             a copy of your array if the order matters in the caller's space.
493             frequency - optional vector of data frequencies. must be the same length as the B
494             constructor argument. These two constructor calls here are equivalent:
495              
496             $a = 'a';
497             $b = 'b';
498              
499             Math::Combinatorics->new( count=>2, data=>[\$a,\$a,\$a,\$a,\$a,\$b,\$b] );
500             Math::Combinatorics->new( count=>2, data=>[\$a,\$b], frequency=>[5,2] );
501              
502             so why use this? sometimes it's useful to have multiple identical entities in
503             a set (in set theory jargon, this is called a "bag", See L).
504             compare - optional subroutine reference used in sorting elements of the set. examples:
505              
506             #appropriate for character elements
507             compare => sub { $_[0] cmp $_[1] }
508             #appropriate for numeric elements
509             compare => sub { $_[0] <=> $_[1] }
510             #appropriate for object elements, perhaps
511             compare => sub { $_[0]->value <=> $_[1]->value }
512              
513             The default sort mechanism is based on references, and cannot be predicted.
514             Improvements for a more flexible compare() mechanism are most welcome.
515              
516             =cut
517              
518             sub new {
519 19     19 1 18090 my($class,%arg) = @_;
520 19         76 my $self = bless {}, $class;
521              
522 19   100 669   229 $self->{compare} = $arg{compare} || sub { $_[0] cmp $_[1] };
  669         1795  
523 19         44 $self->{count} = $arg{count};
524              
525             #convert bag to set
526 19         34 my $freq = $arg{frequency};
527 19 100 66     116 if(ref($freq) eq 'ARRAY' and scalar(@$freq) == scalar(@{$arg{data}})){
  4 50       21  
528 4         11 $self->{frequency}++;
529 4         9 my @bag = @{$arg{data}};
  4         18  
530 4         9 my @set = ();
531              
532             #allow '0 but defined' elements (Yunheng Wang)
533 4         8 foreach my $type ( @bag ) {
534 16         24 my $f = shift @$freq;
535 16 50       32 next if $f < 1;
536 16         25 for(1..$f){
537             #we push on a reference to make sure, for instance, that objects
538             #are identical and not copied
539 18         52 push @set, \$type;
540             }
541             }
542 4         11 $arg{data} = \@set;
543             }
544             elsif(!ref($freq)){
545 15         26 $arg{data} = [map { \$_ } @{$arg{data}}];
  72         165  
  15         44  
546             }
547              
548             #warn join ' ', @{$arg{data}};
549              
550             #OK, this is hokey, but I don't have time to fix it properly right now.
551             #We want to allow both user-specified sorting as well as our own
552             #reference-based internal sorting -- the latter only because unit tests
553             #are failing if we don't have it. Additionally, we don't want to require
554             #the triple derefernce necessary for comparison of the pristine data in
555             #the user-supplied compare coderef. The solution for now is to do an
556             #if/else. If you're staring at this please fix it!
557 19         46 my $compare = $self->{compare};
558 19 100       48 if ( defined $arg{compare} ) {
559 1         1 $self->{data} = [sort {&$compare($$$a,$$$b)} map {\$_} @{$arg{data}}];
  5         18  
  4         9  
  1         3  
560             }
561             else {
562 18         33 $self->{data} = [sort {&$compare($a,$b)} map {\$_} @{$arg{data}}];
  114         151  
  86         180  
  18         34  
563             }
564              
565             #warn Dumper($self->{data});
566              
567 19         48 $self->{cin} = 1;
568 19         40 $self->{pin} = 1;
569              
570 19         71 return $self;
571             }
572              
573             =head1 OBJECT METHODS
574              
575             =cut
576              
577             =head2 next_combination()
578              
579             Usage : my @combo = $c->next_combination();
580             Function: get combinations of size $count from @data.
581             Returns : returns a combination of $count items from @data (see L).
582             repeated calls retrieve all unique combinations of $count elements.
583             a returned empty list signifies all combinations have been iterated.
584             Note : this method may only be used if a B argument is B
585             given to L, otherwise use L.
586             Args : none.
587              
588             =cut
589              
590             sub next_combination {
591 145     145 1 618 my $self = shift;
592 145 50       322 if ( $self->{frequency} ) {
593 0         0 print STDERR "must use next_multiset() if 'frequency' argument passed to constructor\n";
594 0         0 return ();
595             }
596 145         257 return $self->_next_combination;
597             }
598              
599             sub _next_combination {
600 163     163   466 my $self = shift;
601 163         268 my $data = $self->data();
602 163         273 my $combo_end = $self->count();
603              
604 163         194 my $begin = 0;
605 163         153 my $end = $#{$data} + 1;
  163         862  
606              
607 163         176 my @result;
608              
609 163 50       277 return () if scalar(@$data) < $self->count();
610              
611 163 100       465 if($self->{cin}){
612 13         18 $self->{cin} = 0;
613              
614 13         28 for(0..$self->count-1){
615 30         37 push @result, $${ $data->[$_] };
  30         67  
616             }
617             #warn 1;
618 13         70 return @result;
619             }
620              
621 150 50 33     615 if ($combo_end == $begin || $combo_end == $end) {
622 0         0 return ();
623             }
624              
625 150         172 my $combo = $combo_end;
626 150         235 my $total_set;
627              
628 150         136 --$combo;
629 150         323 $total_set = $self->upper_bound($combo_end,$end,$data->[$combo]);
630 150 100       285 if ($total_set != $end) {
631 101         186 $self->swap($combo,$total_set);
632              
633 101         165 for(0..$self->count-1){
634 204         204 push @result, $${ $data->[$_] };
  204         402  
635             }
636             #warn 2;
637 101         396 return @result;
638             }
639              
640 49         47 --$total_set;
641 49         107 $combo = $self->lower_bound($begin, $combo_end, $data->[$total_set]);
642              
643 49 100       99 if ($combo == $begin) {
644 12         27 $self->rotate($begin, $combo_end, $end);
645             #warn 3;
646 12         46 return ();
647             }
648              
649 37         37 my $combo_next = $combo;
650 37         35 --$combo;
651 37         78 $total_set = $self->upper_bound($combo_end, $end, $data->[$combo]);
652              
653 37         42 my $sort_pos = $end;
654 37         46 $sort_pos += $combo_end - $total_set - 1;
655              
656 37         79 $self->rotate($combo_next, $total_set, $end);
657 37         66 $self->rotate($combo, $combo_next, $end);
658 37         74 $self->rotate($combo_end, $sort_pos, $end);
659              
660 37         70 for(0..$self->count-1){
661 78         698 push @result, $${ $data->[$_] };
  78         167  
662             }
663             #warn 4;
664 37         164 return @result;
665             }
666              
667             =head2 next_multiset()
668              
669             Usage : my @multiset = $c->next_multiset();
670             Function: get multisets for @data.
671             Returns : returns a multiset of items from @data (see L).
672             a multiset is a special type of combination where the set from which
673             combinations are drawn contains items that are indistinguishable. use
674             L when a B argument is passed to L.
675             repeated calls retrieve all unique multisets of @data elements. a
676             returned empty list signifies all multisets have been iterated.
677             Note : this method may only be used if a B argument is given to
678             L, otherwise use L.
679             Args : none.
680              
681             =cut
682              
683             sub next_multiset {
684 15     15 1 78 my $self = shift;
685              
686 15 50       34 if ( ! $self->{frequency} ) {
687 0         0 print STDERR "must use next_combination() if 'frequency' argument not passed to constructor\n";
688 0         0 return ();
689             }
690              
691 15         27 my $data = $self->data();
692 15         30 my $compare = $self->compare();
693              
694 15         42 while ( my @combo = $self->_next_combination ) {
695 16         97 my $x = join '', map {scalar($$_)} sort @$data;
  74         131  
696 16         36 my $y = join '', map {scalar($_) } sort @combo;
  32         58  
697              
698 16 100       67 next if $self->{'cache_multiset'}{$y}++;
699 13         53 return @combo;
700             }
701 2         3 $self->{'cache_multiset'} = undef;
702 2         9 return ();
703             }
704              
705             =head2 next_permutation()
706              
707             Usage : my @permu = $c->next_permutation();
708             Function: get permutations of elements in @data.
709             Returns : returns a permutation of items from @data (see L).
710             repeated calls retrieve all unique permutations of @data elements.
711             a returned empty list signifies all permutations have been iterated.
712             Note : this method may only be used if a B argument is B
713             given to L, otherwise use L.
714             Args : none.
715              
716             =cut
717              
718             sub next_permutation {
719 50     50 1 150 my $self = shift;
720 50 50       107 if ( $self->{frequency} ) {
721 0         0 print STDERR "must use next_string() if 'frequency' argument passed to constructor\n";
722 0         0 return ();
723             }
724 50         86 return $self->_next_permutation;
725             }
726              
727             sub _next_permutation {
728 196     196   233 my $self = shift;
729 196         316 my $data = $self->data();
730              
731 196 100       426 if($self->{pin}){
732 4         7 $self->{pin} = 0;
733 4         27 return map {$$$_} @$data;
  17         44  
734             }
735              
736 192         8964 my $cursor = $self->_permutation_cursor();
737              
738 192         725 my $last= $#{$cursor};
  192         278  
739              
740 192 50       377 if($last < 1){
741 0         0 return ();
742             }
743              
744             # Find last item not in reverse-sorted order:
745 192         237 my $i = $last - 1;
746 192   100     1244 $i-- while 0 <= $i && $cursor->[$i] >= $cursor->[$i+1];
747              
748 192 100       391 if($i == -1){
749 4         17 return ();
750             }
751              
752              
753             # Re-sort the reversely-sorted tail of the list:
754 188 100       1055 @{$cursor}[$i+1..$last] = reverse @{$cursor}[$i+1..$last]
  92         173  
  92         170  
755             if $cursor->[$i+1] > $cursor->[$last];
756              
757             # Find next item that will make us "greater":
758 188         237 my $j = $i+1;
759 188         486 $j++ while $cursor->[$i] >= $cursor->[$j];
760              
761             # Swap:
762 188         216 @{$cursor}[$i,$j] = @{$cursor}[$j,$i];
  188         6419  
  188         271  
763              
764             # map cursor to data array
765 188         228 my @result;
766 188         8377 foreach my $c (@$cursor){
767 871         1216 push @result, $${ $data->[$c] };
  871         1693  
768             }
769 188         1192 return @result;
770             }
771              
772             =head2 next_string()
773              
774             Usage : my @string = $c->next_string();
775             Function: get strings for @data.
776             Returns : returns a multiset of items from @data (see L).
777             a multiset is a special type of permutation where the set from which
778             combinations are drawn contains items that are indistinguishable. use
779             L when a B argument is passed to L.
780             repeated calls retrieve all unique multisets of @data elements. a
781             returned empty list signifies all strings have been iterated.
782             Note : this method may only be used if a B argument is given to
783             L, otherwise use L.
784             Args : none.
785              
786             =cut
787              
788             sub next_string {
789 86     86 1 460 my $self = shift;
790 86         151 my $data = $self->data();
791              
792 86 50       192 if ( ! $self->{frequency} ) {
793 0         0 print STDERR "must use next_permutation() if 'frequency' argument not passed to constructor\n";
794 0         0 return ();
795             }
796              
797              
798 86         159 while ( my @permu = $self->_next_permutation ) {
799 144         216 my $x = join '', map {scalar($$_)} @$data;
  696         1184  
800 144         236 my $y = join '', map {scalar($_) } @permu;
  696         968  
801              
802 144 100       669 next if $self->{'cache_string'}{$y}++;
803 84         381 return @permu;
804             }
805              
806 2         3 $self->{'cache_string'} = undef;
807 2         34 return ();
808             }
809              
810             =head1 INTERNAL FUNCTIONS AND METHODS
811              
812             =head2 sum()
813              
814             Usage : my $sum = sum(1,2,3); # returns 6
815             Function: sums a list of integers. non-integer list elements are ignored
816             Returns : sum of integer items in arguments passed in
817             Args : a list of integers
818             Note : this function is used internally by combine()
819              
820             =cut
821              
822             sub sum {
823 0     0 1 0 my $sum = 0;
824 0         0 foreach my $i (@_){
825 0 0       0 $sum += $i if $i == int($i);
826             }
827 0         0 return $sum;
828             }
829              
830             =head2 compare()
831              
832             Usage : $obj->compare()
833             Function: internal, undocumented. holds a comparison coderef.
834             Returns : value of compare (a coderef)
835              
836              
837             =cut
838              
839             sub compare {
840 251     251 1 276 my($self,$val) = @_;
841 251         438 return $self->{'compare'};
842             }
843              
844              
845             =head2 count()
846              
847             Usage : $obj->count()
848             Function: internal, undocumented. holds the "k" in nCk or nPk.
849             Returns : value of count (an int)
850              
851             =cut
852              
853             sub count {
854 477     477 1 526 my($self) = @_;
855 477         1045 return $self->{'count'};
856             }
857              
858              
859             =head2 data()
860              
861             Usage : $obj->data()
862             Function: internal, undocumented. holds the set "n" in nCk or nPk.
863             Returns : value of data (an arrayref)
864              
865             =cut
866              
867             sub data {
868 1455     1455 1 1636 my($self) = @_;
869 1455         2350 return $self->{'data'};
870             }
871              
872              
873             =head2 swap()
874              
875             internal, undocumented.
876              
877             =cut
878              
879             sub swap {
880 610     610 1 887 my $self = shift;
881 610         572 my $first = shift;
882 610         636 my $second = shift;
883 610         1912 my $data = $self->data();
884              
885 610         802 my $temp = $data->[$first];
886 610         680 $data->[$first] = $data->[$second];
887 610         966 $data->[$second] = $temp;
888             }
889              
890             =head2 reverse()
891              
892             internal, undocumented.
893              
894             =cut
895              
896             sub reverse {
897 0     0 1 0 my $self = shift;
898 0         0 my $first = shift;
899 0         0 my $last = shift;
900 0         0 my $data = $self->data();
901              
902 0         0 while (1) {
903 0 0 0     0 if ($first == $last || $first == --$last) {
904 0         0 return;
905             } else {
906 0         0 $self->swap($first++, $last);
907             }
908             }
909             }
910              
911             =head2 rotate()
912              
913             internal, undocumented.
914              
915             =cut
916              
917             sub rotate {
918 123     123 1 135 my $self = shift;
919 123         127 my $first = shift;
920 123         116 my $middle = shift;
921 123         114 my $last = shift;
922 123         307 my $data = $self->data();
923              
924 123 100 66     477 if ($first == $middle || $last == $middle) {
925 14         23 return;
926             }
927              
928 109         111 my $first2 = $middle;
929              
930 109         108 do {
931 412         849 $self->swap($first++, $first2++);
932              
933 412 100       969 if ($first == $middle) {
934 305         1248 $middle = $first2;
935             }
936             } while ($first2 != $last);
937              
938 109         139 $first2 = $middle;
939              
940 109         248 while ($first2 != $last) {
941 97         197 $self->swap($first++, $first2++);
942 97 100       258 if ($first == $middle) {
    100          
943 46         112 $middle = $first2;
944             } elsif ($first2 == $last) {
945 37         74 $first2 = $middle;
946             }
947             }
948             }
949              
950             =head2 upper_bound()
951              
952             internal, undocumented.
953              
954             =cut
955              
956             sub upper_bound {
957 187     187 1 207 my $self = shift;
958 187         180 my $first = shift;
959 187         191 my $last = shift;
960 187         187 my $value = shift;
961 187         308 my $compare = $self->compare();
962 187         421 my $data = $self->data();
963              
964 187         238 my $len = $last - $first;
965 187         185 my $half;
966             my $middle;
967              
968 187         365 while ($len > 0) {
969 457         505 $half = $len >> 1;
970 457         421 $middle = $first;
971 457         428 $middle += $half;
972              
973 457 100       752 if (&$compare($value,$data->[$middle]) == -1) {
974 210         587 $len = $half;
975             } else {
976 247         294 $first = $middle;
977 247         234 ++$first;
978 247         749 $len = $len - $half - 1;
979             }
980             }
981              
982 187         342 return $first;
983             }
984              
985             =head2 lower_bound()
986              
987             internal, undocumented.
988              
989             =cut
990              
991             sub lower_bound {
992 49     49 1 53 my $self = shift;
993 49         52 my $first = shift;
994 49         46 my $last = shift;
995 49         47 my $value = shift;
996 49         84 my $compare = $self->compare();
997 49         84 my $data = $self->data();
998              
999 49         61 my $len = $last - $first;
1000 49         55 my $half;
1001             my $middle;
1002              
1003 49         112 while ($len > 0) {
1004 98         92 $half = $len >> 1;
1005 98         90 $middle = $first;
1006 98         90 $middle += $half;
1007              
1008 98 100       165 if (&$compare($data->[$middle],$value) == -1) {
1009 37         34 $first = $middle;
1010 37         37 ++$first;
1011 37         89 $len = $len - $half - 1;
1012             } else {
1013 61         131 $len = $half;
1014             }
1015             }
1016              
1017 49         82 return $first;
1018             }
1019              
1020             =head2 _permutation_cursor()
1021              
1022             Usage : $obj->_permutation_cursor()
1023             Function: internal method. cursor on permutation iterator order.
1024             Returns : value of _permutation_cursor (an arrayref)
1025             Args : none
1026              
1027             =cut
1028              
1029             sub _permutation_cursor {
1030 212     212   278 my($self,$val) = @_;
1031              
1032 212 100       426 if(!$self->{'_permutation_cursor'}){
1033 6         16 my $data = $self->data();
1034 6         11 my @tmp = ();
1035 6         8 my $i = 0;
1036 6         32 push @tmp, $i++ foreach @$data;
1037 6         20 $self->{'_permutation_cursor'} = \@tmp;
1038             }
1039              
1040 212         343 return $self->{'_permutation_cursor'};
1041             }
1042              
1043             1;
1044