line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Math::Vector::Real::kdTree; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
our $VERSION = '0.14'; |
4
|
|
|
|
|
|
|
|
5
|
3
|
|
|
3
|
|
17491
|
use 5.010; |
|
3
|
|
|
|
|
8
|
|
|
3
|
|
|
|
|
93
|
|
6
|
3
|
|
|
3
|
|
11
|
use strict; |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
77
|
|
7
|
3
|
|
|
3
|
|
12
|
use warnings; |
|
3
|
|
|
|
|
8
|
|
|
3
|
|
|
|
|
80
|
|
8
|
3
|
|
|
3
|
|
15
|
use Carp; |
|
3
|
|
|
|
|
3
|
|
|
3
|
|
|
|
|
222
|
|
9
|
|
|
|
|
|
|
|
10
|
3
|
|
|
3
|
|
609
|
use Math::Vector::Real; |
|
3
|
|
|
|
|
14981
|
|
|
3
|
|
|
|
|
146
|
|
11
|
3
|
|
|
3
|
|
639
|
use Sort::Key::Top qw(nkeypartref nhead ntail nkeyhead); |
|
3
|
|
|
|
|
2338
|
|
|
3
|
|
|
|
|
295
|
|
12
|
3
|
|
|
3
|
|
2443
|
use Hash::Util::FieldHash qw(idhash); |
|
3
|
|
|
|
|
2254
|
|
|
3
|
|
|
|
|
183
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
our $max_per_pole = 12; |
15
|
|
|
|
|
|
|
our $recommended_per_pole = 6; |
16
|
|
|
|
|
|
|
|
17
|
3
|
|
|
3
|
|
15
|
use constant _n => 0; # elements on subtree |
|
3
|
|
|
|
|
3
|
|
|
3
|
|
|
|
|
176
|
|
18
|
3
|
|
|
3
|
|
13
|
use constant _c0 => 1; # corner 0 |
|
3
|
|
|
|
|
4
|
|
|
3
|
|
|
|
|
102
|
|
19
|
3
|
|
|
3
|
|
11
|
use constant _c1 => 2; # corner 1 |
|
3
|
|
|
|
|
3
|
|
|
3
|
|
|
|
|
101
|
|
20
|
3
|
|
|
3
|
|
12
|
use constant _sum => 3; # centroid * n |
|
3
|
|
|
|
|
2
|
|
|
3
|
|
|
|
|
98
|
|
21
|
3
|
|
|
3
|
|
9
|
use constant _s0 => 4; # subtree 0 |
|
3
|
|
|
|
|
2
|
|
|
3
|
|
|
|
|
83
|
|
22
|
3
|
|
|
3
|
|
9
|
use constant _s1 => 5; # subtree 1 |
|
3
|
|
|
|
|
3
|
|
|
3
|
|
|
|
|
87
|
|
23
|
3
|
|
|
3
|
|
14
|
use constant _axis => 6; # cut axis |
|
3
|
|
|
|
|
3
|
|
|
3
|
|
|
|
|
84
|
|
24
|
3
|
|
|
3
|
|
9
|
use constant _cut => 7; # cut point (mediam) |
|
3
|
|
|
|
|
2
|
|
|
3
|
|
|
|
|
81
|
|
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
# on leaf nodes: |
27
|
3
|
|
|
3
|
|
9
|
use constant _ixs => 4; |
|
3
|
|
|
|
|
2
|
|
|
3
|
|
|
|
|
125
|
|
28
|
3
|
|
|
3
|
|
10
|
use constant _leaf_size => _ixs + 1; |
|
3
|
|
|
|
|
2
|
|
|
3
|
|
|
|
|
17612
|
|
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
sub new { |
31
|
683
|
|
|
683
|
1
|
196004329
|
my $class = shift; |
32
|
683
|
|
|
|
|
2992
|
my @v = map V(@$_), @_; |
33
|
683
|
100
|
|
|
|
87371
|
my $self = { vs => \@v, |
34
|
|
|
|
|
|
|
tree => (@v ? _build(\@v, [0..$#v]) : undef) }; |
35
|
683
|
|
|
|
|
25302
|
bless $self, $class; |
36
|
|
|
|
|
|
|
} |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
sub clone { |
39
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
40
|
0
|
|
|
|
|
0
|
require Storable; |
41
|
0
|
|
|
|
|
0
|
my $clone = { vs => [@{$self->{vs}}], |
|
0
|
|
|
|
|
0
|
|
42
|
|
|
|
|
|
|
tree => Storable::dclone($self->{tree}) }; |
43
|
0
|
0
|
|
|
|
0
|
$clone->{hidden} = { %{$self->{hidden}} } if $self->{hidden}; |
|
0
|
|
|
|
|
0
|
|
44
|
0
|
|
|
|
|
0
|
bless $clone, ref $self; |
45
|
|
|
|
|
|
|
} |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
sub _build { |
48
|
38802
|
|
|
38802
|
|
30793
|
my ($v, $ixs) = @_; |
49
|
38802
|
100
|
|
|
|
48378
|
if (@$ixs > $recommended_per_pole) { |
50
|
18867
|
|
|
|
|
52992
|
my ($b, $t) = Math::Vector::Real->box(@$v[@$ixs]); |
51
|
18867
|
|
|
|
|
3653673
|
my $axis = ($t - $b)->max_component_index; |
52
|
18867
|
|
|
|
|
423654
|
my $bstart = @$ixs >> 1; |
53
|
18867
|
|
|
383835
|
|
79451
|
my ($p0, $p1) = nkeypartref { $v->[$_][$axis] } $bstart => @$ixs; |
|
475061
|
|
|
|
|
531629
|
|
54
|
18867
|
|
|
|
|
41903
|
my $s0 = _build($v, $p0); |
55
|
18867
|
|
|
|
|
484811
|
my $s1 = _build($v, $p1); |
56
|
18867
|
|
|
|
|
470834
|
my ($c0, $c1) = Math::Vector::Real->box(@{$s0}[_c0, _c1], @{$s1}[_c0, _c1]); |
|
18867
|
|
|
|
|
18181
|
|
|
18867
|
|
|
|
|
30704
|
|
57
|
18867
|
|
|
|
|
605623
|
my $cut = 0.5 * ($s0->[_c1][$axis] + $s1->[_c0][$axis]); |
58
|
|
|
|
|
|
|
# [n sum s0 s1 axis cut] |
59
|
18867
|
|
|
|
|
33952
|
[scalar(@$ixs), $c0, $c1, $s0->[_sum] + $s1->[_sum], $s0, $s1, $axis, $cut]; |
60
|
|
|
|
|
|
|
} |
61
|
|
|
|
|
|
|
else { |
62
|
|
|
|
|
|
|
# [n, sum, ixs] |
63
|
19935
|
|
|
|
|
14630
|
my @vs = @{$v}[@$ixs]; |
|
19935
|
|
|
|
|
28834
|
|
64
|
19935
|
|
|
|
|
36501
|
my ($c0, $c1) = Math::Vector::Real->box(@vs); |
65
|
19935
|
|
|
|
|
707558
|
[scalar(@$ixs), $c0, $c1, Math::Vector::Real->sum(@vs), $ixs]; |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
|
69
|
0
|
|
|
0
|
1
|
0
|
sub size { scalar @{shift->{vs}} } |
|
0
|
|
|
|
|
0
|
|
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
sub at { |
72
|
38976
|
|
|
38976
|
1
|
1263520
|
my ($self, $ix) = @_; |
73
|
38976
|
|
|
|
|
49375
|
Math::Vector::Real::clone($self->{vs}[$ix]); |
74
|
|
|
|
|
|
|
} |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
sub insert { |
77
|
6536
|
|
|
6536
|
1
|
2566540
|
my $self = shift; |
78
|
6536
|
50
|
|
|
|
12559
|
@_ or return; |
79
|
6536
|
|
|
|
|
6589
|
my $vs = $self->{vs}; |
80
|
6536
|
|
|
|
|
5951
|
my $ix = @$vs; |
81
|
6536
|
100
|
|
|
|
10165
|
if (my $tree = $self->{tree}) { |
82
|
6495
|
|
|
|
|
8492
|
for (@_) { |
83
|
6495
|
|
|
|
|
18227
|
my $v = V(@$_); |
84
|
6495
|
|
|
|
|
27450
|
push @$vs, $v; |
85
|
6495
|
|
|
|
|
10463
|
_insert($vs, $self->{tree}, $#$vs) |
86
|
|
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
else { |
89
|
41
|
|
|
|
|
249
|
@$vs = map V(@$_), @_; |
90
|
41
|
|
|
|
|
394
|
$self->{tree} = _build($vs, [0..$#$vs]); |
91
|
|
|
|
|
|
|
} |
92
|
6536
|
|
|
|
|
11504
|
return $ix; |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
# _insert does not return anything but modifies its $t argument in |
96
|
|
|
|
|
|
|
# place. This is really ugly but done to improve performance. |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
sub _insert { |
99
|
35025
|
|
|
35025
|
|
32180
|
my ($vs, $t, $ix) = @_; |
100
|
35025
|
|
|
|
|
27418
|
my $v = $vs->[$ix]; |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
# update aggregated values |
103
|
35025
|
|
|
|
|
28706
|
my $n = $t->[_n]++; |
104
|
35025
|
|
|
|
|
23505
|
@{$t}[_c0, _c1] = Math::Vector::Real->box($v, @{$t}[_c0, _c1]); |
|
35025
|
|
|
|
|
974463
|
|
|
35025
|
|
|
|
|
67032
|
|
105
|
35025
|
|
|
|
|
88514
|
$t->[_sum] += $v; |
106
|
|
|
|
|
|
|
|
107
|
35025
|
100
|
|
|
|
419816
|
if (defined (my $axis = $t->[_axis])) { |
108
|
28585
|
|
|
|
|
23160
|
my $cut = $t->[_cut]; |
109
|
28585
|
|
|
|
|
22853
|
my $c = $v->[$axis]; |
110
|
|
|
|
|
|
|
|
111
|
28585
|
|
|
|
|
25257
|
my $n0 = $t->[_s0][_n]; |
112
|
28585
|
|
|
|
|
22686
|
my $n1 = $t->[_s1][_n]; |
113
|
|
|
|
|
|
|
|
114
|
28585
|
100
|
|
|
|
36940
|
if ($c <= $cut) { |
115
|
14620
|
100
|
|
|
|
24485
|
if (2 * $n1 + $max_per_pole >= $n0) { |
116
|
14588
|
|
|
|
|
18891
|
_insert($vs, $t->[_s0], $ix); |
117
|
14588
|
|
|
|
|
20439
|
return; |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
else { |
121
|
13965
|
100
|
|
|
|
22602
|
if (2 * $n0 + $max_per_pole >= $n1) { |
122
|
13942
|
|
|
|
|
17370
|
_insert($vs, $t->[_s1], $ix); |
123
|
13942
|
|
|
|
|
20352
|
return; |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
# tree needs rebalancing |
128
|
55
|
|
|
|
|
71
|
my @store; |
129
|
55
|
|
|
|
|
146
|
$#store = $n; # preallocate space |
130
|
55
|
|
|
|
|
105
|
@store = ($ix); |
131
|
55
|
|
|
|
|
176
|
_push_all($t, \@store); |
132
|
55
|
|
|
|
|
156
|
$_[1] = _build($vs, \@store); |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
else { |
135
|
6440
|
|
|
|
|
5874
|
my $ixs = $t->[_ixs]; |
136
|
6440
|
|
|
|
|
8766
|
push @$ixs, $ix; |
137
|
6440
|
100
|
|
|
|
13052
|
if ($n > $max_per_pole) { |
138
|
330
|
|
|
|
|
857
|
$_[1] = _build($vs, $ixs); |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
sub move { |
144
|
0
|
|
|
0
|
1
|
0
|
my ($self, $ix, $v) = @_; |
145
|
0
|
|
|
|
|
0
|
my $vs = $self->{vs}; |
146
|
0
|
0
|
0
|
|
|
0
|
($ix >= 0 and $ix < @$vs) or croak "index out of range"; |
147
|
0
|
|
|
|
|
0
|
_delete($vs, $self->{tree}, $ix); |
148
|
0
|
|
|
|
|
0
|
$vs->[$ix] = Math::Vector::Real::clone($v); |
149
|
0
|
|
|
|
|
0
|
_insert($vs, $self->{tree}, $ix); |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
sub _delete { |
153
|
0
|
|
|
0
|
|
0
|
my ($vs, $t, $ix) = @_; |
154
|
0
|
0
|
|
|
|
0
|
if (defined (my $axis = $t->[_axis])) { |
155
|
0
|
|
|
|
|
0
|
my $v = $vs->[$ix]; |
156
|
0
|
|
|
|
|
0
|
my $c = $v->[$axis]; |
157
|
0
|
|
|
|
|
0
|
my ($s0, $s1, $cut) = @{$t}[_s0, _s1, _cut]; |
|
0
|
|
|
|
|
0
|
|
158
|
0
|
0
|
0
|
|
|
0
|
if ($c <= $cut and _delete($vs, $s0, $ix)) { |
|
|
0
|
0
|
|
|
|
|
159
|
0
|
0
|
|
|
|
0
|
if ($s0->[_n]) { |
160
|
0
|
|
|
|
|
0
|
$t->[_n]--; |
161
|
0
|
|
|
|
|
0
|
$t->[_sum] -= $v; |
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
else { |
164
|
|
|
|
|
|
|
# when one subnode becomes empty, the other gets promoted up: |
165
|
0
|
|
|
|
|
0
|
@$t = @$s1; |
166
|
|
|
|
|
|
|
} |
167
|
0
|
|
|
|
|
0
|
return 1; |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
elsif ($c >= $cut and _delete($vs, $s1, $ix)) { |
170
|
0
|
0
|
|
|
|
0
|
if ($s1->[_n]) { |
171
|
0
|
|
|
|
|
0
|
$t->[_n]--; |
172
|
0
|
|
|
|
|
0
|
$t->[_sum] -= $v; |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
else { |
175
|
0
|
|
|
|
|
0
|
@$t = @$s0; |
176
|
|
|
|
|
|
|
} |
177
|
0
|
|
|
|
|
0
|
return 1; |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
else { |
181
|
0
|
|
|
|
|
0
|
my $ixs = $t->[_ixs]; |
182
|
0
|
|
|
|
|
0
|
for (0..$#$ixs) { |
183
|
0
|
0
|
|
|
|
0
|
if ($ixs->[$_] == $ix) { |
184
|
0
|
|
|
|
|
0
|
splice(@$ixs, $_, 1); |
185
|
0
|
|
|
|
|
0
|
$t->[_n]--; |
186
|
0
|
|
|
|
|
0
|
$t->[_sum] -= $vs->[$ix]; |
187
|
0
|
|
|
|
|
0
|
return 1; |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
} |
190
|
|
|
|
|
|
|
} |
191
|
0
|
|
|
|
|
0
|
return 0; |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
sub hide { |
195
|
0
|
|
|
0
|
0
|
0
|
my ($self, $ix) = @_; |
196
|
0
|
|
|
|
|
0
|
my $vs = $self->{vs}; |
197
|
0
|
0
|
0
|
|
|
0
|
($ix >= 0 and $ix < @$vs) or croak "index out of range"; |
198
|
0
|
|
|
|
|
0
|
_delete($vs, $self->{tree}, $ix); |
199
|
0
|
|
0
|
|
|
0
|
($self->{hidden} //= {})->{$ix} = 1; |
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
sub _push_all { |
203
|
55
|
|
|
55
|
|
88
|
my ($t, $store) = @_; |
204
|
55
|
|
|
|
|
92
|
my @q; |
205
|
55
|
|
|
|
|
156
|
while ($t) { |
206
|
987
|
100
|
|
|
|
994
|
if (defined $t->[_axis]) { |
207
|
466
|
|
|
|
|
424
|
push @q, $t->[_s1]; |
208
|
466
|
|
|
|
|
569
|
$t = $t->[_s0]; |
209
|
|
|
|
|
|
|
} |
210
|
|
|
|
|
|
|
else { |
211
|
521
|
|
|
|
|
396
|
push @$store, @{$t->[_ixs]}; |
|
521
|
|
|
|
|
675
|
|
212
|
521
|
|
|
|
|
728
|
$t = pop @q; |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
} |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
sub path { |
218
|
0
|
|
|
0
|
0
|
0
|
my ($self, $ix) = @_; |
219
|
0
|
|
|
|
|
0
|
my $p = _path($self->{vs}, $self->{tree}, $ix); |
220
|
0
|
|
|
|
|
0
|
my $l = 1; |
221
|
0
|
|
|
|
|
0
|
$l = (($l << 1) | $_) for @$p; |
222
|
0
|
|
|
|
|
0
|
$l |
223
|
|
|
|
|
|
|
} |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
sub _path { |
226
|
0
|
|
|
0
|
|
0
|
my ($vs, $t, $ix) = @_; |
227
|
0
|
0
|
|
|
|
0
|
if (defined (my $axis = $t->[_axis])) { |
228
|
0
|
|
|
|
|
0
|
my $v = $vs->[$ix]; |
229
|
0
|
|
|
|
|
0
|
my $c = $v->[$axis]; |
230
|
0
|
|
|
|
|
0
|
my $cut = $t->[_cut]; |
231
|
0
|
|
|
|
|
0
|
my $p; |
232
|
0
|
0
|
|
|
|
0
|
if ($c <= $cut) { |
233
|
0
|
0
|
|
|
|
0
|
if ($p = _path($vs, $t->[_s0], $ix)) { |
234
|
0
|
|
|
|
|
0
|
unshift @$p, 0; |
235
|
0
|
|
|
|
|
0
|
return $p; |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
} |
238
|
0
|
0
|
|
|
|
0
|
if ($c >= $cut) { |
239
|
0
|
0
|
|
|
|
0
|
if ($p = _path($vs, $t->[_s1], $ix)) { |
240
|
0
|
|
|
|
|
0
|
unshift @$p, 1; |
241
|
0
|
|
|
|
|
0
|
return $p; |
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
} |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
else { |
246
|
0
|
0
|
|
|
|
0
|
return [] if grep $_ == $ix, @{$t->[_ixs]} |
|
0
|
|
|
|
|
0
|
|
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
() |
249
|
0
|
|
|
|
|
0
|
} |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
sub find { |
252
|
0
|
|
|
0
|
0
|
0
|
my ($self, $v) = @_; |
253
|
0
|
|
|
|
|
0
|
_find($self->{vs}, $self->{tree}, $v); |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
sub _find { |
257
|
0
|
|
|
0
|
|
0
|
my ($vs, $t, $v) = @_; |
258
|
0
|
|
|
|
|
0
|
while (defined (my $axis = $t->[_axis])) { |
259
|
0
|
|
|
|
|
0
|
my $cut = $t->[_cut]; |
260
|
0
|
|
|
|
|
0
|
my $c = $v->[$axis]; |
261
|
0
|
0
|
|
|
|
0
|
if ($c < $cut) { |
262
|
0
|
|
|
|
|
0
|
$t = $t->[_s0]; |
263
|
|
|
|
|
|
|
} |
264
|
|
|
|
|
|
|
else { |
265
|
0
|
0
|
|
|
|
0
|
if ($c == $cut) { |
266
|
0
|
|
|
|
|
0
|
my $ix = _find($vs, $t->[_s0], $v); |
267
|
0
|
0
|
|
|
|
0
|
return $ix if defined $ix; |
268
|
|
|
|
|
|
|
} |
269
|
0
|
|
|
|
|
0
|
$t = $t->[_s1]; |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
|
273
|
0
|
|
|
|
|
0
|
for (@{$t->[_ixs]}) { |
|
0
|
|
|
|
|
0
|
|
274
|
0
|
0
|
|
|
|
0
|
return $_ if $vs->[$_] == $v; |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
() |
277
|
0
|
|
|
|
|
0
|
} |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
sub find_nearest_vector { |
280
|
67728
|
|
|
67728
|
1
|
71477
|
my ($self, $v, $d, @but) = @_; |
281
|
67728
|
50
|
|
|
|
105593
|
my $t = $self->{tree} or return; |
282
|
67728
|
|
|
|
|
57172
|
my $vs = $self->{vs}; |
283
|
67728
|
50
|
|
|
|
71282
|
my $d2 = (defined $d ? $d * $d : 'inf'); |
284
|
|
|
|
|
|
|
|
285
|
67728
|
|
|
|
|
39721
|
my $but; |
286
|
67728
|
100
|
|
|
|
82837
|
if (@but) { |
287
|
12992
|
50
|
33
|
|
|
37385
|
if (@but == 1 and ref $but[0] eq 'HASH') { |
288
|
0
|
|
|
|
|
0
|
$but = $but[0]; |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
else { |
291
|
12992
|
|
|
|
|
12727
|
my %but = map { $_ => 1 } @but; |
|
12992
|
|
|
|
|
33767
|
|
292
|
12992
|
|
|
|
|
17416
|
$but = \%but; |
293
|
|
|
|
|
|
|
} |
294
|
|
|
|
|
|
|
} |
295
|
|
|
|
|
|
|
|
296
|
67728
|
|
|
|
|
80570
|
my ($rix, $rd2) = _find_nearest_vector($vs, $t, $v, $d2, undef, $but); |
297
|
67728
|
|
50
|
|
|
84550
|
$rix // return; |
298
|
67728
|
50
|
|
|
|
199479
|
wantarray ? ($rix, sqrt($rd2)) : $rix; |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
*find_nearest_neighbor = \&find_nearest_vector; # for backwards compatibility |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
sub find_nearest_vector_internal { |
304
|
12992
|
|
|
12992
|
0
|
1092089
|
my ($self, $ix, $d) = @_; |
305
|
12992
|
50
|
|
|
|
19928
|
$ix >= 0 or croak "index out of range"; |
306
|
12992
|
|
|
|
|
23832
|
$self->find_nearest_vector($self->{vs}[$ix], $d, $ix); |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
*find_nearest_neighbor_internal = \&find_nearest_vector_internal; # for backwards compatibility |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
sub _find_nearest_vector { |
312
|
92701
|
|
|
92701
|
|
92676
|
my ($vs, $t, $v, $best_d2, $best_ix, $but) = @_; |
313
|
|
|
|
|
|
|
|
314
|
92701
|
|
|
|
|
59401
|
my @queue; |
315
|
|
|
|
|
|
|
my @queue_d2; |
316
|
|
|
|
|
|
|
|
317
|
92701
|
|
|
|
|
62312
|
while (1) { |
318
|
987232
|
100
|
|
|
|
1195277
|
if (defined (my $axis = $t->[_axis])) { |
319
|
|
|
|
|
|
|
# substitute the current one by the best subtree and queue |
320
|
|
|
|
|
|
|
# the worst for later |
321
|
629640
|
100
|
|
|
|
801365
|
($t, my ($q)) = @{$t}[($v->[$axis] <= $t->[_cut]) ? (_s0, _s1) : (_s1, _s0)]; |
|
629640
|
|
|
|
|
680100
|
|
322
|
629640
|
|
|
|
|
439764
|
my $q_d2 = $v->dist2_to_box(@{$q}[_c0, _c1]); |
|
629640
|
|
|
|
|
959049
|
|
323
|
629640
|
100
|
|
|
|
23385382
|
if ($q_d2 <= $best_d2) { |
324
|
409284
|
|
|
|
|
226385
|
my $j; |
325
|
409284
|
|
|
|
|
545096
|
for ($j = $#queue_d2; $j >= 0; $j--) { |
326
|
1151156
|
100
|
|
|
|
1774762
|
last if $queue_d2[$j] >= $q_d2; |
327
|
|
|
|
|
|
|
} |
328
|
409284
|
|
|
|
|
361003
|
splice @queue, ++$j, 0, $q; |
329
|
409284
|
|
|
|
|
364512
|
splice @queue_d2, $j, 0, $q_d2; |
330
|
|
|
|
|
|
|
} |
331
|
|
|
|
|
|
|
} |
332
|
|
|
|
|
|
|
else { |
333
|
357592
|
|
|
|
|
230871
|
for (@{$t->[_ixs]}) { |
|
357592
|
|
|
|
|
426504
|
|
334
|
1709687
|
100
|
100
|
|
|
3119410
|
next if $but and $but->{$_}; |
335
|
1696695
|
|
|
|
|
2488528
|
my $d21 = $vs->[$_]->dist2($v); |
336
|
1696695
|
100
|
|
|
|
27910314
|
if ($d21 <= $best_d2) { |
337
|
262820
|
|
|
|
|
169803
|
$best_d2 = $d21; |
338
|
262820
|
|
|
|
|
256440
|
$best_ix = $_; |
339
|
|
|
|
|
|
|
} |
340
|
|
|
|
|
|
|
} |
341
|
|
|
|
|
|
|
|
342
|
357592
|
100
|
|
|
|
551985
|
if ($t = pop @queue) { |
343
|
308068
|
100
|
|
|
|
382743
|
if ($best_d2 >= pop @queue_d2) { |
344
|
264891
|
|
|
|
|
211344
|
next; |
345
|
|
|
|
|
|
|
} |
346
|
|
|
|
|
|
|
} |
347
|
|
|
|
|
|
|
|
348
|
92701
|
|
|
|
|
191599
|
return ($best_ix, $best_d2); |
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
} |
351
|
|
|
|
|
|
|
} |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
sub find_nearest_vector_in_box { |
354
|
0
|
|
|
0
|
1
|
0
|
my ($self, $v, $a, $b, $d, @but) = @_; |
355
|
|
|
|
|
|
|
|
356
|
0
|
0
|
|
|
|
0
|
my $t = $self->{tree} or return; |
357
|
0
|
|
|
|
|
0
|
my $vs = $self->{vs}; |
358
|
0
|
|
|
|
|
0
|
my ($a1, $b1) = Math::Vector::Real->box($a, $b); |
359
|
0
|
0
|
|
|
|
0
|
my $d2 = (defined $d ? $d * $d : $v->max_dist2_to_box($a1, $b1)); |
360
|
0
|
|
|
|
|
0
|
my $but; |
361
|
0
|
0
|
|
|
|
0
|
if (@but) { |
362
|
0
|
0
|
0
|
|
|
0
|
if (@but == 1 and ref $but[0] eq 'HASH') { |
363
|
0
|
|
|
|
|
0
|
$but = $but[0]; |
364
|
|
|
|
|
|
|
} |
365
|
|
|
|
|
|
|
else { |
366
|
0
|
|
|
|
|
0
|
my %but = map { $_ => 1 } @but; |
|
0
|
|
|
|
|
0
|
|
367
|
0
|
|
|
|
|
0
|
$but = \%but; |
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
} |
370
|
0
|
|
|
|
|
0
|
my ($rix, $rd2) = _find_nearest_vector_in_box($vs, $t, $v, $a1, $b1, $d2, $but); |
371
|
0
|
|
0
|
|
|
0
|
$rix // return; |
372
|
0
|
0
|
|
|
|
0
|
wantarray ? ($rix, sqrt($rd2)) : $rix; |
373
|
|
|
|
|
|
|
} |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
sub _find_nearest_vector_in_box { |
376
|
0
|
|
|
0
|
|
0
|
my ($vs, $t, $v, $a, $b, $best_d2, $but) = @_; |
377
|
0
|
|
|
|
|
0
|
my $best_ix; |
378
|
0
|
|
|
|
|
0
|
my @queue = $t; |
379
|
0
|
|
|
|
|
0
|
my @queue_d2 = 0; |
380
|
|
|
|
|
|
|
|
381
|
0
|
|
|
|
|
0
|
while (my $t = pop @queue) { |
382
|
0
|
0
|
|
|
|
0
|
last if $best_d2 < pop @queue_d2; |
383
|
0
|
0
|
|
|
|
0
|
if (defined (my $axis = $t->[_axis])) { |
384
|
0
|
|
|
|
|
0
|
my @sides; |
385
|
0
|
0
|
|
|
|
0
|
push @sides, $t->[_s0] if $a->[$axis] <= $t->[_cut]; |
386
|
0
|
0
|
|
|
|
0
|
push @sides, $t->[_s1] if $b->[$axis] >= $t->[_cut]; |
387
|
0
|
|
|
|
|
0
|
for my $s (@sides) { |
388
|
0
|
|
|
|
|
0
|
my $d2 = $v->dist2_to_box(@$s[_c0, _c1]); |
389
|
0
|
0
|
|
|
|
0
|
if ($d2 <= $best_d2) { |
390
|
0
|
|
|
|
|
0
|
my $j; |
391
|
0
|
|
|
|
|
0
|
for ($j = $#queue_d2; $j >= 0; $j--) { |
392
|
0
|
0
|
|
|
|
0
|
last if $queue_d2[$j] >= $d2; |
393
|
|
|
|
|
|
|
} |
394
|
0
|
|
|
|
|
0
|
splice @queue, ++$j, 0, $s; |
395
|
0
|
|
|
|
|
0
|
splice @queue_d2, $j, 0, $d2; |
396
|
|
|
|
|
|
|
} |
397
|
|
|
|
|
|
|
} |
398
|
|
|
|
|
|
|
} |
399
|
|
|
|
|
|
|
else { |
400
|
0
|
|
|
|
|
0
|
for (@{$t->[_ixs]}) { |
|
0
|
|
|
|
|
0
|
|
401
|
0
|
0
|
0
|
|
|
0
|
next if $but and $but->{$_}; |
402
|
0
|
|
|
|
|
0
|
my $v1 = $vs->[$_]; |
403
|
0
|
|
|
|
|
0
|
my $d2 = $v1->dist2($v); |
404
|
0
|
0
|
0
|
|
|
0
|
if ($d2 <= $best_d2 and $v1->dist2_to_box($a, $b) == 0) { |
405
|
0
|
|
|
|
|
0
|
$best_d2 = $d2; |
406
|
0
|
|
|
|
|
0
|
$best_ix = $_; |
407
|
|
|
|
|
|
|
} |
408
|
|
|
|
|
|
|
} |
409
|
|
|
|
|
|
|
} |
410
|
|
|
|
|
|
|
} |
411
|
0
|
|
|
|
|
0
|
return ($best_ix, $best_d2); |
412
|
|
|
|
|
|
|
} |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
sub find_nearest_vector_all_internal { |
415
|
80
|
|
|
80
|
1
|
2140002
|
my ($self, $d) = @_; |
416
|
80
|
|
|
|
|
220
|
my $vs = $self->{vs}; |
417
|
80
|
50
|
|
|
|
288
|
return unless @$vs > 1; |
418
|
80
|
50
|
|
|
|
214
|
my $d2 = (defined $d ? $d * $d : 'inf'); |
419
|
|
|
|
|
|
|
|
420
|
80
|
|
|
|
|
661
|
my @best = ((undef) x @$vs); |
421
|
80
|
|
|
|
|
2749
|
my @d2 = (($d2) x @$vs); |
422
|
80
|
|
|
|
|
339
|
_find_nearest_vector_all_internal($vs, $self->{tree}, \@best, \@d2); |
423
|
80
|
|
|
|
|
11331
|
return @best; |
424
|
|
|
|
|
|
|
} |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
*find_nearest_neighbor_all_internal = \&find_nearest_vector_all_internal; # for backwards compatibility |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
sub _find_nearest_vector_all_internal { |
429
|
5452
|
|
|
5452
|
|
4759
|
my ($vs, $t, $bests, $d2s) = @_; |
430
|
5452
|
100
|
|
|
|
7527
|
if (defined (my $axis = $t->[_axis])) { |
431
|
2686
|
|
|
|
|
1882
|
my @all_leafs; |
432
|
2686
|
|
|
|
|
2306
|
for my $side (0, 1) { |
433
|
5372
|
|
|
|
|
8411
|
my @leafs = _find_nearest_vector_all_internal($vs, $t->[_s0 + $side], $bests, $d2s); |
434
|
5372
|
|
|
|
|
6739
|
my $other = $t->[_s1 - $side]; |
435
|
5372
|
|
|
|
|
4275
|
my ($c0, $c1) = @{$other}[_c0, _c1]; |
|
5372
|
|
|
|
|
7185
|
|
436
|
5372
|
|
|
|
|
5931
|
for my $leaf (@leafs) { |
437
|
17310
|
|
|
|
|
178786
|
for my $ix (@{$leaf->[_ixs]}) { |
|
17310
|
|
|
|
|
20619
|
|
438
|
79334
|
|
|
|
|
894814
|
my $v = $vs->[$ix]; |
439
|
79334
|
100
|
|
|
|
112879
|
if ($v->dist2_to_box($c0, $c1) < $d2s->[$ix]) { |
440
|
24973
|
|
|
|
|
1008001
|
($bests->[$ix], $d2s->[$ix]) = |
441
|
|
|
|
|
|
|
_find_nearest_vector($vs, $other, $v, $d2s->[$ix], $bests->[$ix]); |
442
|
|
|
|
|
|
|
} |
443
|
|
|
|
|
|
|
} |
444
|
|
|
|
|
|
|
} |
445
|
5372
|
|
|
|
|
79199
|
push @all_leafs, @leafs; |
446
|
|
|
|
|
|
|
} |
447
|
2686
|
|
|
|
|
6585
|
return @all_leafs; |
448
|
|
|
|
|
|
|
} |
449
|
|
|
|
|
|
|
else { |
450
|
2766
|
|
|
|
|
2515
|
my $ixs = $t->[_ixs]; |
451
|
2766
|
|
|
|
|
4730
|
for my $i (1 .. $#$ixs) { |
452
|
10226
|
|
|
|
|
9340
|
my $ix_i = $ixs->[$i]; |
453
|
10226
|
|
|
|
|
8719
|
my $v_i = $vs->[$ix_i]; |
454
|
10226
|
|
|
|
|
11240
|
for my $ix_j (@{$ixs}[0 .. $i - 1]) { |
|
10226
|
|
|
|
|
10802
|
|
455
|
29069
|
|
|
|
|
39880
|
my $d2 = $v_i->dist2($vs->[$ix_j]); |
456
|
29069
|
100
|
|
|
|
381912
|
if ($d2 < $d2s->[$ix_i]) { |
457
|
15776
|
|
|
|
|
11929
|
$d2s->[$ix_i] = $d2; |
458
|
15776
|
|
|
|
|
16739
|
$bests->[$ix_i] = $ix_j; |
459
|
|
|
|
|
|
|
} |
460
|
29069
|
100
|
|
|
|
44710
|
if ($d2 < $d2s->[$ix_j]) { |
461
|
8575
|
|
|
|
|
6156
|
$d2s->[$ix_j] = $d2; |
462
|
8575
|
|
|
|
|
13030
|
$bests->[$ix_j] = $ix_i; |
463
|
|
|
|
|
|
|
} |
464
|
|
|
|
|
|
|
} |
465
|
|
|
|
|
|
|
} |
466
|
2766
|
|
|
|
|
4785
|
return $t; |
467
|
|
|
|
|
|
|
} |
468
|
|
|
|
|
|
|
} |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
sub find_two_nearest_vectors { |
471
|
418
|
|
|
418
|
1
|
1058933
|
my $self = shift; |
472
|
418
|
50
|
|
|
|
1562
|
my $t = $self->{tree} or return; |
473
|
418
|
|
|
|
|
571
|
my $vs = $self->{vs}; |
474
|
418
|
50
|
|
|
|
1224
|
if (my ($rix0, $rix1, $rd2) = _find_two_nearest_vectors($vs, $t)) { |
475
|
418
|
50
|
|
|
|
2239
|
return wantarray ? ($rix0, $rix1, sqrt($rd2)) : sqrt($rd2) |
476
|
|
|
|
|
|
|
} |
477
|
|
|
|
|
|
|
() |
478
|
0
|
|
|
|
|
0
|
} |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
sub _pole_id { |
481
|
0
|
|
|
0
|
|
0
|
my ($id, $deep) = __pole_id(@_); |
482
|
0
|
|
|
|
|
0
|
"$id/$deep"; |
483
|
|
|
|
|
|
|
} |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
sub __pole_id { |
486
|
0
|
|
|
0
|
|
0
|
my ($vs, $t) = @_; |
487
|
0
|
0
|
|
|
|
0
|
if (defined $t->[_axis]) { |
488
|
0
|
|
|
|
|
0
|
my ($id, $deep) = __pole_id($vs, $t->[_s0]); |
489
|
0
|
|
|
|
|
0
|
return ($id, $deep+1); |
490
|
|
|
|
|
|
|
} |
491
|
0
|
|
|
|
|
0
|
return ($t->[_ixs][0], 0) |
492
|
|
|
|
|
|
|
} |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
sub _find_two_nearest_vectors { |
495
|
418
|
|
|
418
|
|
507
|
my ($vs, $t) = @_; |
496
|
|
|
|
|
|
|
|
497
|
418
|
|
|
|
|
829
|
my @best_ixs = (undef, undef); |
498
|
418
|
|
|
|
|
423
|
my $best_d2 = 'inf' + 0; |
499
|
|
|
|
|
|
|
|
500
|
418
|
|
|
|
|
470
|
my @inner; |
501
|
|
|
|
|
|
|
my @queue_t1; |
502
|
0
|
|
|
|
|
0
|
my @queue_t2; |
503
|
418
|
|
|
|
|
864
|
while ($t) { |
504
|
30982
|
100
|
|
|
|
36283
|
if (defined $t->[_axis]) { |
505
|
15282
|
|
|
|
|
10218
|
my ($s0, $s1) = @{$t}[_s0, _s1]; |
|
15282
|
|
|
|
|
16593
|
|
506
|
15282
|
|
|
|
|
12031
|
push @inner, $s1; |
507
|
15282
|
|
|
|
|
9561
|
push @queue_t1, $s0; |
508
|
15282
|
|
|
|
|
9278
|
push @queue_t2, $s1; |
509
|
15282
|
|
|
|
|
18469
|
$t = $s0; |
510
|
|
|
|
|
|
|
} |
511
|
|
|
|
|
|
|
else { |
512
|
15700
|
|
|
|
|
12872
|
my $ixs = $t->[_ixs]; |
513
|
15700
|
|
|
|
|
17538
|
for my $i (1 .. $#$ixs) { |
514
|
52263
|
|
|
|
|
42505
|
my $ix1 = $ixs->[$i]; |
515
|
52263
|
|
|
|
|
42296
|
my $v1 = $vs->[$ix1]; |
516
|
52263
|
|
|
|
|
43557
|
for my $j (0 .. $i - 1) { |
517
|
122384
|
|
|
|
|
84641
|
my $ix2 = $ixs->[$j]; |
518
|
122384
|
|
|
|
|
149341
|
my $d2 = Math::Vector::Real::dist2($v1, $vs->[$ix2]); |
519
|
122384
|
100
|
|
|
|
1586744
|
if ($d2 < $best_d2) { |
520
|
1970
|
|
|
|
|
1467
|
$best_d2 = $d2; |
521
|
1970
|
|
|
|
|
3438
|
@best_ixs = ($ix1, $ix2); |
522
|
|
|
|
|
|
|
} |
523
|
|
|
|
|
|
|
} |
524
|
|
|
|
|
|
|
} |
525
|
15700
|
|
|
|
|
25830
|
$t = pop @inner; |
526
|
|
|
|
|
|
|
} |
527
|
|
|
|
|
|
|
} |
528
|
|
|
|
|
|
|
|
529
|
418
|
|
|
|
|
2546
|
my @queue_d2 = (0) x @queue_t1; |
530
|
418
|
|
|
|
|
909
|
while (my $t1 = pop @queue_t1) { |
531
|
132479
|
|
|
|
|
105274
|
my $t2 = pop @queue_t2; |
532
|
132479
|
|
|
|
|
108954
|
my $d2 = pop @queue_d2; |
533
|
132479
|
100
|
|
|
|
165525
|
if ($d2 < $best_d2) { |
534
|
128830
|
100
|
|
|
|
183326
|
unless (defined $t1->[_axis]) { |
535
|
40432
|
100
|
|
|
|
51974
|
unless (defined $t2->[_axis]) { |
536
|
39017
|
|
|
|
|
23175
|
for my $ix1 (@{$t1->[_ixs]}) { |
|
39017
|
|
|
|
|
49113
|
|
537
|
178496
|
|
|
|
|
153471
|
my $v1 = $vs->[$ix1]; |
538
|
178496
|
|
|
|
|
107807
|
for my $ix2 (@{$t2->[_ixs]}) { |
|
178496
|
|
|
|
|
172061
|
|
539
|
848356
|
|
|
|
|
1028692
|
my $d2 = Math::Vector::Real::dist2($v1, $vs->[$ix2]); |
540
|
848356
|
100
|
|
|
|
14110155
|
if ($d2 < $best_d2) { |
541
|
114
|
|
|
|
|
134
|
$best_d2 = $d2; |
542
|
114
|
|
|
|
|
268
|
@best_ixs = ($ix1, $ix2); |
543
|
|
|
|
|
|
|
} |
544
|
|
|
|
|
|
|
} |
545
|
|
|
|
|
|
|
} |
546
|
39017
|
|
|
|
|
82731
|
next; |
547
|
|
|
|
|
|
|
} |
548
|
1415
|
|
|
|
|
1631
|
($t1, $t2) = ($t2, $t1); |
549
|
|
|
|
|
|
|
} |
550
|
89813
|
|
|
|
|
60717
|
for my $s (@{$t1}[_s0, _s1]) { |
|
89813
|
|
|
|
|
96749
|
|
551
|
179626
|
|
|
|
|
116774
|
my $d2 = Math::Vector::Real->dist2_between_boxes(@{$s}[_c0, _c1], @{$t2}[_c0, _c1]); |
|
179626
|
|
|
|
|
152283
|
|
|
179626
|
|
|
|
|
294774
|
|
552
|
179626
|
100
|
|
|
|
11921856
|
if ($d2) { |
553
|
171654
|
100
|
|
|
|
271014
|
if ($d2 < $best_d2) { |
554
|
109225
|
|
|
|
|
91035
|
unshift @queue_t1, $t2; |
555
|
109225
|
|
|
|
|
70893
|
unshift @queue_t2, $s; |
556
|
109225
|
|
|
|
|
187176
|
unshift @queue_d2, $d2; |
557
|
|
|
|
|
|
|
} |
558
|
|
|
|
|
|
|
} |
559
|
|
|
|
|
|
|
else { |
560
|
7972
|
|
|
|
|
6697
|
push @queue_t1, $t2; |
561
|
7972
|
|
|
|
|
5150
|
push @queue_t2, $s; |
562
|
7972
|
|
|
|
|
13456
|
push @queue_d2, 0; |
563
|
|
|
|
|
|
|
} |
564
|
|
|
|
|
|
|
} |
565
|
|
|
|
|
|
|
} |
566
|
|
|
|
|
|
|
} |
567
|
418
|
|
|
|
|
1745
|
(@best_ixs, $best_d2) |
568
|
|
|
|
|
|
|
} |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
sub find_in_ball { |
571
|
6496
|
|
|
6496
|
1
|
40165136
|
my ($self, $z, $d, $but) = @_; |
572
|
6496
|
50
|
33
|
|
|
42434
|
if (defined $but and ref $but ne 'HASH') { |
573
|
6496
|
|
|
|
|
24402
|
$but = { $but => 1 }; |
574
|
|
|
|
|
|
|
} |
575
|
6496
|
|
|
|
|
29074
|
_find_in_ball($self->{vs}, $self->{tree}, $z, $d * $d, $but); |
576
|
|
|
|
|
|
|
} |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
sub _find_in_ball { |
579
|
6496
|
|
|
6496
|
|
11260
|
my ($vs, $t, $z, $d2, $but) = @_; |
580
|
6496
|
|
|
|
|
6178
|
my @queue; |
581
|
6496
|
|
|
|
|
5664
|
my (@r, $r); |
582
|
|
|
|
|
|
|
|
583
|
6496
|
|
|
|
|
6862
|
while (1) { |
584
|
293645
|
100
|
|
|
|
4861269
|
if (defined (my $axis = $t->[_axis])) { |
585
|
164692
|
|
|
|
|
139139
|
my $c = $z->[$axis]; |
586
|
164692
|
|
|
|
|
133682
|
my $cut = $t->[_cut]; |
587
|
164692
|
100
|
|
|
|
186083
|
($t, my ($q)) = @{$t}[$c <= $cut ? (_s0, _s1) : (_s1, _s0)]; |
|
164692
|
|
|
|
|
226223
|
|
588
|
164692
|
100
|
|
|
|
128728
|
push @queue, $q if $z->dist2_to_box(@{$q}[_c0, _c1]) <= $d2; |
|
164692
|
|
|
|
|
285284
|
|
589
|
|
|
|
|
|
|
} |
590
|
|
|
|
|
|
|
else { |
591
|
128953
|
|
|
|
|
110178
|
my $ixs = $t->[_ixs]; |
592
|
128953
|
50
|
|
|
|
122362
|
if (wantarray) { |
593
|
128953
|
|
|
|
|
125278
|
push @r, grep { $vs->[$_]->dist2($z) <= $d2 } @$ixs; |
|
747569
|
|
|
|
|
8172309
|
|
594
|
|
|
|
|
|
|
} |
595
|
|
|
|
|
|
|
else { |
596
|
0
|
0
|
|
|
|
0
|
$r += ( $but |
597
|
0
|
|
|
|
|
0
|
? grep { !$but->{$_} and $vs->[$_]->dist2($z) <= $d2 } @$ixs |
598
|
0
|
0
|
|
|
|
0
|
: grep { $vs->[$_]->dist2($z) <= $d2 } @$ixs ); |
599
|
|
|
|
|
|
|
} |
600
|
|
|
|
|
|
|
|
601
|
128953
|
100
|
|
|
|
1712745
|
$t = pop @queue or last; |
602
|
|
|
|
|
|
|
} |
603
|
|
|
|
|
|
|
} |
604
|
|
|
|
|
|
|
|
605
|
6496
|
50
|
|
|
|
11102
|
if (wantarray) { |
606
|
6496
|
50
|
|
|
|
9936
|
if ($but) { |
607
|
6496
|
|
|
|
|
261263
|
return grep !$but->{$_}, @r; |
608
|
|
|
|
|
|
|
} |
609
|
0
|
|
|
|
|
0
|
return @r; |
610
|
|
|
|
|
|
|
} |
611
|
0
|
|
|
|
|
0
|
return $r; |
612
|
|
|
|
|
|
|
} |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
sub find_farthest_vector { |
615
|
6496
|
|
|
6496
|
1
|
8268
|
my ($self, $v, $d, @but) = @_; |
616
|
6496
|
50
|
|
|
|
11477
|
my $t = $self->{tree} or return; |
617
|
6496
|
|
|
|
|
5436
|
my $vs = $self->{vs}; |
618
|
6496
|
50
|
|
|
|
6764
|
my $d2 = ($d ? $d * $d : -1); |
619
|
6496
|
|
|
|
|
4027
|
my $but; |
620
|
6496
|
50
|
|
|
|
9520
|
if (@but) { |
621
|
6496
|
50
|
33
|
|
|
18460
|
if (@but == 1 and ref $but[0] eq 'HASH') { |
622
|
0
|
|
|
|
|
0
|
$but = $but[0]; |
623
|
|
|
|
|
|
|
} |
624
|
|
|
|
|
|
|
else { |
625
|
6496
|
|
|
|
|
6723
|
my %but = map { $_ => 1 } @but; |
|
6496
|
|
|
|
|
17409
|
|
626
|
6496
|
|
|
|
|
9001
|
$but = \%but; |
627
|
|
|
|
|
|
|
} |
628
|
|
|
|
|
|
|
} |
629
|
|
|
|
|
|
|
|
630
|
6496
|
|
|
|
|
8752
|
my ($rix, $rd2) = _find_farthest_vector($vs, $t, $v, $d2, undef, $but); |
631
|
6496
|
|
50
|
|
|
10976
|
$rix // return; |
632
|
6496
|
50
|
|
|
|
31761
|
wantarray ? ($rix, sqrt($d2)) : $rix; |
633
|
|
|
|
|
|
|
} |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
sub find_farthest_vector_internal { |
636
|
6496
|
|
|
6496
|
1
|
17626267
|
my ($self, $ix, $d) = @_; |
637
|
6496
|
50
|
|
|
|
10533
|
$ix >= 0 or croak "index out of range"; |
638
|
6496
|
|
|
|
|
12636
|
$self->find_farthest_vector($self->{vs}[$ix], $d, $ix); |
639
|
|
|
|
|
|
|
} |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
sub _find_farthest_vector { |
642
|
6496
|
|
|
6496
|
|
5825
|
my ($vs, $t, $v, $best_d2, $best_ix, $but) = @_; |
643
|
|
|
|
|
|
|
|
644
|
6496
|
|
|
|
|
4333
|
my @queue; |
645
|
|
|
|
|
|
|
my @queue_d2; |
646
|
|
|
|
|
|
|
|
647
|
6496
|
|
|
|
|
3966
|
while (1) { |
648
|
193019
|
100
|
|
|
|
241587
|
if (defined (my $axis = $t->[_axis])) { |
649
|
|
|
|
|
|
|
# substitute the current one by the best subtree and queue |
650
|
|
|
|
|
|
|
# the worst for later |
651
|
122190
|
100
|
|
|
|
159419
|
($t, my ($q)) = @{$t}[($v->[$axis] >= $t->[_cut]) ? (_s0, _s1) : (_s1, _s0)]; |
|
122190
|
|
|
|
|
138594
|
|
652
|
122190
|
|
|
|
|
93138
|
my $q_d2 = $v->max_dist2_to_box(@{$q}[_c0, _c1]); |
|
122190
|
|
|
|
|
196378
|
|
653
|
122190
|
100
|
|
|
|
5045889
|
if ($q_d2 >= $best_d2) { |
654
|
89157
|
|
|
|
|
53630
|
my $j; |
655
|
89157
|
|
|
|
|
131294
|
for ($j = $#queue_d2; $j >= 0; $j--) { |
656
|
497071
|
100
|
|
|
|
775816
|
last if $queue_d2[$j] <= $q_d2; |
657
|
|
|
|
|
|
|
} |
658
|
89157
|
|
|
|
|
80488
|
splice @queue, ++$j, 0, $q; |
659
|
89157
|
|
|
|
|
84842
|
splice @queue_d2, $j, 0, $q_d2; |
660
|
|
|
|
|
|
|
} |
661
|
|
|
|
|
|
|
} |
662
|
|
|
|
|
|
|
else { |
663
|
70829
|
|
|
|
|
41154
|
for (@{$t->[_ixs]}) { |
|
70829
|
|
|
|
|
95927
|
|
664
|
478679
|
100
|
66
|
|
|
1254610
|
next if $but and $but->{$_}; |
665
|
478506
|
|
|
|
|
708382
|
my $d21 = $vs->[$_]->dist2($v); |
666
|
478506
|
100
|
|
|
|
8449737
|
if ($d21 >= $best_d2) { |
667
|
54622
|
|
|
|
|
32861
|
$best_d2 = $d21; |
668
|
54622
|
|
|
|
|
51160
|
$best_ix = $_; |
669
|
|
|
|
|
|
|
} |
670
|
|
|
|
|
|
|
} |
671
|
|
|
|
|
|
|
|
672
|
70829
|
100
|
|
|
|
106037
|
if ($t = pop @queue) { |
673
|
69749
|
100
|
|
|
|
86771
|
if ($best_d2 <= pop @queue_d2) { |
674
|
64333
|
|
|
|
|
59415
|
next; |
675
|
|
|
|
|
|
|
} |
676
|
|
|
|
|
|
|
} |
677
|
6496
|
|
|
|
|
15348
|
return ($best_ix, $best_d2); |
678
|
|
|
|
|
|
|
} |
679
|
|
|
|
|
|
|
} |
680
|
|
|
|
|
|
|
} |
681
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
sub find_random_vector { |
683
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
684
|
0
|
0
|
|
|
|
0
|
my $t = $self->{tree} or return; |
685
|
0
|
|
|
|
|
0
|
my $vs = $self->{vs}; |
686
|
0
|
|
|
|
|
0
|
my $hidden = $self->{hidden}; |
687
|
0
|
0
|
0
|
|
|
0
|
if (not $hidden or @$vs > 20 * keys(%$hidden)) { |
688
|
|
|
|
|
|
|
# pick directly when the hidden elements are less than 5% of the total |
689
|
0
|
|
|
|
|
0
|
while (1) { |
690
|
0
|
|
|
|
|
0
|
my $ix = int rand @$vs; |
691
|
0
|
0
|
0
|
|
|
0
|
return $ix unless $hidden and $hidden->{$ix}; |
692
|
|
|
|
|
|
|
} |
693
|
|
|
|
|
|
|
} |
694
|
0
|
|
|
|
|
0
|
_find_random_vector($vs, $t); |
695
|
|
|
|
|
|
|
} |
696
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
sub _find_random_vector { |
698
|
0
|
|
|
0
|
|
0
|
my ($vs, $t) = @_; |
699
|
0
|
|
|
|
|
0
|
while (defined $t->[_axis]) { |
700
|
0
|
0
|
|
|
|
0
|
$t = $t->[rand($t->[_n]) < $t->[_s0][_n] ? _s0 : _s1]; |
701
|
|
|
|
|
|
|
} |
702
|
0
|
|
|
|
|
0
|
$t->[_ixs][rand $t->[_n]] |
703
|
|
|
|
|
|
|
} |
704
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
sub k_means_seed { |
706
|
224
|
|
|
224
|
1
|
17720125
|
my ($self, $n_req) = @_; |
707
|
224
|
50
|
|
|
|
762
|
$n_req = int($n_req) or return; |
708
|
224
|
50
|
|
|
|
790
|
my $t = $self->{tree} or return; |
709
|
224
|
|
|
|
|
369
|
my $vs = $self->{vs}; |
710
|
224
|
|
|
|
|
514
|
_k_means_seed($vs, $t, $n_req); |
711
|
|
|
|
|
|
|
} |
712
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
*k_means_start = \&k_means_seed; |
714
|
|
|
|
|
|
|
|
715
|
|
|
|
|
|
|
sub _k_means_seed { |
716
|
7486
|
|
|
7486
|
|
18430
|
my ($vs, $t, $n_req) = @_; |
717
|
7486
|
100
|
|
|
|
6962
|
if ($n_req <= 1) { |
718
|
2295
|
100
|
|
|
|
2517
|
return if $n_req < 1; |
719
|
|
|
|
|
|
|
# print STDERR "returning centroid\n"; |
720
|
2283
|
|
|
|
|
4205
|
return $t->[_sum] / $t->[_n]; |
721
|
|
|
|
|
|
|
} |
722
|
|
|
|
|
|
|
else { |
723
|
5191
|
|
|
|
|
3687
|
my $n = $t->[_n]; |
724
|
5191
|
100
|
|
|
|
5766
|
if (defined $t->[_axis]) { |
725
|
3631
|
|
|
|
|
2540
|
my ($s0, $s1) = @{$t}[_s0, _s1]; |
|
3631
|
|
|
|
|
4682
|
|
726
|
3631
|
|
|
|
|
3697
|
my $n0 = $s0->[_n]; |
727
|
3631
|
|
|
|
|
3305
|
my $n1 = $s1->[_n]; |
728
|
3631
|
|
|
|
|
3879
|
my $n0_req = int(0.5 + $n_req * ($n0 / $n)); |
729
|
3631
|
50
|
|
|
|
4091
|
$n0_req = $n0 if $n0_req > $n0; |
730
|
3631
|
|
|
|
|
3911
|
return (_k_means_seed($vs, $s0, $n0_req), |
731
|
|
|
|
|
|
|
_k_means_seed($vs, $s1, $n_req - $n0_req)); |
732
|
|
|
|
|
|
|
} |
733
|
|
|
|
|
|
|
else { |
734
|
1560
|
|
|
|
|
1319
|
my $ixs = $t->[_ixs]; |
735
|
1560
|
|
|
|
|
1012
|
my @out; |
736
|
1560
|
|
|
|
|
1848
|
for (0..$#$ixs) { |
737
|
10631
|
100
|
|
|
|
18256
|
push @out, $vs->[$ixs->[$_]] |
738
|
|
|
|
|
|
|
if rand($n - $_) < ($n_req - @out); |
739
|
|
|
|
|
|
|
} |
740
|
|
|
|
|
|
|
# print STDERR "asked for $n_req elements, returning ".scalar(@out)."\n"; |
741
|
|
|
|
|
|
|
|
742
|
1560
|
|
|
|
|
3811
|
return @out; |
743
|
|
|
|
|
|
|
} |
744
|
|
|
|
|
|
|
} |
745
|
|
|
|
|
|
|
} |
746
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
our $k_means_seed_pp_test; |
748
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
sub _k_means_seed_pp_test { |
750
|
0
|
|
|
0
|
|
0
|
my ($self, $err, $kms, $players, $weights) = @_; |
751
|
0
|
|
|
|
|
0
|
my @w; |
752
|
0
|
|
|
|
|
0
|
my $last = 0; |
753
|
0
|
|
|
|
|
0
|
for my $i (0..$#$players) { |
754
|
0
|
|
|
|
|
0
|
my $p = $players->[$i]; |
755
|
0
|
|
|
|
|
0
|
my $w = $weights->[$i] - $last; |
756
|
0
|
|
|
|
|
0
|
$last = $weights->[$i]; |
757
|
|
|
|
|
|
|
|
758
|
0
|
|
|
|
|
0
|
my @store; |
759
|
0
|
0
|
|
|
|
0
|
if (ref $p) { |
760
|
0
|
|
|
|
|
0
|
_push_all($p, \@store); |
761
|
|
|
|
|
|
|
} |
762
|
|
|
|
|
|
|
else { |
763
|
0
|
|
|
|
|
0
|
@store = $p |
764
|
|
|
|
|
|
|
} |
765
|
0
|
0
|
|
|
|
0
|
if (@store) { |
766
|
0
|
|
|
|
|
0
|
$w /= @store; |
767
|
0
|
|
|
|
|
0
|
$w[$_] = $w for @store; |
768
|
|
|
|
|
|
|
} |
769
|
|
|
|
|
|
|
} |
770
|
0
|
|
|
|
|
0
|
my $vs = $self->{vs}; |
771
|
0
|
|
0
|
|
|
0
|
$w[$_] //= 0 for 0..$#$vs; |
772
|
|
|
|
|
|
|
|
773
|
0
|
|
|
|
|
0
|
$k_means_seed_pp_test->($self, $err, [map $self->{vs}[$_], @$kms], \@w); |
774
|
|
|
|
|
|
|
} |
775
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
sub k_means_seed_pp { |
777
|
0
|
|
|
0
|
0
|
0
|
my ($self, $n_req, $err) = @_; |
778
|
0
|
0
|
|
|
|
0
|
$n_req = int($n_req) or return; |
779
|
0
|
|
0
|
|
|
0
|
$err ||= 0.5; |
780
|
0
|
0
|
|
|
|
0
|
my $t = $self->{tree} or return; |
781
|
0
|
|
|
|
|
0
|
my $vs = $self->{vs}; |
782
|
0
|
|
|
|
|
0
|
my $km = $self->find_random_vector; |
783
|
|
|
|
|
|
|
|
784
|
0
|
|
|
|
|
0
|
my (@km, @d2); |
785
|
0
|
|
|
|
|
0
|
idhash my %extra; # [$min_d2, $max_d2] |
786
|
|
|
|
|
|
|
|
787
|
|
|
|
|
|
|
# my (@player, @weight, @queue); |
788
|
|
|
|
|
|
|
# $#player = @$vs; # preallocate memory |
789
|
|
|
|
|
|
|
|
790
|
0
|
|
|
|
|
0
|
my (@weight, @queue); |
791
|
0
|
|
|
|
|
0
|
$#weight = @$vs; # preallocate memory |
792
|
|
|
|
|
|
|
|
793
|
0
|
|
|
|
|
0
|
while (1) { |
794
|
0
|
|
|
|
|
0
|
push @km, $km; |
795
|
0
|
0
|
|
|
|
0
|
last unless @km < $n_req; |
796
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
# update distances |
798
|
0
|
|
|
|
|
0
|
@queue = $t; |
799
|
0
|
|
|
|
|
0
|
while (my $p = pop @queue) { |
800
|
0
|
|
|
|
|
0
|
my $kmv = $vs->[$km]; |
801
|
0
|
|
|
|
|
0
|
my ($c0, $c1) = @{$p}[_c0, _c1]; |
|
0
|
|
|
|
|
0
|
|
802
|
0
|
|
0
|
|
|
0
|
my $extra = $extra{$p} //= ['inf', 'inf']; |
803
|
0
|
|
|
|
|
0
|
my ($min_d2, $max_d2) = @$extra; |
804
|
0
|
|
|
|
|
0
|
my $min_d2_to_box = $kmv->dist2_to_box($c0, $c1); |
805
|
0
|
0
|
|
|
|
0
|
if ($max_d2 > $min_d2_to_box) { |
806
|
0
|
0
|
|
|
|
0
|
if (defined $p->[_axis]) { |
807
|
0
|
|
|
|
|
0
|
push @queue, @{$p}[_s0, _s1]; |
|
0
|
|
|
|
|
0
|
|
808
|
|
|
|
|
|
|
} |
809
|
|
|
|
|
|
|
else { |
810
|
0
|
|
|
|
|
0
|
for (@{$p->[_ixs]}) { |
|
0
|
|
|
|
|
0
|
|
811
|
0
|
|
|
|
|
0
|
my $d2 = $kmv->dist2($vs->[$_]); |
812
|
0
|
0
|
0
|
|
|
0
|
if ($d2 < ($d2[$_] //= $d2)) { |
813
|
0
|
|
|
|
|
0
|
$d2[$_] = $d2; |
814
|
|
|
|
|
|
|
} |
815
|
|
|
|
|
|
|
} |
816
|
|
|
|
|
|
|
} |
817
|
|
|
|
|
|
|
|
818
|
0
|
0
|
|
|
|
0
|
if ($min_d2_to_box < $min_d2) { |
819
|
0
|
|
|
|
|
0
|
$extra->[0] = $min_d2_to_box; |
820
|
|
|
|
|
|
|
} |
821
|
|
|
|
|
|
|
|
822
|
0
|
|
|
|
|
0
|
my $max_d2_to_box = $kmv->max_dist2_to_box($c0, $c1); |
823
|
0
|
0
|
|
|
|
0
|
if ($max_d2_to_box < $max_d2) { |
824
|
0
|
|
|
|
|
0
|
$extra->[1] = $max_d2_to_box; |
825
|
|
|
|
|
|
|
} |
826
|
|
|
|
|
|
|
} |
827
|
|
|
|
|
|
|
} |
828
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
# find players and weight them |
830
|
0
|
|
|
|
|
0
|
my $weight = 0; |
831
|
|
|
|
|
|
|
# @player = (); |
832
|
0
|
|
|
|
|
0
|
@weight = (); |
833
|
|
|
|
|
|
|
|
834
|
|
|
|
|
|
|
# @queue = $t; |
835
|
|
|
|
|
|
|
# while (my $p = pop @queue) { |
836
|
|
|
|
|
|
|
# my $extra = $extra{$p} or die "internal error: extra information missing for $p"; |
837
|
|
|
|
|
|
|
# my ($min_d2, $max_d2) = @$extra; |
838
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
# if ($max_d2 * $err < $min_d2) { |
840
|
|
|
|
|
|
|
# $weight += $p->[_n] * ($min_d2 + $max_d2) * 0.5; |
841
|
|
|
|
|
|
|
# push @weight, $weight; |
842
|
|
|
|
|
|
|
# push @player, $p; |
843
|
|
|
|
|
|
|
# } |
844
|
|
|
|
|
|
|
# else { |
845
|
|
|
|
|
|
|
# if (defined $p->[_axis]) { |
846
|
|
|
|
|
|
|
# push @queue, @{$p}[_s0, _s1]; |
847
|
|
|
|
|
|
|
# } |
848
|
|
|
|
|
|
|
# else { |
849
|
|
|
|
|
|
|
# for (@{$p->[_ixs]}) { |
850
|
|
|
|
|
|
|
# if (my $d2 = $d2[$_]) { |
851
|
|
|
|
|
|
|
# $weight += $d2; |
852
|
|
|
|
|
|
|
# push @weight, $weight; |
853
|
|
|
|
|
|
|
# push @player, $_; |
854
|
|
|
|
|
|
|
# } |
855
|
|
|
|
|
|
|
# } |
856
|
|
|
|
|
|
|
# } |
857
|
|
|
|
|
|
|
# } |
858
|
|
|
|
|
|
|
# } |
859
|
|
|
|
|
|
|
|
860
|
0
|
|
|
|
|
0
|
for my $ix (0..@$vs) { |
861
|
0
|
|
0
|
|
|
0
|
$weight += $d2[$ix] // 0; |
862
|
0
|
|
|
|
|
0
|
$weight[$ix] += $weight; |
863
|
|
|
|
|
|
|
} |
864
|
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
# in order to check the algorithm we have to tap it here |
866
|
|
|
|
|
|
|
# $k_means_seed_pp_test and @km > 1 and |
867
|
|
|
|
|
|
|
# $self->_k_means_seed_pp_test($err, \@km, \@player, \@weight); |
868
|
|
|
|
|
|
|
|
869
|
|
|
|
|
|
|
# to many k-means requested? |
870
|
|
|
|
|
|
|
# @player or last; |
871
|
|
|
|
|
|
|
|
872
|
|
|
|
|
|
|
# select a position on the weight queue: |
873
|
0
|
|
|
|
|
0
|
my $dice = rand($weight); |
874
|
|
|
|
|
|
|
|
875
|
|
|
|
|
|
|
# and use binary search to look for it: |
876
|
0
|
|
|
|
|
0
|
my $i = 0; |
877
|
0
|
|
|
|
|
0
|
my $j = @weight; |
878
|
0
|
|
|
|
|
0
|
while ($i < $j) { |
879
|
0
|
|
|
|
|
0
|
my $pivot = (($i + $j) >> 1); |
880
|
0
|
0
|
|
|
|
0
|
if ($weight[$pivot] < $dice) { |
881
|
0
|
|
|
|
|
0
|
$i = $pivot + 1; |
882
|
|
|
|
|
|
|
} |
883
|
|
|
|
|
|
|
else { |
884
|
0
|
|
|
|
|
0
|
$j = $pivot; |
885
|
|
|
|
|
|
|
} |
886
|
|
|
|
|
|
|
} |
887
|
|
|
|
|
|
|
#my $player = $player[$i]; |
888
|
|
|
|
|
|
|
#$km = (ref $player ? _find_random_vector($vs, $player) : $player); |
889
|
0
|
|
|
|
|
0
|
$km = $i; |
890
|
|
|
|
|
|
|
} |
891
|
0
|
|
|
|
|
0
|
return @{$vs}[@km]; |
|
0
|
|
|
|
|
0
|
|
892
|
|
|
|
|
|
|
} |
893
|
|
|
|
|
|
|
|
894
|
|
|
|
|
|
|
sub k_means_loop { |
895
|
224
|
|
|
224
|
1
|
97764
|
my ($self, @k) = @_; |
896
|
224
|
50
|
|
|
|
809
|
@k or next; |
897
|
224
|
50
|
|
|
|
685
|
my $t = $self->{tree} or next; |
898
|
224
|
|
|
|
|
308
|
my $vs = $self->{vs}; |
899
|
224
|
|
|
|
|
377
|
while (1) { |
900
|
1100
|
|
|
|
|
1104
|
my $diffs; |
901
|
1100
|
|
|
|
|
4462
|
my @n = ((0) x @k); |
902
|
1100
|
|
|
|
|
2761
|
my @sum = ((undef) x @k); |
903
|
|
|
|
|
|
|
|
904
|
1100
|
|
|
|
|
6974
|
_k_means_step($vs, $t, \@k, [0..$#k], \@n, \@sum); |
905
|
|
|
|
|
|
|
|
906
|
1100
|
|
|
|
|
34506
|
for (0..$#k) { |
907
|
32236
|
100
|
|
|
|
42053
|
if (my $n = $n[$_]) { |
908
|
30616
|
|
|
|
|
39510
|
my $k = $sum[$_] / $n; |
909
|
30616
|
100
|
|
|
|
192886
|
$diffs++ if $k != $k[$_]; |
910
|
30616
|
|
|
|
|
331456
|
$k[$_] = $k; |
911
|
|
|
|
|
|
|
} |
912
|
|
|
|
|
|
|
} |
913
|
1100
|
100
|
|
|
|
14823
|
unless ($diffs) { |
914
|
224
|
50
|
|
|
|
5202
|
return (wantarray ? @k : $k[0]); |
915
|
|
|
|
|
|
|
} |
916
|
|
|
|
|
|
|
} |
917
|
|
|
|
|
|
|
} |
918
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
sub k_means_step { |
920
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
921
|
0
|
0
|
|
|
|
0
|
@_ or return; |
922
|
0
|
0
|
|
|
|
0
|
my $t = $self->{tree} or return; |
923
|
0
|
|
|
|
|
0
|
my $vs = $self->{vs}; |
924
|
|
|
|
|
|
|
|
925
|
0
|
|
|
|
|
0
|
my @n = ((0) x @_); |
926
|
0
|
|
|
|
|
0
|
my @sum = ((undef) x @_); |
927
|
|
|
|
|
|
|
|
928
|
0
|
|
|
|
|
0
|
_k_means_step($vs, $t, \@_, [0..$#_], \@n, \@sum); |
929
|
|
|
|
|
|
|
|
930
|
0
|
|
|
|
|
0
|
for (0..$#n) { |
931
|
0
|
0
|
|
|
|
0
|
if (my $n = $n[$_]) { |
932
|
0
|
|
|
|
|
0
|
$sum[$_] /= $n; |
933
|
|
|
|
|
|
|
} |
934
|
|
|
|
|
|
|
else { |
935
|
|
|
|
|
|
|
# otherwise let the original value stay |
936
|
0
|
|
|
|
|
0
|
$sum[$_] = $_[$_]; |
937
|
|
|
|
|
|
|
} |
938
|
|
|
|
|
|
|
} |
939
|
0
|
0
|
|
|
|
0
|
wantarray ? @sum : $sum[0]; |
940
|
|
|
|
|
|
|
} |
941
|
|
|
|
|
|
|
|
942
|
|
|
|
|
|
|
sub _k_means_step { |
943
|
103062
|
|
|
103062
|
|
104078
|
my ($vs, $t, $centers, $cixs, $ns, $sums) = @_; |
944
|
103062
|
|
|
|
|
95530
|
my ($n, $sum, $c0, $c1) = @{$t}[_n, _sum, _c0, _c1]; |
|
103062
|
|
|
|
|
168842
|
|
945
|
103062
|
50
|
|
|
|
157283
|
if ($n) { |
946
|
103062
|
|
|
|
|
185150
|
my $centroid = $sum/$n; |
947
|
103062
|
|
|
0
|
|
1105401
|
my $best = nkeyhead { $centroid->dist2($centers->[$_]) } @$cixs; |
|
2135470
|
|
|
|
|
28267258
|
|
948
|
103062
|
|
|
|
|
1499614
|
my $max_d2 = Math::Vector::Real::max_dist2_to_box($centers->[$best], $c0, $c1); |
949
|
103062
|
|
|
|
|
3716956
|
my @down = grep { Math::Vector::Real::dist2_to_box($centers->[$_], $c0, $c1) <= $max_d2 } @$cixs; |
|
2135510
|
|
|
|
|
63779821
|
|
950
|
103062
|
100
|
|
|
|
3140586
|
if (@down <= 1) { |
951
|
10061
|
|
|
|
|
9324
|
$ns->[$best] += $n; |
952
|
|
|
|
|
|
|
# FIXME: M::V::R objects should support this undef + vector logic natively! |
953
|
10061
|
100
|
|
|
|
11673
|
if (defined $sums->[$best]) { |
954
|
8643
|
|
|
|
|
15917
|
$sums->[$best] += $sum; |
955
|
|
|
|
|
|
|
} |
956
|
|
|
|
|
|
|
else { |
957
|
1418
|
|
|
|
|
2873
|
$sums->[$best] = V(@$sum); |
958
|
|
|
|
|
|
|
} |
959
|
|
|
|
|
|
|
} |
960
|
|
|
|
|
|
|
else { |
961
|
93001
|
100
|
|
|
|
141657
|
if (defined (my $axis = $t->[_axis])) { |
962
|
50981
|
|
|
|
|
40594
|
my ($s0, $s1) = @{$t}[_s0, _s1]; |
|
50981
|
|
|
|
|
64287
|
|
963
|
50981
|
|
|
|
|
106687
|
_k_means_step($vs, $t->[_s0], $centers, \@down, $ns, $sums); |
964
|
50981
|
|
|
|
|
788327
|
_k_means_step($vs, $t->[_s1], $centers, \@down, $ns, $sums); |
965
|
|
|
|
|
|
|
} |
966
|
|
|
|
|
|
|
else { |
967
|
42020
|
|
|
|
|
32369
|
for my $ix (@{$t->[_ixs]}) { |
|
42020
|
|
|
|
|
71303
|
|
968
|
267768
|
|
|
|
|
2383476
|
my $v = $vs->[$ix]; |
969
|
267768
|
|
|
0
|
|
852924
|
my $best = nkeyhead { $v->dist2($centers->[$_]) } @down; |
|
4578442
|
|
|
|
|
68749522
|
|
970
|
267768
|
|
|
|
|
3771521
|
$ns->[$best]++; |
971
|
267768
|
100
|
|
|
|
323895
|
if (defined $sums->[$best]) { |
972
|
238570
|
|
|
|
|
360657
|
$sums->[$best] += $v; |
973
|
|
|
|
|
|
|
} |
974
|
|
|
|
|
|
|
else { |
975
|
29198
|
|
|
|
|
58522
|
$sums->[$best] = V(@$v); |
976
|
|
|
|
|
|
|
} |
977
|
|
|
|
|
|
|
} |
978
|
|
|
|
|
|
|
} |
979
|
|
|
|
|
|
|
} |
980
|
|
|
|
|
|
|
} |
981
|
|
|
|
|
|
|
} |
982
|
|
|
|
|
|
|
|
983
|
|
|
|
|
|
|
sub k_means_assign { |
984
|
224
|
|
|
224
|
1
|
131509
|
my $self = shift; |
985
|
224
|
50
|
|
|
|
649
|
@_ or return; |
986
|
224
|
50
|
|
|
|
711
|
my $t = $self->{tree} or return; |
987
|
224
|
|
|
|
|
342
|
my $vs = $self->{vs}; |
988
|
|
|
|
|
|
|
|
989
|
224
|
|
|
|
|
4063
|
my @out = ((undef) x @$vs); |
990
|
224
|
|
|
|
|
1736
|
_k_means_assign($vs, $t, \@_, [0..$#_], \@out); |
991
|
224
|
|
|
|
|
9671
|
@out; |
992
|
|
|
|
|
|
|
} |
993
|
|
|
|
|
|
|
|
994
|
|
|
|
|
|
|
sub _k_means_assign { |
995
|
13660
|
|
|
13660
|
|
13330
|
my ($vs, $t, $centers, $cixs, $outs) = @_; |
996
|
13660
|
|
|
|
|
10861
|
my ($n, $sum, $c0, $c1) = @{$t}[_n, _sum, _c0, _c1]; |
|
13660
|
|
|
|
|
22885
|
|
997
|
13660
|
50
|
|
|
|
20759
|
if ($n) { |
998
|
13660
|
|
|
|
|
29661
|
my $centroid = $sum/$n; |
999
|
13660
|
|
|
0
|
|
163197
|
my $best = nkeyhead { $centroid->dist2($centers->[$_]) } @$cixs; |
|
404178
|
|
|
|
|
5469359
|
|
1000
|
13660
|
|
|
|
|
192285
|
my $max_d2 = Math::Vector::Real::max_dist2_to_box($centers->[$best], $c0, $c1); |
1001
|
13660
|
|
|
|
|
458998
|
my @down = grep { Math::Vector::Real::dist2_to_box($centers->[$_], $c0, $c1) <= $max_d2 } @$cixs; |
|
404218
|
|
|
|
|
12247312
|
|
1002
|
13660
|
100
|
|
|
|
397585
|
if (@down <= 1) { |
1003
|
1412
|
|
|
|
|
1927
|
_k_means_assign_1($t, $best, $outs); |
1004
|
|
|
|
|
|
|
} |
1005
|
|
|
|
|
|
|
else { |
1006
|
12248
|
100
|
|
|
|
19347
|
if (defined (my $axis = $t->[_axis])) { |
1007
|
6718
|
|
|
|
|
5844
|
my ($s0, $s1) = @{$t}[_s0, _s1]; |
|
6718
|
|
|
|
|
9542
|
|
1008
|
6718
|
|
|
|
|
13813
|
_k_means_assign($vs, $t->[_s0], $centers, \@down, $outs); |
1009
|
6718
|
|
|
|
|
21740
|
_k_means_assign($vs, $t->[_s1], $centers, \@down, $outs); |
1010
|
|
|
|
|
|
|
} |
1011
|
|
|
|
|
|
|
else { |
1012
|
5530
|
|
|
|
|
5489
|
for my $ix (@{$t->[_ixs]}) { |
|
5530
|
|
|
|
|
10350
|
|
1013
|
34208
|
|
|
|
|
40999
|
my $v = $vs->[$ix]; |
1014
|
34208
|
|
|
0
|
|
114378
|
my $best = nkeyhead { $v->dist2($centers->[$_]) } @down; |
|
853722
|
|
|
|
|
13052256
|
|
1015
|
34208
|
|
|
|
|
493155
|
$outs->[$ix] = $best; |
1016
|
|
|
|
|
|
|
} |
1017
|
|
|
|
|
|
|
} |
1018
|
|
|
|
|
|
|
} |
1019
|
|
|
|
|
|
|
} |
1020
|
|
|
|
|
|
|
} |
1021
|
|
|
|
|
|
|
|
1022
|
|
|
|
|
|
|
sub _k_means_assign_1 { |
1023
|
6416
|
|
|
6416
|
|
4830
|
my ($t, $best, $outs) = @_; |
1024
|
6416
|
100
|
|
|
|
7885
|
if (defined (my $axis = $t->[_axis])) { |
1025
|
2502
|
|
|
|
|
2718
|
_k_means_assign_1($t->[_s0], $best, $outs); |
1026
|
2502
|
|
|
|
|
2894
|
_k_means_assign_1($t->[_s1], $best, $outs); |
1027
|
|
|
|
|
|
|
} |
1028
|
|
|
|
|
|
|
else { |
1029
|
3914
|
|
|
|
|
2341
|
$outs->[$_] = $best for @{$t->[_ixs]}; |
|
3914
|
|
|
|
|
18915
|
|
1030
|
|
|
|
|
|
|
} |
1031
|
|
|
|
|
|
|
} |
1032
|
|
|
|
|
|
|
|
1033
|
|
|
|
|
|
|
sub ordered_by_proximity { |
1034
|
6536
|
|
|
6536
|
1
|
15500
|
my $self = shift; |
1035
|
6536
|
|
|
|
|
4738
|
my @r; |
1036
|
6536
|
|
|
|
|
4098
|
$#r = $#{$self->{vs}}; $#r = -1; # preallocate |
|
6536
|
|
|
|
|
13915
|
|
|
6536
|
|
|
|
|
8269
|
|
1037
|
6536
|
|
|
|
|
9468
|
_ordered_by_proximity($self->{tree}, \@r); |
1038
|
6536
|
|
|
|
|
196337
|
return @r; |
1039
|
|
|
|
|
|
|
} |
1040
|
|
|
|
|
|
|
|
1041
|
|
|
|
|
|
|
sub _ordered_by_proximity { |
1042
|
429702
|
|
|
429702
|
|
262577
|
my $t = shift; |
1043
|
429702
|
|
|
|
|
237317
|
my $r = shift; |
1044
|
429702
|
100
|
|
|
|
396285
|
if (defined $t->[_axis]) { |
1045
|
211583
|
|
|
|
|
186833
|
_ordered_by_proximity($t->[_s0], $r); |
1046
|
211583
|
|
|
|
|
202549
|
_ordered_by_proximity($t->[_s1], $r); |
1047
|
|
|
|
|
|
|
} |
1048
|
|
|
|
|
|
|
else { |
1049
|
218119
|
|
|
|
|
123370
|
push @$r, @{$t->[_ixs]} |
|
218119
|
|
|
|
|
360910
|
|
1050
|
|
|
|
|
|
|
} |
1051
|
|
|
|
|
|
|
} |
1052
|
|
|
|
|
|
|
|
1053
|
|
|
|
|
|
|
sub _dump_to_string { |
1054
|
0
|
|
|
0
|
|
|
my ($vs, $t, $indent, $opts) = @_; |
1055
|
0
|
|
|
|
|
|
my ($n, $c0, $c1, $sum) = @{$t}[_n, _c0, _c1, _sum]; |
|
0
|
|
|
|
|
|
|
1056
|
0
|
0
|
|
|
|
|
my $id = ($opts->{pole_id} ? _pole_id($vs, $t)." " : ''); |
1057
|
0
|
0
|
|
|
|
|
if (defined (my $axis = $t->[_axis])) { |
1058
|
0
|
|
|
|
|
|
my ($s0, $s1, $cut) = @{$t}[_s0, _s1, _cut]; |
|
0
|
|
|
|
|
|
|
1059
|
0
|
|
|
|
|
|
return ( "${indent}${id}n: $n, c0: $c0, c1: $c1, sum: $sum, axis: $axis, cut: $cut\n" . |
1060
|
|
|
|
|
|
|
_dump_to_string($vs, $s0, "$indent$opts->{tab}", $opts) . |
1061
|
|
|
|
|
|
|
_dump_to_string($vs, $s1, "$indent$opts->{tab}", $opts) ); |
1062
|
|
|
|
|
|
|
} |
1063
|
|
|
|
|
|
|
else { |
1064
|
0
|
|
0
|
|
|
|
my $remark = $opts->{remark} // []; |
1065
|
0
|
|
|
|
|
|
my $o = ( "${indent}${id}n: $n, c0: $c0, c1: $c1, sum: $sum\n" . |
1066
|
|
|
|
|
|
|
"${indent}$opts->{tab}ixs: [" ); |
1067
|
0
|
|
|
|
|
|
my @str; |
1068
|
0
|
|
|
|
|
|
for my $ix (@{$t->[_ixs]}) { |
|
0
|
|
|
|
|
|
|
1069
|
0
|
|
0
|
|
|
|
my $colored_ix = (@$remark and grep($ix == $_, @$remark) |
1070
|
|
|
|
|
|
|
? Term::ANSIColor::colored($ix, 'red') |
1071
|
|
|
|
|
|
|
: $ix); |
1072
|
0
|
0
|
0
|
|
|
|
if ($opts->{dump_vectors} // 1) { |
1073
|
0
|
|
|
|
|
|
push @str, "$colored_ix $vs->[$ix]"; |
1074
|
|
|
|
|
|
|
} |
1075
|
|
|
|
|
|
|
else { |
1076
|
0
|
|
|
|
|
|
push @str, $colored_ix; |
1077
|
|
|
|
|
|
|
} |
1078
|
|
|
|
|
|
|
} |
1079
|
0
|
|
|
|
|
|
return $o . join(', ', @str) . "]\n"; |
1080
|
|
|
|
|
|
|
} |
1081
|
|
|
|
|
|
|
} |
1082
|
|
|
|
|
|
|
|
1083
|
|
|
|
|
|
|
sub dump_to_string { |
1084
|
0
|
|
|
0
|
0
|
|
my ($self, %opts) = @_; |
1085
|
0
|
|
0
|
|
|
|
my $tab = $opts{tab} //= ' '; |
1086
|
0
|
|
|
|
|
|
my $vs = $self->{vs}; |
1087
|
0
|
|
|
|
|
|
my $nvs = @$vs; |
1088
|
0
|
0
|
|
|
|
|
my $hidden = join ", ", keys %{$self->{hidden} || {}}; |
|
0
|
|
|
|
|
|
|
1089
|
0
|
|
|
|
|
|
my $o = "tree: n: $nvs, hidden: {$hidden}\n"; |
1090
|
0
|
0
|
|
|
|
|
if (my $t = $self->{tree}) { |
1091
|
0
|
0
|
|
|
|
|
require Term::ANSIColor if $opts{remark}; |
1092
|
0
|
|
|
|
|
|
return $o . _dump_to_string($vs, $t, $tab, \%opts); |
1093
|
|
|
|
|
|
|
} |
1094
|
|
|
|
|
|
|
else { |
1095
|
0
|
|
|
|
|
|
return "$o${tab}(empty)\n"; |
1096
|
|
|
|
|
|
|
} |
1097
|
|
|
|
|
|
|
} |
1098
|
|
|
|
|
|
|
|
1099
|
|
|
|
|
|
|
sub dump { |
1100
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
1101
|
0
|
|
|
|
|
|
print $self->dump_to_string(@_); |
1102
|
|
|
|
|
|
|
} |
1103
|
|
|
|
|
|
|
|
1104
|
|
|
|
|
|
|
1; |
1105
|
|
|
|
|
|
|
__END__ |