line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Algorithm::Combinatorics; |
2
|
|
|
|
|
|
|
|
3
|
15
|
|
|
15
|
|
479428
|
use 5.006002; |
|
15
|
|
|
|
|
59
|
|
|
15
|
|
|
|
|
675
|
|
4
|
15
|
|
|
15
|
|
90
|
use strict; |
|
15
|
|
|
|
|
29
|
|
|
15
|
|
|
|
|
747
|
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
our $VERSION = '0.27'; |
7
|
|
|
|
|
|
|
|
8
|
15
|
|
|
15
|
|
86
|
use XSLoader; |
|
15
|
|
|
|
|
44
|
|
|
15
|
|
|
|
|
645
|
|
9
|
|
|
|
|
|
|
XSLoader::load('Algorithm::Combinatorics', $VERSION); |
10
|
|
|
|
|
|
|
|
11
|
15
|
|
|
15
|
|
88
|
use Carp; |
|
15
|
|
|
|
|
27
|
|
|
15
|
|
|
|
|
1277
|
|
12
|
15
|
|
|
15
|
|
89
|
use Scalar::Util qw(reftype); |
|
15
|
|
|
|
|
38
|
|
|
15
|
|
|
|
|
1591
|
|
13
|
15
|
|
|
15
|
|
83
|
use Exporter; |
|
15
|
|
|
|
|
23
|
|
|
15
|
|
|
|
|
610
|
|
14
|
15
|
|
|
15
|
|
78
|
use base 'Exporter'; |
|
15
|
|
|
|
|
22
|
|
|
15
|
|
|
|
|
78634
|
|
15
|
|
|
|
|
|
|
our @EXPORT_OK = qw( |
16
|
|
|
|
|
|
|
combinations |
17
|
|
|
|
|
|
|
combinations_with_repetition |
18
|
|
|
|
|
|
|
variations |
19
|
|
|
|
|
|
|
variations_with_repetition |
20
|
|
|
|
|
|
|
tuples |
21
|
|
|
|
|
|
|
tuples_with_repetition |
22
|
|
|
|
|
|
|
permutations |
23
|
|
|
|
|
|
|
circular_permutations |
24
|
|
|
|
|
|
|
derangements |
25
|
|
|
|
|
|
|
complete_permutations |
26
|
|
|
|
|
|
|
partitions |
27
|
|
|
|
|
|
|
subsets |
28
|
|
|
|
|
|
|
); |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
our %EXPORT_TAGS = (all => [ @EXPORT_OK ]); |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
sub combinations { |
34
|
69
|
|
|
69
|
1
|
40984
|
my ($data, $k) = @_; |
35
|
69
|
|
|
|
|
137
|
__check_params($data, $k); |
36
|
|
|
|
|
|
|
|
37
|
66
|
50
|
|
|
|
134
|
return __contextualize(__null_iter()) if $k < 0; |
38
|
66
|
100
|
|
|
|
140
|
return __contextualize(__once_iter()) if $k == 0; |
39
|
50
|
50
|
|
|
|
103
|
if ($k > @$data) { |
40
|
0
|
|
|
|
|
0
|
carp("Parameter k is greater than the size of data"); |
41
|
0
|
|
|
|
|
0
|
return __contextualize(__null_iter()); |
42
|
|
|
|
|
|
|
} |
43
|
|
|
|
|
|
|
|
44
|
50
|
|
|
|
|
135
|
my @indices = 0..($k-1); |
45
|
|
|
|
|
|
|
my $iter = Algorithm::Combinatorics::Iterator->new(sub { |
46
|
31176
|
100
|
|
31176
|
|
88121
|
__next_combination(\@indices, @$data-1) == -1 ? undef : [ @{$data}[@indices] ]; |
|
31126
|
|
|
|
|
107915
|
|
47
|
50
|
|
|
|
|
274
|
}, [ @{$data}[@indices] ]); |
|
50
|
|
|
|
|
198
|
|
48
|
|
|
|
|
|
|
|
49
|
50
|
|
|
|
|
106
|
return __contextualize($iter); |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
sub combinations_with_repetition { |
54
|
37
|
|
|
37
|
1
|
58998
|
my ($data, $k) = @_; |
55
|
37
|
|
|
|
|
104
|
__check_params($data, $k); |
56
|
|
|
|
|
|
|
|
57
|
34
|
50
|
|
|
|
160
|
return __contextualize(__null_iter()) if $k < 0; |
58
|
34
|
100
|
|
|
|
78
|
return __contextualize(__once_iter()) if $k == 0; |
59
|
|
|
|
|
|
|
|
60
|
26
|
|
|
|
|
70
|
my @indices = (0) x $k; |
61
|
|
|
|
|
|
|
my $iter = Algorithm::Combinatorics::Iterator->new(sub { |
62
|
24148
|
100
|
|
24148
|
|
60853
|
__next_combination_with_repetition(\@indices, @$data-1) == -1 ? undef : [ @{$data}[@indices] ]; |
|
24122
|
|
|
|
|
67254
|
|
63
|
26
|
|
|
|
|
118
|
}, [ @{$data}[@indices] ]); |
|
26
|
|
|
|
|
106
|
|
64
|
|
|
|
|
|
|
|
65
|
26
|
|
|
|
|
70
|
return __contextualize($iter); |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
sub subsets { |
69
|
46
|
|
|
46
|
1
|
48618
|
my ($data, $k) = @_; |
70
|
46
|
|
|
|
|
115
|
__check_params($data, $k, 1); |
71
|
|
|
|
|
|
|
|
72
|
42
|
100
|
|
|
|
100
|
return combinations($data, $k) if defined $k; |
73
|
|
|
|
|
|
|
|
74
|
9
|
|
|
|
|
10
|
my $finished = 0; |
75
|
9
|
|
|
|
|
24
|
my @odometer = (1) x @$data; |
76
|
|
|
|
|
|
|
my $iter = Algorithm::Combinatorics::Iterator->new(sub { |
77
|
65603
|
100
|
|
65603
|
|
133196
|
return if $finished; |
78
|
65594
|
|
|
|
|
301568
|
my $subset = __next_subset($data, \@odometer); |
79
|
65594
|
100
|
|
|
|
151621
|
$finished = 1 if @$subset == 0; |
80
|
65594
|
|
|
|
|
152749
|
$subset; |
81
|
9
|
|
|
|
|
51
|
}); |
82
|
|
|
|
|
|
|
|
83
|
9
|
|
|
|
|
20
|
return __contextualize($iter); |
84
|
|
|
|
|
|
|
} |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
sub variations { |
87
|
72
|
|
|
72
|
1
|
162207
|
my ($data, $k) = @_; |
88
|
72
|
|
|
|
|
184
|
__check_params($data, $k); |
89
|
|
|
|
|
|
|
|
90
|
66
|
50
|
|
|
|
134
|
return __contextualize(__null_iter()) if $k < 0; |
91
|
66
|
100
|
|
|
|
139
|
return __contextualize(__once_iter()) if $k == 0; |
92
|
50
|
50
|
|
|
|
103
|
if ($k > @$data) { |
93
|
0
|
|
|
|
|
0
|
carp("Parameter k is greater than the size of data"); |
94
|
0
|
|
|
|
|
0
|
return __contextualize(__null_iter()); |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
# permutations() is more efficient because it knows |
98
|
|
|
|
|
|
|
# all indices are always used |
99
|
50
|
100
|
|
|
|
124
|
return permutations($data) if @$data == $k; |
100
|
|
|
|
|
|
|
|
101
|
34
|
|
|
|
|
123
|
my @indices = 0..($k-1); |
102
|
34
|
|
|
|
|
110
|
my @used = ((1) x $k, (0) x (@$data-$k)); |
103
|
|
|
|
|
|
|
my $iter = Algorithm::Combinatorics::Iterator->new(sub { |
104
|
30816
|
100
|
|
30816
|
|
92778
|
__next_variation(\@indices, \@used, @$data-1) == -1 ? undef : [ @{$data}[@indices] ]; |
|
30782
|
|
|
|
|
91507
|
|
105
|
34
|
|
|
|
|
223
|
}, [ @{$data}[@indices] ]); |
|
34
|
|
|
|
|
167
|
|
106
|
|
|
|
|
|
|
|
107
|
34
|
|
|
|
|
84
|
return __contextualize($iter); |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
*tuples = \&variations; |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
sub variations_with_repetition { |
113
|
74
|
|
|
74
|
1
|
195530
|
my ($data, $k) = @_; |
114
|
74
|
|
|
|
|
203
|
__check_params($data, $k); |
115
|
|
|
|
|
|
|
|
116
|
68
|
50
|
|
|
|
150
|
return __contextualize(__null_iter()) if $k < 0; |
117
|
68
|
100
|
|
|
|
154
|
return __contextualize(__once_iter()) if $k == 0; |
118
|
|
|
|
|
|
|
|
119
|
52
|
|
|
|
|
137
|
my @indices = (0) x $k; |
120
|
|
|
|
|
|
|
my $iter = Algorithm::Combinatorics::Iterator->new(sub { |
121
|
67086
|
100
|
|
67086
|
|
175338
|
__next_variation_with_repetition(\@indices, @$data-1) == -1 ? undef : [ @{$data}[@indices] ]; |
|
67034
|
|
|
|
|
177073
|
|
122
|
52
|
|
|
|
|
279
|
}, [ @{$data}[@indices] ]); |
|
52
|
|
|
|
|
243
|
|
123
|
|
|
|
|
|
|
|
124
|
52
|
|
|
|
|
133
|
return __contextualize($iter); |
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
*tuples_with_repetition = \&variations_with_repetition; |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
sub __variations_with_repetition_gray_code { |
130
|
0
|
|
|
0
|
|
0
|
my ($data, $k) = @_; |
131
|
0
|
|
|
|
|
0
|
__check_params($data, $k); |
132
|
|
|
|
|
|
|
|
133
|
0
|
0
|
|
|
|
0
|
return __contextualize(__null_iter()) if $k < 0; |
134
|
0
|
0
|
|
|
|
0
|
return __contextualize(__once_iter()) if $k == 0; |
135
|
|
|
|
|
|
|
|
136
|
0
|
|
|
|
|
0
|
my @indices = (0) x $k; |
137
|
0
|
|
|
|
|
0
|
my @focus_pointers = 0..$k; # yeah, length $k+1 |
138
|
0
|
|
|
|
|
0
|
my @directions = (1) x $k; |
139
|
|
|
|
|
|
|
my $iter = Algorithm::Combinatorics::Iterator->new(sub { |
140
|
0
|
|
|
|
|
0
|
__next_variation_with_repetition_gray_code( |
141
|
|
|
|
|
|
|
\@indices, |
142
|
|
|
|
|
|
|
\@focus_pointers, |
143
|
|
|
|
|
|
|
\@directions, |
144
|
|
|
|
|
|
|
@$data-1, |
145
|
0
|
0
|
|
0
|
|
0
|
) == -1 ? undef : [ @{$data}[@indices] ]; |
146
|
0
|
|
|
|
|
0
|
}, [ @{$data}[@indices] ]); |
|
0
|
|
|
|
|
0
|
|
147
|
|
|
|
|
|
|
|
148
|
0
|
|
|
|
|
0
|
return __contextualize($iter); |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
sub permutations { |
153
|
27
|
|
|
27
|
1
|
9465
|
my ($data) = @_; |
154
|
27
|
|
|
|
|
195
|
__check_params($data, 0); |
155
|
|
|
|
|
|
|
|
156
|
25
|
100
|
|
|
|
54
|
return __contextualize(__once_iter()) if @$data == 0; |
157
|
|
|
|
|
|
|
|
158
|
23
|
|
|
|
|
63
|
my @indices = 0..(@$data-1); |
159
|
|
|
|
|
|
|
my $iter = Algorithm::Combinatorics::Iterator->new(sub { |
160
|
40362
|
100
|
|
40362
|
|
118123
|
__next_permutation(\@indices) == -1 ? undef : [ @{$data}[@indices] ]; |
|
40339
|
|
|
|
|
128836
|
|
161
|
23
|
|
|
|
|
111
|
}, [ @{$data}[@indices] ]); |
|
23
|
|
|
|
|
105
|
|
162
|
|
|
|
|
|
|
|
163
|
23
|
|
|
|
|
48
|
return __contextualize($iter); |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
sub circular_permutations { |
168
|
11
|
|
|
11
|
1
|
11312
|
my ($data) = @_; |
169
|
11
|
|
|
|
|
27
|
__check_params($data, 0); |
170
|
|
|
|
|
|
|
|
171
|
11
|
100
|
|
|
|
25
|
return __contextualize(__once_iter()) if @$data == 0; |
172
|
9
|
100
|
100
|
|
|
43
|
return __contextualize(__once_iter([@$data])) if @$data == 1 || @$data == 2; |
173
|
|
|
|
|
|
|
|
174
|
5
|
|
|
|
|
14
|
my @indices = 1..(@$data-1); |
175
|
|
|
|
|
|
|
my $iter = Algorithm::Combinatorics::Iterator->new(sub { |
176
|
40336
|
100
|
|
40336
|
|
112220
|
__next_permutation(\@indices) == -1 ? undef : [ @{$data}[0, @indices] ]; |
|
40331
|
|
|
|
|
132869
|
|
177
|
5
|
|
|
|
|
23
|
}, [ @{$data}[0, @indices] ]); |
|
5
|
|
|
|
|
38
|
|
178
|
|
|
|
|
|
|
|
179
|
5
|
|
|
|
|
11
|
return __contextualize($iter); |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
sub __permutations_heap { |
183
|
0
|
|
|
0
|
|
0
|
my ($data) = @_; |
184
|
0
|
|
|
|
|
0
|
__check_params($data, 0); |
185
|
|
|
|
|
|
|
|
186
|
0
|
0
|
|
|
|
0
|
return __contextualize(__once_iter()) if @$data == 0; |
187
|
|
|
|
|
|
|
|
188
|
0
|
|
|
|
|
0
|
my @a = 0..(@$data-1); |
189
|
0
|
|
|
|
|
0
|
my @c = (0) x (@$data+1); # yeah, there's an spurious $c[0] to make the notation coincide |
190
|
|
|
|
|
|
|
my $iter = Algorithm::Combinatorics::Iterator->new(sub { |
191
|
0
|
0
|
|
0
|
|
0
|
__next_permutation_heap(\@a, \@c) == -1 ? undef : [ @{$data}[@a] ]; |
|
0
|
|
|
|
|
0
|
|
192
|
0
|
|
|
|
|
0
|
}, [ @{$data}[@a] ]); |
|
0
|
|
|
|
|
0
|
|
193
|
|
|
|
|
|
|
|
194
|
0
|
|
|
|
|
0
|
return __contextualize($iter); |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
sub derangements { |
199
|
28
|
|
|
28
|
1
|
27158
|
my ($data) = @_; |
200
|
28
|
|
|
|
|
70
|
__check_params($data, 0); |
201
|
|
|
|
|
|
|
|
202
|
24
|
100
|
|
|
|
59
|
return __contextualize(__once_iter()) if @$data == 0; |
203
|
20
|
100
|
|
|
|
52
|
return __contextualize(__null_iter()) if @$data == 1; |
204
|
|
|
|
|
|
|
|
205
|
16
|
|
|
|
|
93
|
my @indices = 0..(@$data-1); |
206
|
16
|
|
|
|
|
55
|
@indices[$_, $_+1] = @indices[$_+1, $_] for map { 2*$_ } 0..((@$data-2)/2); |
|
32
|
|
|
|
|
121
|
|
207
|
16
|
100
|
|
|
|
67
|
@indices[-1, -2] = @indices[-2, -1] if @$data % 2; |
208
|
|
|
|
|
|
|
my $iter = Algorithm::Combinatorics::Iterator->new(sub { |
209
|
296706
|
100
|
|
296706
|
|
966087
|
__next_derangement(\@indices) == -1 ? undef : [ @{$data}[@indices] ]; |
|
296690
|
|
|
|
|
976728
|
|
210
|
16
|
|
|
|
|
70
|
}, [ @{$data}[@indices] ]); |
|
16
|
|
|
|
|
80
|
|
211
|
|
|
|
|
|
|
|
212
|
16
|
|
|
|
|
39
|
return __contextualize($iter); |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
*complete_permutations = \&derangements; |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
sub partitions { |
219
|
42
|
|
|
42
|
1
|
1706702
|
my ($data, $k) = @_; |
220
|
42
|
100
|
|
|
|
102
|
if (defined $k) { |
221
|
26
|
|
|
|
|
50
|
__partitions_of_size_p($data, $k); |
222
|
|
|
|
|
|
|
} else { |
223
|
16
|
|
|
|
|
35
|
__partitions_of_all_sizes($data); |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
sub __partitions_of_all_sizes { |
228
|
16
|
|
|
16
|
|
22
|
my ($data) = @_; |
229
|
16
|
|
|
|
|
36
|
__check_params($data, 0); |
230
|
|
|
|
|
|
|
|
231
|
12
|
100
|
|
|
|
34
|
return __contextualize(__once_iter()) if @$data == 0; |
232
|
|
|
|
|
|
|
|
233
|
10
|
|
|
|
|
26
|
my @k = (0) x @$data; |
234
|
10
|
|
|
|
|
21
|
my @M = (0) x @$data; |
235
|
|
|
|
|
|
|
my $iter = Algorithm::Combinatorics::Iterator->new(sub { |
236
|
137168
|
100
|
|
137168
|
|
479571
|
__next_partition(\@k, \@M) == -1 ? undef : __slice_partition(\@k, \@M, $data); |
237
|
10
|
|
|
|
|
54
|
}, __slice_partition(\@k, \@M, $data)); |
238
|
|
|
|
|
|
|
|
239
|
10
|
|
|
|
|
20
|
return __contextualize($iter); |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
# We use @k and $p here and sacrifice the uniform usage of $k |
243
|
|
|
|
|
|
|
# to follow the notation in [3]. |
244
|
|
|
|
|
|
|
sub __partitions_of_size_p { |
245
|
26
|
|
|
26
|
|
40
|
my ($data, $p) = @_; |
246
|
26
|
|
|
|
|
46
|
__check_params($data, $p); |
247
|
|
|
|
|
|
|
|
248
|
26
|
50
|
|
|
|
54
|
return __contextualize(__null_iter()) if $p < 0; |
249
|
26
|
100
|
66
|
|
|
74
|
return __contextualize(__once_iter()) if @$data == 0 && $p == 0; |
250
|
22
|
100
|
|
|
|
47
|
return __contextualize(__null_iter()) if $p == 0; |
251
|
|
|
|
|
|
|
|
252
|
18
|
50
|
|
|
|
37
|
if ($p > @$data) { |
253
|
0
|
|
|
|
|
0
|
carp("Parameter k is greater than the size of data"); |
254
|
0
|
|
|
|
|
0
|
return __contextualize(__null_iter()); |
255
|
|
|
|
|
|
|
} |
256
|
|
|
|
|
|
|
|
257
|
18
|
|
|
|
|
55
|
my $q = @$data - $p + 1; |
258
|
18
|
|
|
|
|
38
|
my @k = (0) x $q; |
259
|
18
|
|
|
|
|
27
|
my @M = (0) x $q; |
260
|
18
|
|
|
|
|
84
|
push @k, $_ - $q + 1 for $q..(@$data-1); |
261
|
18
|
|
|
|
|
45
|
push @M, $_ - $q + 1 for $q..(@$data-1); |
262
|
|
|
|
|
|
|
my $iter = Algorithm::Combinatorics::Iterator->new(sub { |
263
|
179891
|
100
|
|
179891
|
|
746553
|
__next_partition_of_size_p(\@k, \@M, $p) == -1 ? undef : __slice_partition_of_size_p(\@k, $p, $data); |
264
|
18
|
|
|
|
|
95
|
}, __slice_partition_of_size_p(\@k, $p, $data)); |
265
|
|
|
|
|
|
|
|
266
|
18
|
|
|
|
|
34
|
return __contextualize($iter); |
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
sub __slice_partition { |
271
|
137168
|
|
|
137168
|
|
158378
|
my ($k, $M, $data) = @_; |
272
|
137168
|
|
|
|
|
161854
|
my @partition = (); |
273
|
137168
|
|
|
|
|
172029
|
my $size = $M->[-1] + 1; # $M->[0] is always 0 in our code |
274
|
137168
|
|
|
|
|
616676
|
push @partition, [] for 1..$size; |
275
|
137168
|
|
|
|
|
166475
|
my $i = 0; |
276
|
137168
|
|
|
|
|
183527
|
foreach my $x (@$data) { |
277
|
1350233
|
|
|
|
|
1154058
|
push @{$partition[$k->[$i]]}, $x; |
|
1350233
|
|
|
|
|
1806992
|
|
278
|
1350233
|
|
|
|
|
1545103
|
++$i; |
279
|
|
|
|
|
|
|
} |
280
|
137168
|
|
|
|
|
397488
|
return \@partition; |
281
|
|
|
|
|
|
|
} |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
# We use @k and $p here and sacrifice the uniform usage of $k |
284
|
|
|
|
|
|
|
# to follow the notation in [3]. |
285
|
|
|
|
|
|
|
sub __slice_partition_of_size_p { |
286
|
179891
|
|
|
179891
|
|
258398
|
my ($k, $p, $data) = @_; |
287
|
179891
|
|
|
|
|
235525
|
my @partition = (); |
288
|
179891
|
|
|
|
|
801607
|
push @partition, [] for 1..$p; |
289
|
179891
|
|
|
|
|
279873
|
my $i = 0; |
290
|
179891
|
|
|
|
|
254780
|
foreach my $x (@$data) { |
291
|
1944420
|
|
|
|
|
2115178
|
push @{$partition[$k->[$i]]}, $x; |
|
1944420
|
|
|
|
|
2916746
|
|
292
|
1944420
|
|
|
|
|
2587411
|
++$i; |
293
|
|
|
|
|
|
|
} |
294
|
179891
|
|
|
|
|
569278
|
return \@partition; |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
sub __check_params { |
299
|
406
|
|
|
406
|
|
629
|
my ($data, $k, $k_is_not_required) = @_; |
300
|
406
|
100
|
|
|
|
1329
|
if (not defined $data) { |
301
|
13
|
|
|
|
|
2624
|
croak("Missing parameter data"); |
302
|
|
|
|
|
|
|
} |
303
|
393
|
100
|
100
|
|
|
1745
|
unless ($k_is_not_required || defined $k) { |
304
|
6
|
|
|
|
|
2625
|
croak("Missing parameter k"); |
305
|
|
|
|
|
|
|
} |
306
|
|
|
|
|
|
|
|
307
|
387
|
|
|
|
|
1130
|
my $type = reftype $data; |
308
|
387
|
100
|
66
|
|
|
1791
|
if (!defined($type) || $type ne "ARRAY") { |
309
|
13
|
|
|
|
|
1778
|
croak("Parameter data is not an arrayref"); |
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
|
312
|
374
|
50
|
66
|
|
|
1832
|
carp("Parameter k is negative") if !$k_is_not_required && $k < 0; |
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
# Given an iterator that responds to the next() method this |
317
|
|
|
|
|
|
|
# subrutine returns the iterator in scalar context, loops |
318
|
|
|
|
|
|
|
# over the iterator to build and return an array of results |
319
|
|
|
|
|
|
|
# in list context, and does nothing but issue a warning in |
320
|
|
|
|
|
|
|
# void context. |
321
|
|
|
|
|
|
|
sub __contextualize { |
322
|
325
|
|
|
325
|
|
408
|
my $iter = shift; |
323
|
325
|
|
|
|
|
456
|
my $w = wantarray; |
324
|
325
|
50
|
|
|
|
554
|
if (defined $w) { |
325
|
325
|
100
|
|
|
|
548
|
if ($w) { |
326
|
152
|
|
|
|
|
249
|
my @result = (); |
327
|
152
|
|
|
|
|
414
|
while (my $c = $iter->next) { |
328
|
956
|
|
|
|
|
2198
|
push @result, $c; |
329
|
|
|
|
|
|
|
} |
330
|
152
|
|
|
|
|
1425
|
return @result; |
331
|
|
|
|
|
|
|
} else { |
332
|
173
|
|
|
|
|
727
|
return $iter; |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
} else { |
335
|
0
|
|
|
|
|
0
|
my $sub = (caller(1))[3]; |
336
|
0
|
|
|
|
|
0
|
carp("Useless use of $sub in void context"); |
337
|
|
|
|
|
|
|
} |
338
|
|
|
|
|
|
|
} |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
sub __null_iter { |
341
|
8
|
|
|
8
|
|
27
|
return Algorithm::Combinatorics::Iterator->new(sub { return }); |
|
8
|
|
|
8
|
|
45
|
|
342
|
|
|
|
|
|
|
} |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
sub __once_iter { |
346
|
74
|
|
|
74
|
|
101
|
my $tuple = shift; |
347
|
4
|
|
|
4
|
|
10
|
$tuple ? Algorithm::Combinatorics::Iterator->new(sub { return }, $tuple) : |
348
|
74
|
100
|
|
70
|
|
1181
|
Algorithm::Combinatorics::Iterator->new(sub { return }, []); |
|
70
|
|
|
|
|
321
|
|
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
# This is a bit dirty by now, the objective is to be able to |
354
|
|
|
|
|
|
|
# pass an initial sequence to the iterator and avoid a test |
355
|
|
|
|
|
|
|
# in each iteration saying whether the sequence was already |
356
|
|
|
|
|
|
|
# returned or not, since that might potentially be done a lot |
357
|
|
|
|
|
|
|
# of times. |
358
|
|
|
|
|
|
|
# |
359
|
|
|
|
|
|
|
# The solution is to return an iterator that has a first sequence |
360
|
|
|
|
|
|
|
# associated. The first time you call it that sequence is returned |
361
|
|
|
|
|
|
|
# and the iterator rebless itself to become just a wrapped coderef. |
362
|
|
|
|
|
|
|
# |
363
|
|
|
|
|
|
|
# Note that the public contract is that responds to next(), no |
364
|
|
|
|
|
|
|
# iterator class name is documented. |
365
|
|
|
|
|
|
|
package Algorithm::Combinatorics::Iterator; |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
sub new { |
368
|
325
|
|
|
325
|
|
515
|
my ($class, $coderef, $first_seq) = @_; |
369
|
325
|
100
|
|
|
|
632
|
if (defined $first_seq) { |
370
|
308
|
|
|
|
|
1394
|
return bless [$coderef, $first_seq], $class; |
371
|
|
|
|
|
|
|
} else { |
372
|
17
|
|
|
|
|
73
|
return bless $coderef, 'Algorithm::Combinatorics::JustCoderef'; |
373
|
|
|
|
|
|
|
} |
374
|
|
|
|
|
|
|
} |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
sub next { |
377
|
308
|
|
|
308
|
|
1006
|
my ($self) = @_; |
378
|
308
|
|
|
|
|
583
|
$_[0] = $self->[0]; |
379
|
308
|
|
|
|
|
707
|
bless $_[0], 'Algorithm::Combinatorics::JustCoderef'; |
380
|
308
|
|
|
|
|
979
|
return $self->[1]; |
381
|
|
|
|
|
|
|
} |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
package Algorithm::Combinatorics::JustCoderef; |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
sub next { |
386
|
913374
|
|
|
913374
|
|
4099987
|
my ($self) = @_; |
387
|
913374
|
|
|
|
|
1485085
|
return $self->(); |
388
|
|
|
|
|
|
|
} |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
1; |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
__END__ |