line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# build and eval the code to efficiently merge several iterators in one iterator |
2
|
|
|
|
|
|
|
package Iterator::Merger; |
3
|
4
|
|
|
4
|
|
72764
|
use strict; |
|
4
|
|
|
|
|
11
|
|
|
4
|
|
|
|
|
1015
|
|
4
|
4
|
|
|
4
|
|
28
|
use warnings; |
|
4
|
|
|
|
|
10
|
|
|
4
|
|
|
|
|
1915
|
|
5
|
4
|
|
|
4
|
|
30
|
use Carp; |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
3664
|
|
6
|
4
|
|
|
4
|
|
36
|
use base 'Exporter'; |
|
4
|
|
|
|
|
10
|
|
|
4
|
|
|
|
|
7803
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
our $VERSION = '0.64'; |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
# use constant DEBUG => 1; |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
our @EXPORT_OK = qw( |
13
|
|
|
|
|
|
|
imerge |
14
|
|
|
|
|
|
|
imerge_num |
15
|
|
|
|
|
|
|
imerge_raw |
16
|
|
|
|
|
|
|
); |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( |
19
|
|
|
|
|
|
|
all => \@EXPORT_OK |
20
|
|
|
|
|
|
|
); |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
our $Has_defined_or; |
23
|
|
|
|
|
|
|
our $Has_array_heap; |
24
|
|
|
|
|
|
|
our $Max_generate; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
$Has_defined_or = eval "undef // 1" unless defined $Has_defined_or; |
27
|
4
|
100
|
|
4
|
|
1512
|
BEGIN { $Has_array_heap = eval "require Array::Heap;1" unless defined $Has_array_heap }; |
28
|
|
|
|
|
|
|
$Max_generate = $Has_array_heap ? 9 : 12 unless defined $Max_generate; |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
my %Generator_cache; |
31
|
|
|
|
|
|
|
|
32
|
1
|
100
|
100
|
1
|
|
3
|
*imerge_raw = eval($Has_defined_or ? |
|
2384
|
50
|
100
|
|
|
391868
|
|
|
101597
|
100
|
66
|
|
|
2753982
|
|
|
101400
|
50
|
|
|
|
2066008
|
|
|
21283
|
100
|
|
|
|
97110
|
|
|
1470
|
|
|
|
|
2911
|
|
|
83932
|
|
|
|
|
270378
|
|
|
197
|
|
|
|
|
577
|
|
|
1893
|
|
|
|
|
324808
|
|
|
213
|
|
|
|
|
810
|
|
|
16
|
|
|
|
|
29
|
|
|
213
|
|
|
|
|
561
|
|
|
1865
|
|
|
|
|
4003
|
|
|
1864
|
|
|
|
|
3847
|
|
|
196
|
|
|
|
|
1138
|
|
|
102309
|
|
|
|
|
4441239
|
|
|
21435
|
|
|
|
|
450157
|
|
|
22934
|
|
|
|
|
60337
|
|
|
1666
|
|
|
|
|
3286
|
|
33
|
|
|
|
|
|
|
q!sub { |
34
|
|
|
|
|
|
|
# DEBUG && warn "defined or"; |
35
|
|
|
|
|
|
|
my @ites = @_ or return sub {}; |
36
|
|
|
|
|
|
|
if (@ites==1) { |
37
|
|
|
|
|
|
|
my $ite = shift; |
38
|
|
|
|
|
|
|
return ref($ite) eq 'GLOB' ? sub {scalar <$ite>} : sub {scalar &$ite}; |
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
for (@ites) { |
41
|
|
|
|
|
|
|
if (ref($_) eq 'GLOB') { |
42
|
|
|
|
|
|
|
my $fh = $_; |
43
|
|
|
|
|
|
|
$_ = sub {<$fh>} |
44
|
|
|
|
|
|
|
} |
45
|
|
|
|
|
|
|
} |
46
|
|
|
|
|
|
|
croak "arguments must be CODE references or filehandles" if grep {ref($_) ne 'CODE'} @ites; |
47
|
|
|
|
|
|
|
my $ite = shift(@ites); |
48
|
|
|
|
|
|
|
sub { |
49
|
|
|
|
|
|
|
&$ite // do { |
50
|
|
|
|
|
|
|
{ # block for redo |
51
|
|
|
|
|
|
|
$ite = shift(@ites) || return; |
52
|
|
|
|
|
|
|
&$ite // redo |
53
|
|
|
|
|
|
|
} |
54
|
|
|
|
|
|
|
} |
55
|
|
|
|
|
|
|
} |
56
|
|
|
|
|
|
|
}! |
57
|
|
|
|
|
|
|
: |
58
|
|
|
|
|
|
|
q!sub { |
59
|
|
|
|
|
|
|
# DEBUG && warn "temp var"; |
60
|
|
|
|
|
|
|
my @ites = @_ or return sub {}; |
61
|
|
|
|
|
|
|
if (@ites==1) { |
62
|
|
|
|
|
|
|
my $ite = shift; |
63
|
|
|
|
|
|
|
return ref($ite) eq 'GLOB' ? sub {scalar <$ite>} : sub {scalar &$ite}; |
64
|
|
|
|
|
|
|
} |
65
|
|
|
|
|
|
|
for (@ites) { |
66
|
|
|
|
|
|
|
if (ref($_) eq 'GLOB') { |
67
|
|
|
|
|
|
|
my $fh = $_; |
68
|
|
|
|
|
|
|
$_ = sub {<$fh>} |
69
|
|
|
|
|
|
|
} |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
croak "arguments must be CODE references or filehandles" if grep {ref($_) ne 'CODE'} @ites; |
72
|
|
|
|
|
|
|
my $ite = shift(@ites); |
73
|
|
|
|
|
|
|
sub { |
74
|
|
|
|
|
|
|
my $next = &$ite; |
75
|
|
|
|
|
|
|
until (defined $next) { |
76
|
|
|
|
|
|
|
$ite = shift(@ites) || return; |
77
|
|
|
|
|
|
|
$next = &$ite; |
78
|
|
|
|
|
|
|
} |
79
|
|
|
|
|
|
|
$next |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
}! |
82
|
|
|
|
|
|
|
) || die $@; |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
sub imerge { |
85
|
646
|
|
|
450
|
1
|
807229
|
_imerge(1, 1, \@_) |
86
|
|
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
sub imerge_num { |
89
|
450
|
|
|
450
|
1
|
1545970
|
_imerge(0, 1, \@_) |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
sub _imerge { |
93
|
900
|
|
|
900
|
|
2947
|
my ($lex, $asc, $iterators) = @_; |
94
|
900
|
|
|
|
|
1934
|
my $nb = @$iterators; |
95
|
|
|
|
|
|
|
|
96
|
900
|
100
|
|
|
|
1993
|
croak "arguments must be CODE references or filehandles" if grep {ref($_) !~ /^CODE$|^GLOB$/} @$iterators; |
|
6736
|
|
|
|
|
20709
|
|
97
|
|
|
|
|
|
|
|
98
|
896
|
100
|
|
|
|
5453
|
if ($nb==0) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
99
|
48
|
|
|
4848
|
|
305
|
return sub {undef}; |
|
4848
|
|
|
|
|
36571
|
|
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
elsif ($nb==1) { |
102
|
|
|
|
|
|
|
#return $iterators->[0]; |
103
|
|
|
|
|
|
|
# ensure scalar context |
104
|
64
|
|
|
|
|
119
|
my $ite = $iterators->[0]; |
105
|
64
|
50
|
|
4564
|
|
425
|
return ref($ite) eq 'GLOB' ? sub {scalar <$ite>} : sub {scalar &$ite}; |
|
0
|
|
|
|
|
0
|
|
|
9066
|
|
|
|
|
101979
|
|
106
|
|
|
|
|
|
|
} |
107
|
|
|
|
|
|
|
elsif ($nb <= $Max_generate) { |
108
|
|
|
|
|
|
|
# DEBUG && warn "generate"; |
109
|
532
|
50
|
|
|
|
1305
|
if ($nb == grep {ref($_) eq 'GLOB'} @$iterators) { |
|
3388
|
|
|
|
|
6943
|
|
110
|
|
|
|
|
|
|
# only globs |
111
|
0
|
|
0
|
|
|
0
|
my $code = $Generator_cache{$nb, $lex, 1} ||= _merger_generator($nb, $lex, $asc, 1); |
112
|
0
|
|
|
|
|
0
|
return $code->(@$iterators); |
113
|
|
|
|
|
|
|
} else { |
114
|
532
|
|
|
|
|
1507
|
for (@$iterators) { |
115
|
3388
|
50
|
|
|
|
6716
|
if (ref($_) eq 'GLOB') { |
116
|
0
|
|
|
|
|
0
|
my $fh = $_; |
117
|
0
|
|
|
0
|
|
0
|
$_ = sub {<$fh>} |
118
|
0
|
|
|
|
|
0
|
} |
119
|
|
|
|
|
|
|
} |
120
|
532
|
|
66
|
|
|
3969
|
my $code = $Generator_cache{$nb, $lex, 0} ||= _merger_generator($nb, $lex, $asc, 0); |
121
|
532
|
|
|
|
|
16047
|
return $code->(@$iterators); |
122
|
|
|
|
|
|
|
} |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
else { |
125
|
|
|
|
|
|
|
# no generation, giveup on some ultimate optim: lets turn all GLOBs to CODEs... |
126
|
252
|
|
|
|
|
810
|
for (@$iterators) { |
127
|
3276
|
50
|
|
|
|
6133
|
if (ref($_) eq 'GLOB') { |
128
|
0
|
|
|
|
|
0
|
my $fh = $_; |
129
|
0
|
|
|
0
|
|
0
|
$_ = sub {<$fh>} |
130
|
0
|
|
|
|
|
0
|
} |
131
|
|
|
|
|
|
|
} |
132
|
252
|
100
|
|
|
|
758
|
if ($Has_array_heap) { |
133
|
|
|
|
|
|
|
# DEBUG && warn "heap"; |
134
|
|
|
|
|
|
|
# general case, use a heap |
135
|
168
|
|
|
|
|
287
|
my @heap; |
136
|
|
|
|
|
|
|
# cannot take references to *_heap_lex and *_heap functions, |
137
|
|
|
|
|
|
|
# due to prototype problems... |
138
|
168
|
100
|
|
|
|
412
|
if ($lex) { |
139
|
84
|
|
|
|
|
164
|
for my $ite (@$iterators) { |
140
|
1050
|
|
|
|
|
1798
|
my $val = &$ite; |
141
|
1050
|
100
|
|
|
|
6447
|
Array::Heap::push_heap_lex(@heap, [$val, $ite]) if defined $val; |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
return sub { |
144
|
62099
|
|
100
|
62099
|
|
755858
|
my $data = Array::Heap::pop_heap_lex(@heap) || return undef; |
145
|
53615
|
|
|
|
|
77259
|
my $min = $data->[0]; |
146
|
53615
|
100
|
|
|
|
83586
|
if ( defined($data->[0] = $data->[1]->()) ) { |
147
|
52583
|
|
|
|
|
266196
|
Array::Heap::push_heap_lex(@heap, $data); |
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
$min |
150
|
84
|
|
|
|
|
646
|
}; |
|
53615
|
|
|
|
|
101924
|
|
151
|
|
|
|
|
|
|
} |
152
|
|
|
|
|
|
|
else { |
153
|
84
|
|
|
|
|
182
|
for my $ite (@$iterators) { |
154
|
1050
|
|
|
|
|
1822
|
my $val = &$ite; |
155
|
1050
|
100
|
|
|
|
6259
|
Array::Heap::push_heap(@heap, [$val, $ite]) if defined $val; |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
return sub { |
158
|
62965
|
|
100
|
62965
|
|
741483
|
my $data = Array::Heap::pop_heap(@heap) || return undef; |
159
|
54481
|
|
|
|
|
77901
|
my $min = $data->[0]; |
160
|
54481
|
100
|
|
|
|
83308
|
if ( defined($data->[0] = $data->[1]->()) ) { |
161
|
53449
|
|
|
|
|
262736
|
Array::Heap::push_heap(@heap, $data); |
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
$min |
164
|
84
|
|
|
|
|
685
|
}; |
|
54481
|
|
|
|
|
94312
|
|
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
else { |
168
|
|
|
|
|
|
|
# DEBUG && warn "brutal"; |
169
|
|
|
|
|
|
|
# no heap available, lets be dirty |
170
|
84
|
|
|
|
|
227
|
my @values = map {scalar &$_} @$iterators; |
|
1176
|
|
|
|
|
5349
|
|
171
|
|
|
|
|
|
|
# warn "values: ", join(", ", map {length($_)?1:0} @values), "\n"; |
172
|
84
|
100
|
|
|
|
639
|
if ($lex) { |
173
|
|
|
|
|
|
|
return sub { |
174
|
33692
|
|
|
33692
|
|
411231
|
my $i=-1; |
175
|
33692
|
|
|
|
|
44283
|
my $min; |
176
|
|
|
|
|
|
|
my $min_i; |
177
|
33692
|
|
|
|
|
49608
|
for (@values) { |
178
|
473333
|
|
|
|
|
556547
|
++$i; |
179
|
473333
|
100
|
100
|
|
|
1384401
|
if (defined and ((not defined $min) or ($_ lt $min))) { |
|
|
|
100
|
|
|
|
|
180
|
92650
|
|
|
|
|
118795
|
$min = $_; |
181
|
92650
|
|
|
|
|
125595
|
$min_i = $i; |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
} |
184
|
33692
|
100
|
|
|
|
74928
|
$values[$min_i] = $iterators->[$min_i]->() if defined $min_i; |
185
|
|
|
|
|
|
|
# warn "value is ", (length($min)?1:0), " from $min_i"; |
186
|
33692
|
|
|
|
|
168129
|
$min |
187
|
42
|
|
|
|
|
399
|
}; |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
else { |
190
|
|
|
|
|
|
|
return sub { |
191
|
34044
|
|
|
34044
|
|
401448
|
my $i=-1; |
192
|
34044
|
|
|
|
|
44083
|
my $min; |
193
|
|
|
|
|
|
|
my $min_i; |
194
|
34044
|
|
|
|
|
51342
|
for (@values) { |
195
|
478261
|
|
|
|
|
560980
|
++$i; |
196
|
478261
|
100
|
100
|
|
|
1377061
|
if (defined and ((not defined $min) or ($_ < $min))) { |
|
|
|
100
|
|
|
|
|
197
|
90971
|
|
|
|
|
112085
|
$min = $_; |
198
|
90971
|
|
|
|
|
126141
|
$min_i = $i; |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
} |
201
|
34044
|
100
|
|
|
|
77506
|
$values[$min_i] = $iterators->[$min_i]->() if defined $min_i; |
202
|
34044
|
|
|
|
|
156582
|
$min |
203
|
42
|
|
|
|
|
385
|
}; |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
# nb=10 => ~30KiB to eval (doubles each increment) |
210
|
|
|
|
|
|
|
sub _merger_generator { |
211
|
38
|
|
|
38
|
|
156
|
my ($nb, $lex, $asc, $globs) = @_; |
212
|
38
|
|
|
|
|
95
|
my $str = "no warnings;sub{"; |
213
|
38
|
|
|
|
|
134
|
$str .= "my(". join(',', map {"\$i$_"} 1..$nb). ")=\@_;"; |
|
242
|
|
|
|
|
579
|
|
214
|
38
|
50
|
|
|
|
438
|
$str .= $globs ? "my\$n$_=<\$i$_>;" : "my\$n$_=&\$i$_;" for 1..$nb; |
215
|
38
|
|
|
|
|
107
|
$str .= "my\$r;sub{"; |
216
|
38
|
50
|
|
|
|
187
|
my $cmp = $lex ? ($asc ? ' lt' : ' gt') : ($asc ? '<' : '>'); |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
217
|
38
|
|
|
|
|
201
|
$str .= _cmp($cmp, $globs, 1..$nb); |
218
|
38
|
|
|
|
|
402
|
$str .= ";\$r}}"; |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
# $str =~ s/;/;\n/g; |
221
|
|
|
|
|
|
|
# $str =~ s/\$/ \$/g; |
222
|
|
|
|
|
|
|
# $str =~ s/{/ {\n/g; |
223
|
|
|
|
|
|
|
# $str =~ s/}/ }\n/g; |
224
|
|
|
|
|
|
|
# warn "\n\n$str\n\n"; |
225
|
|
|
|
|
|
|
|
226
|
38
|
50
|
|
2
|
|
4000
|
eval($str) || die "$@ in $str" |
|
2
|
|
|
2
|
|
20
|
|
|
2
|
|
|
2
|
|
6
|
|
|
2
|
|
|
2
|
|
291
|
|
|
2
|
|
|
2
|
|
19
|
|
|
2
|
|
|
2
|
|
4
|
|
|
2
|
|
|
2
|
|
375
|
|
|
2
|
|
|
2
|
|
24
|
|
|
2
|
|
|
2
|
|
5
|
|
|
2
|
|
|
2
|
|
552
|
|
|
2
|
|
|
2
|
|
23
|
|
|
2
|
|
|
2
|
|
6
|
|
|
2
|
|
|
2
|
|
697
|
|
|
2
|
|
|
2
|
|
22
|
|
|
2
|
|
|
2
|
|
5
|
|
|
2
|
|
|
2
|
|
1075
|
|
|
2
|
|
|
|
|
24
|
|
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
1828
|
|
|
2
|
|
|
|
|
54
|
|
|
2
|
|
|
|
|
10
|
|
|
2
|
|
|
|
|
3633
|
|
|
2
|
|
|
|
|
31
|
|
|
2
|
|
|
|
|
35
|
|
|
2
|
|
|
|
|
6134
|
|
|
2
|
|
|
|
|
23
|
|
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
5620
|
|
|
2
|
|
|
|
|
24
|
|
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
15072
|
|
|
2
|
|
|
|
|
22
|
|
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
37452
|
|
|
2
|
|
|
|
|
24
|
|
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
585
|
|
|
2
|
|
|
|
|
20
|
|
|
2
|
|
|
|
|
7
|
|
|
2
|
|
|
|
|
756
|
|
|
2
|
|
|
|
|
23
|
|
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
1218
|
|
|
2
|
|
|
|
|
22
|
|
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
2081
|
|
|
2
|
|
|
|
|
21
|
|
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
3885
|
|
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
# recursive comparison expression building |
230
|
|
|
|
|
|
|
sub _cmp { |
231
|
18378
|
|
|
18378
|
|
31505
|
my ($cmp, $globs, $i, $j) = splice(@_, 0, 4); |
232
|
18378
|
50
|
|
|
|
54946
|
return $globs ? "(\$r=\$n$i,\$n$i=<\$i$i>)" : "(\$r=\$n$i,\$n$i=&\$i$i)" unless defined $j; |
|
|
100
|
|
|
|
|
|
233
|
9170
|
|
|
|
|
19463
|
"(!defined\$n$j||defined\$n$i&&\$n$i$cmp\$n$j)?". _cmp($cmp, $globs, $i, @_). ":". _cmp($cmp, $globs, $j, @_) |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
1 |