File Coverage

blib/lib/Math/Vector/Real/kdTree.pm
Criterion Covered Total %
statement 445 710 62.6
branch 145 296 48.9
condition 10 70 14.2
subroutine 45 70 64.2
pod 18 26 69.2
total 663 1172 56.5


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__