File Coverage

blib/lib/Math/Vector/Real/kdTree.pm
Criterion Covered Total %
statement 445 778 57.2
branch 145 342 42.4
condition 10 84 11.9
subroutine 45 74 60.8
pod 20 28 71.4
total 665 1306 50.9


line stmt bran cond sub pod time code
1             package Math::Vector::Real::kdTree;
2              
3             our $VERSION = '0.15';
4              
5 3     3   14457 use 5.010;
  3         6  
  3         85  
6 3     3   9 use strict;
  3         3  
  3         64  
7 3     3   8 use warnings;
  3         5  
  3         68  
8 3     3   11 use Carp;
  3         3  
  3         184  
9              
10 3     3   484 use Math::Vector::Real;
  3         9543  
  3         121  
11 3     3   418 use Sort::Key::Top qw(nkeypartref nhead ntail nkeyhead);
  3         1636  
  3         243  
12 3     3   1576 use Hash::Util::FieldHash qw(idhash);
  3         2244  
  3         183  
13              
14             our $max_per_pole = 12;
15             our $recommended_per_pole = 6;
16              
17 3     3   13 use constant _n => 0; # elements on subtree
  3         3  
  3         155  
18 3     3   12 use constant _c0 => 1; # corner 0
  3         4  
  3         141  
19 3     3   11 use constant _c1 => 2; # corner 1
  3         4  
  3         109  
20 3     3   11 use constant _sum => 3; # centroid * n
  3         2  
  3         89  
21 3     3   9 use constant _s0 => 4; # subtree 0
  3         2  
  3         83  
22 3     3   10 use constant _s1 => 5; # subtree 1
  3         4  
  3         95  
23 3     3   17 use constant _axis => 6; # cut axis
  3         3  
  3         82  
24 3     3   9 use constant _cut => 7; # cut point (mediam)
  3         3  
  3         82  
25              
26             # on leaf nodes:
27 3     3   8 use constant _ixs => 4;
  3         6  
  3         117  
28 3     3   9 use constant _leaf_size => _ixs + 1;
  3         2  
  3         18461  
29              
30             sub new {
31 683     683 1 200000338 my $class = shift;
32 683         2860 my @v = map V(@$_), @_;
33 683 100       88487 my $self = { vs => \@v,
34             tree => (@v ? _build(\@v, [0..$#v]) : undef) };
35 683         24150 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 38771     38771   31171 my ($v, $ixs) = @_;
49 38771 100       49499 if (@$ixs > $recommended_per_pole) {
50 18845         51321 my ($b, $t) = Math::Vector::Real->box(@$v[@$ixs]);
51 18845         3621459 my $axis = ($t - $b)->max_component_index;
52 18845         427721 my $bstart = @$ixs >> 1;
53 18845     383835   79852 my ($p0, $p1) = nkeypartref { $v->[$_][$axis] } $bstart => @$ixs;
  474209         528820  
54 18845         39702 my $s0 = _build($v, $p0);
55 18845         486096 my $s1 = _build($v, $p1);
56 18845         469477 my ($c0, $c1) = Math::Vector::Real->box(@{$s0}[_c0, _c1], @{$s1}[_c0, _c1]);
  18845         17579  
  18845         30140  
57 18845         604866 my $cut = 0.5 * ($s0->[_c1][$axis] + $s1->[_c0][$axis]);
58             # warn "b: $b, t: $t, axis: $axis, p0: $p0, p1: $p1, s0: $s0, s1: $s1, c0: $c0, c1: $c1, cut: $cut\n";
59             # [n sum s0 s1 axis cut]
60 18845         33441 [scalar(@$ixs), $c0, $c1, $s0->[_sum] + $s1->[_sum], $s0, $s1, $axis, $cut];
61             }
62             else {
63             # [n, sum, ixs]
64 19926         14720 my @vs = @{$v}[@$ixs];
  19926         28720  
65 19926         33512 my ($c0, $c1) = Math::Vector::Real->box(@vs);
66 19926         695034 [scalar(@$ixs), $c0, $c1, Math::Vector::Real->sum(@vs), $ixs];
67             }
68             }
69              
70 0     0 1 0 sub size { scalar @{shift->{vs}} }
  0         0  
71              
72             sub at {
73 38976     38976 1 1219862 my ($self, $ix) = @_;
74 38976         48393 Math::Vector::Real::clone($self->{vs}[$ix]);
75             }
76              
77             sub insert {
78 6536     6536 1 2428378 my $self = shift;
79 6536 50       12418 @_ or return;
80 6536         6240 my $vs = $self->{vs};
81 6536         6352 my $ix = @$vs;
82 6536 100       9975 if (my $tree = $self->{tree}) {
83 6495         8285 for (@_) {
84 6495         16357 my $v = V(@$_);
85 6495         26083 push @$vs, $v;
86 6495         9798 _insert($vs, $self->{tree}, $#$vs)
87             }
88             }
89             else {
90 41         252 @$vs = map V(@$_), @_;
91 41         340 $self->{tree} = _build($vs, [0..$#$vs]);
92             }
93 6536         11869 return $ix;
94             }
95              
96             # _insert does not return anything but modifies its $t argument in
97             # place. This is really ugly but done to improve performance.
98              
99             sub _insert {
100 35009     35009   30119 my ($vs, $t, $ix) = @_;
101 35009         26808 my $v = $vs->[$ix];
102              
103             # update aggregated values
104 35009         28488 my $n = $t->[_n]++;
105 35009         22902 @{$t}[_c0, _c1] = Math::Vector::Real->box($v, @{$t}[_c0, _c1]);
  35009         912595  
  35009         66508  
106 35009         84425 $t->[_sum] += $v;
107              
108 35009 100       404138 if (defined (my $axis = $t->[_axis])) {
109 28562         22858 my $cut = $t->[_cut];
110 28562         22677 my $c = $v->[$axis];
111              
112 28562         25126 my $n0 = $t->[_s0][_n];
113 28562         21902 my $n1 = $t->[_s1][_n];
114              
115 28562 100       32232 if ($c <= $cut) {
116 14735 100       22228 if (2 * $n1 + $max_per_pole >= $n0) {
117 14710         18249 _insert($vs, $t->[_s0], $ix);
118 14710         19870 return;
119             }
120             }
121             else {
122 13827 100       21977 if (2 * $n0 + $max_per_pole >= $n1) {
123 13804         18230 _insert($vs, $t->[_s1], $ix);
124 13804         18364 return;
125             }
126             }
127              
128             # tree needs rebalancing
129 48         65 my @store;
130 48         123 $#store = $n; # preallocate space
131 48         72 @store = ($ix);
132 48         144 _push_all($t, \@store);
133 48         117 $_[1] = _build($vs, \@store);
134             }
135             else {
136 6447         6758 my $ixs = $t->[_ixs];
137 6447         8888 push @$ixs, $ix;
138 6447 100       12285 if ($n > $max_per_pole) {
139 350         840 $_[1] = _build($vs, $ixs);
140             }
141             }
142             }
143              
144             sub move {
145 0     0 1 0 my ($self, $ix, $v) = @_;
146 0         0 my $vs = $self->{vs};
147 0 0 0     0 ($ix >= 0 and $ix < @$vs) or croak "index out of range";
148 0         0 _delete($vs, $self->{tree}, $ix);
149 0         0 $vs->[$ix] = Math::Vector::Real::clone($v);
150 0         0 _insert($vs, $self->{tree}, $ix);
151             }
152              
153             sub _delete {
154 0     0   0 my ($vs, $t, $ix) = @_;
155 0 0       0 if (defined (my $axis = $t->[_axis])) {
156 0         0 my $v = $vs->[$ix];
157 0         0 my $c = $v->[$axis];
158 0         0 my ($s0, $s1, $cut) = @{$t}[_s0, _s1, _cut];
  0         0  
159 0 0 0     0 if ($c <= $cut and _delete($vs, $s0, $ix)) {
    0 0        
160 0 0       0 if ($s0->[_n]) {
161 0         0 $t->[_n]--;
162 0         0 $t->[_sum] -= $v;
163             }
164             else {
165             # when one subnode becomes empty, the other gets promoted up:
166 0         0 @$t = @$s1;
167             }
168 0         0 return 1;
169             }
170             elsif ($c >= $cut and _delete($vs, $s1, $ix)) {
171 0 0       0 if ($s1->[_n]) {
172 0         0 $t->[_n]--;
173 0         0 $t->[_sum] -= $v;
174             }
175             else {
176 0         0 @$t = @$s0;
177             }
178 0         0 return 1;
179             }
180             }
181             else {
182 0         0 my $ixs = $t->[_ixs];
183 0         0 for (0..$#$ixs) {
184 0 0       0 if ($ixs->[$_] == $ix) {
185 0         0 splice(@$ixs, $_, 1);
186 0         0 $t->[_n]--;
187 0         0 $t->[_sum] -= $vs->[$ix];
188 0         0 return 1;
189             }
190             }
191             }
192 0         0 return 0;
193             }
194              
195             sub hide {
196 0     0 0 0 my ($self, $ix) = @_;
197 0         0 my $vs = $self->{vs};
198 0 0 0     0 ($ix >= 0 and $ix < @$vs) or croak "index out of range";
199 0         0 _delete($vs, $self->{tree}, $ix);
200 0   0     0 ($self->{hidden} //= {})->{$ix} = 1;
201             }
202              
203             sub _push_all {
204 48     48   82 my ($t, $store) = @_;
205 48         52 my @q;
206 48         123 while ($t) {
207 880 100       828 if (defined $t->[_axis]) {
208 416         306 push @q, $t->[_s1];
209 416         495 $t = $t->[_s0];
210             }
211             else {
212 464         270 push @$store, @{$t->[_ixs]};
  464         551  
213 464         595 $t = pop @q;
214             }
215             }
216             }
217              
218             sub path {
219 0     0 0 0 my ($self, $ix) = @_;
220 0         0 my $p = _path($self->{vs}, $self->{tree}, $ix);
221 0         0 my $l = 1;
222 0         0 $l = (($l << 1) | $_) for @$p;
223 0         0 $l
224             }
225              
226             sub _path {
227 0     0   0 my ($vs, $t, $ix) = @_;
228 0 0       0 if (defined (my $axis = $t->[_axis])) {
229 0         0 my $v = $vs->[$ix];
230 0         0 my $c = $v->[$axis];
231 0         0 my $cut = $t->[_cut];
232 0         0 my $p;
233 0 0       0 if ($c <= $cut) {
234 0 0       0 if ($p = _path($vs, $t->[_s0], $ix)) {
235 0         0 unshift @$p, 0;
236 0         0 return $p;
237             }
238             }
239 0 0       0 if ($c >= $cut) {
240 0 0       0 if ($p = _path($vs, $t->[_s1], $ix)) {
241 0         0 unshift @$p, 1;
242 0         0 return $p;
243             }
244             }
245             }
246             else {
247 0 0       0 return [] if grep $_ == $ix, @{$t->[_ixs]}
  0         0  
248             }
249             ()
250 0         0 }
251              
252             sub find {
253 0     0 0 0 my ($self, $v) = @_;
254 0         0 _find($self->{vs}, $self->{tree}, $v);
255             }
256              
257             sub _find {
258 0     0   0 my ($vs, $t, $v) = @_;
259 0         0 while (defined (my $axis = $t->[_axis])) {
260 0         0 my $cut = $t->[_cut];
261 0         0 my $c = $v->[$axis];
262 0 0       0 if ($c < $cut) {
263 0         0 $t = $t->[_s0];
264             }
265             else {
266 0 0       0 if ($c == $cut) {
267 0         0 my $ix = _find($vs, $t->[_s0], $v);
268 0 0       0 return $ix if defined $ix;
269             }
270 0         0 $t = $t->[_s1];
271             }
272             }
273              
274 0         0 for (@{$t->[_ixs]}) {
  0         0  
275 0 0       0 return $_ if $vs->[$_] == $v;
276             }
277             ()
278 0         0 }
279              
280             sub find_nearest_vector {
281 67728     67728 1 78285 my ($self, $v, $d, @but) = @_;
282 67728 50       100751 my $t = $self->{tree} or return;
283 67728         47125 my $vs = $self->{vs};
284 67728 50       68571 my $d2 = (defined $d ? $d * $d : 'inf');
285              
286 67728         46283 my $but;
287 67728 100       83941 if (@but) {
288 12992 50 33     36644 if (@but == 1 and ref $but[0] eq 'HASH') {
289 0         0 $but = $but[0];
290             }
291             else {
292 12992         13595 my %but = map { $_ => 1 } @but;
  12992         33962  
293 12992         17690 $but = \%but;
294             }
295             }
296              
297 67728         80975 my ($rix, $rd2) = _find_nearest_vector($vs, $t, $v, $d2, undef, $but);
298 67728   50     96131 $rix // return;
299 67728 50       201429 wantarray ? ($rix, sqrt($rd2)) : $rix;
300             }
301              
302             *find_nearest_neighbor = \&find_nearest_vector; # for backwards compatibility
303              
304             sub find_nearest_vector_internal {
305 12992     12992 0 1036523 my ($self, $ix, $d) = @_;
306 12992 50       19225 $ix >= 0 or croak "index out of range";
307 12992         24746 $self->find_nearest_vector($self->{vs}[$ix], $d, $ix);
308             }
309              
310             *find_nearest_neighbor_internal = \&find_nearest_vector_internal; # for backwards compatibility
311              
312             sub _find_nearest_vector {
313 92505     92505   98342 my ($vs, $t, $v, $best_d2, $best_ix, $but) = @_;
314              
315 92505         60114 my @queue;
316             my @queue_d2;
317              
318 92505         59986 while (1) {
319 998013 100       1222062 if (defined (my $axis = $t->[_axis])) {
320             # substitute the current one by the best subtree and queue
321             # the worst for later
322 639556 100       866550 ($t, my ($q)) = @{$t}[($v->[$axis] <= $t->[_cut]) ? (_s0, _s1) : (_s1, _s0)];
  639556         751782  
323 639556         460347 my $q_d2 = $v->dist2_to_box(@{$q}[_c0, _c1]);
  639556         1063177  
324 639556 100       24486846 if ($q_d2 <= $best_d2) {
325 410338         244157 my $j;
326 410338         596396 for ($j = $#queue_d2; $j >= 0; $j--) {
327 1195851 100       1917703 last if $queue_d2[$j] >= $q_d2;
328             }
329 410338         372457 splice @queue, ++$j, 0, $q;
330 410338         404146 splice @queue_d2, $j, 0, $q_d2;
331             }
332             }
333             else {
334 358457         211838 for (@{$t->[_ixs]}) {
  358457         448994  
335 1656568 100 100     3181704 next if $but and $but->{$_};
336 1643576         2533900 my $d21 = $vs->[$_]->dist2($v);
337 1643576 100       29101283 if ($d21 <= $best_d2) {
338 261272         167259 $best_d2 = $d21;
339 261272         241233 $best_ix = $_;
340             }
341             }
342              
343 358457 100       533732 if ($t = pop @queue) {
344 309551 100       398393 if ($best_d2 >= pop @queue_d2) {
345 265952         238931 next;
346             }
347             }
348              
349 92505         196013 return ($best_ix, $best_d2);
350             }
351             }
352             }
353              
354             sub find_nearest_vector_in_box {
355 0     0 1 0 my ($self, $v, $a, $b, $d, @but) = @_;
356              
357 0 0       0 my $t = $self->{tree} or return;
358 0         0 my $vs = $self->{vs};
359 0         0 my ($a1, $b1) = Math::Vector::Real->box($a, $b);
360 0 0       0 my $d2 = (defined $d ? $d * $d : $v->max_dist2_to_box($a1, $b1));
361 0         0 my $but;
362 0 0       0 if (@but) {
363 0 0 0     0 if (@but == 1 and ref $but[0] eq 'HASH') {
364 0         0 $but = $but[0];
365             }
366             else {
367 0         0 my %but = map { $_ => 1 } @but;
  0         0  
368 0         0 $but = \%but;
369             }
370             }
371 0         0 my ($rix, $rd2) = _find_nearest_vector_in_box($vs, $t, $v, $a1, $b1, $d2, $but);
372 0   0     0 $rix // return;
373 0 0       0 wantarray ? ($rix, sqrt($rd2)) : $rix;
374             }
375              
376             sub _find_nearest_vector_in_box {
377 0     0   0 my ($vs, $t, $v, $a, $b, $best_d2, $but) = @_;
378 0         0 my $best_ix;
379 0         0 my @queue = $t;
380 0         0 my @queue_d2 = 0;
381              
382 0         0 while (my $t = pop @queue) {
383 0 0       0 last if $best_d2 < pop @queue_d2;
384 0 0       0 if (defined (my $axis = $t->[_axis])) {
385 0         0 my @sides;
386 0 0       0 push @sides, $t->[_s0] if $a->[$axis] <= $t->[_cut];
387 0 0       0 push @sides, $t->[_s1] if $b->[$axis] >= $t->[_cut];
388 0         0 for my $s (@sides) {
389 0         0 my $d2 = $v->dist2_to_box(@$s[_c0, _c1]);
390 0 0       0 if ($d2 <= $best_d2) {
391 0         0 my $j;
392 0         0 for ($j = $#queue_d2; $j >= 0; $j--) {
393 0 0       0 last if $queue_d2[$j] >= $d2;
394             }
395 0         0 splice @queue, ++$j, 0, $s;
396 0         0 splice @queue_d2, $j, 0, $d2;
397             }
398             }
399             }
400             else {
401 0         0 for (@{$t->[_ixs]}) {
  0         0  
402 0 0 0     0 next if $but and $but->{$_};
403 0         0 my $v1 = $vs->[$_];
404 0         0 my $d2 = $v1->dist2($v);
405 0 0 0     0 if ($d2 <= $best_d2 and $v1->dist2_to_box($a, $b) == 0) {
406 0         0 $best_d2 = $d2;
407 0         0 $best_ix = $_;
408             }
409             }
410             }
411             }
412 0         0 return ($best_ix, $best_d2);
413             }
414              
415             sub find_nearest_vector_in_box_chebyshev {
416 0     0 1 0 my ($self, $v, $a, $b, $d, @but) = @_;
417              
418 0 0       0 my $t = $self->{tree} or return;
419 0         0 my $vs = $self->{vs};
420 0         0 my ($a1, $b1) = Math::Vector::Real->box($a, $b);
421 0 0       0 my $d2 = (defined $d ? $d * $d : $v->max_dist2_to_box($a1, $b1));
422 0         0 my $but;
423 0 0       0 if (@but) {
424 0 0 0     0 if (@but == 1 and ref $but[0] eq 'HASH') {
425 0         0 $but = $but[0];
426             }
427             else {
428 0         0 my %but = map { $_ => 1 } @but;
  0         0  
429 0         0 $but = \%but;
430             }
431             }
432 0         0 my ($rix, $rd2) = _find_nearest_vector_in_box($vs, $t, $v, $a1, $b1, $d2, $but);
433 0   0     0 $rix // return;
434 0 0       0 wantarray ? ($rix, sqrt($rd2)) : $rix;
435             }
436              
437             sub _find_nearest_vector_in_box_chebyshev {
438 0     0   0 my ($vs, $t, $v, $a, $b, $best_d, $but) = @_;
439 0         0 my $best_ix;
440 0         0 my @queue = $t;
441 0         0 my @queue_d = 0;
442              
443 0         0 while (my $t = pop @queue) {
444 0 0       0 last if $best_d < pop @queue_d;
445 0 0       0 if (defined (my $axis = $t->[_axis])) {
446 0         0 my @sides;
447 0 0       0 push @sides, $t->[_s0] if $a->[$axis] <= $t->[_cut];
448 0 0       0 push @sides, $t->[_s1] if $b->[$axis] >= $t->[_cut];
449 0         0 for my $s (@sides) {
450 0         0 my $d = $v->chebyshev_dist_to_box(@$s[_c0, _c1]);
451 0 0       0 if ($d <= $best_d) {
452 0         0 my $j;
453 0         0 for ($j = $#queue_d; $j >= 0; $j--) {
454 0 0       0 last if $queue_d[$j] >= $d;
455             }
456 0         0 splice @queue, ++$j, 0, $s;
457 0         0 splice @queue_d, $j, 0, $d;
458             }
459             }
460             }
461             else {
462 0         0 for (@{$t->[_ixs]}) {
  0         0  
463 0 0 0     0 next if $but and $but->{$_};
464 0         0 my $v1 = $vs->[$_];
465 0         0 my $d = $v1->chebyshev_dist($v);
466 0 0 0     0 if ($d <= $best_d and $v1->chebyshev_dist_to_box($a, $b) == 0) {
467 0         0 $best_d = $d;
468 0         0 $best_ix = $_;
469             }
470             }
471             }
472             }
473 0         0 return ($best_ix, $best_d);
474             }
475              
476             sub find_nearest_vector_all_internal {
477 80     80 1 2079689 my ($self, $d) = @_;
478 80         195 my $vs = $self->{vs};
479 80 50       257 return unless @$vs > 1;
480 80 50       226 my $d2 = (defined $d ? $d * $d : 'inf');
481              
482 80         639 my @best = ((undef) x @$vs);
483 80         2705 my @d2 = (($d2) x @$vs);
484 80         277 _find_nearest_vector_all_internal($vs, $self->{tree}, \@best, \@d2);
485 80         11139 return @best;
486             }
487              
488             *find_nearest_neighbor_all_internal = \&find_nearest_vector_all_internal; # for backwards compatibility
489              
490             sub _find_nearest_vector_all_internal {
491 5508     5508   5136 my ($vs, $t, $bests, $d2s) = @_;
492 5508 100       7079 if (defined (my $axis = $t->[_axis])) {
493 2714         1816 my @all_leafs;
494 2714         2203 for my $side (0, 1) {
495 5428         8279 my @leafs = _find_nearest_vector_all_internal($vs, $t->[_s0 + $side], $bests, $d2s);
496 5428         6422 my $other = $t->[_s1 - $side];
497 5428         3837 my ($c0, $c1) = @{$other}[_c0, _c1];
  5428         6418  
498 5428         4544 for my $leaf (@leafs) {
499 17557         175534 for my $ix (@{$leaf->[_ixs]}) {
  17557         20839  
500 79694         872920 my $v = $vs->[$ix];
501 79694 100       108495 if ($v->dist2_to_box($c0, $c1) < $d2s->[$ix]) {
502 24777         1046365 ($bests->[$ix], $d2s->[$ix]) =
503             _find_nearest_vector($vs, $other, $v, $d2s->[$ix], $bests->[$ix]);
504             }
505             }
506             }
507 5428         78450 push @all_leafs, @leafs;
508             }
509 2714         6575 return @all_leafs;
510             }
511             else {
512 2794         2603 my $ixs = $t->[_ixs];
513 2794         3725 for my $i (1 .. $#$ixs) {
514 10198         9041 my $ix_i = $ixs->[$i];
515 10198         7857 my $v_i = $vs->[$ix_i];
516 10198         10251 for my $ix_j (@{$ixs}[0 .. $i - 1]) {
  10198         10281  
517 28540         39162 my $d2 = $v_i->dist2($vs->[$ix_j]);
518 28540 100       379511 if ($d2 < $d2s->[$ix_i]) {
519 15644         12268 $d2s->[$ix_i] = $d2;
520 15644         14725 $bests->[$ix_i] = $ix_j;
521             }
522 28540 100       43874 if ($d2 < $d2s->[$ix_j]) {
523 8496         5881 $d2s->[$ix_j] = $d2;
524 8496         12076 $bests->[$ix_j] = $ix_i;
525             }
526             }
527             }
528 2794         4917 return $t;
529             }
530             }
531              
532             sub find_two_nearest_vectors {
533 418     418 1 995776 my $self = shift;
534 418 50       1479 my $t = $self->{tree} or return;
535 418         596 my $vs = $self->{vs};
536 418 50       1116 if (my ($rix0, $rix1, $rd2) = _find_two_nearest_vectors($vs, $t)) {
537 418 50       2404 return wantarray ? ($rix0, $rix1, sqrt($rd2)) : sqrt($rd2)
538             }
539             ()
540 0         0 }
541              
542             sub _pole_id {
543 0     0   0 my ($id, $deep) = __pole_id(@_);
544 0         0 "$id/$deep";
545             }
546              
547             sub __pole_id {
548 0     0   0 my ($vs, $t) = @_;
549 0 0       0 if (defined $t->[_axis]) {
550 0         0 my ($id, $deep) = __pole_id($vs, $t->[_s0]);
551 0         0 return ($id, $deep+1);
552             }
553 0         0 return ($t->[_ixs][0], 0)
554             }
555              
556             sub _find_two_nearest_vectors {
557 418     418   504 my ($vs, $t) = @_;
558              
559 418         876 my @best_ixs = (undef, undef);
560 418         488 my $best_d2 = 'inf' + 0;
561              
562 418         436 my @inner;
563             my @queue_t1;
564 0         0 my @queue_t2;
565 418         781 while ($t) {
566 31038 100       34418 if (defined $t->[_axis]) {
567 15310         9529 my ($s0, $s1) = @{$t}[_s0, _s1];
  15310         16890  
568 15310         12227 push @inner, $s1;
569 15310         9483 push @queue_t1, $s0;
570 15310         9671 push @queue_t2, $s1;
571 15310         18698 $t = $s0;
572             }
573             else {
574 15728         12747 my $ixs = $t->[_ixs];
575 15728         16600 for my $i (1 .. $#$ixs) {
576 52235         41868 my $ix1 = $ixs->[$i];
577 52235         38618 my $v1 = $vs->[$ix1];
578 52235         41978 for my $j (0 .. $i - 1) {
579 121855         81089 my $ix2 = $ixs->[$j];
580 121855         145644 my $d2 = Math::Vector::Real::dist2($v1, $vs->[$ix2]);
581 121855 100       1556033 if ($d2 < $best_d2) {
582 1956         1305 $best_d2 = $d2;
583 1956         3245 @best_ixs = ($ix1, $ix2);
584             }
585             }
586             }
587 15728         23830 $t = pop @inner;
588             }
589             }
590              
591 418         2490 my @queue_d2 = (0) x @queue_t1;
592 418         1065 while (my $t1 = pop @queue_t1) {
593 119352         93649 my $t2 = pop @queue_t2;
594 119352         97935 my $d2 = pop @queue_d2;
595 119352 100       143969 if ($d2 < $best_d2) {
596 115655 100       147726 unless (defined $t1->[_axis]) {
597 36385 100       41846 unless (defined $t2->[_axis]) {
598 34452         20374 for my $ix1 (@{$t1->[_ixs]}) {
  34452         39434  
599 160816         131834 my $v1 = $vs->[$ix1];
600 160816         90344 for my $ix2 (@{$t2->[_ixs]}) {
  160816         155477  
601 738847         877021 my $d2 = Math::Vector::Real::dist2($v1, $vs->[$ix2]);
602 738847 100       11894110 if ($d2 < $best_d2) {
603 101         108 $best_d2 = $d2;
604 101         233 @best_ixs = ($ix1, $ix2);
605             }
606             }
607             }
608 34452         69833 next;
609             }
610 1933         2182 ($t1, $t2) = ($t2, $t1);
611             }
612 81203         50670 for my $s (@{$t1}[_s0, _s1]) {
  81203         84820  
613 162406         99725 my $d2 = Math::Vector::Real->dist2_between_boxes(@{$s}[_c0, _c1], @{$t2}[_c0, _c1]);
  162406         140033  
  162406         254116  
614 162406 100       10416569 if ($d2) {
615 153202 100       239143 if ($d2 < $best_d2) {
616 94838         76145 unshift @queue_t1, $t2;
617 94838         66284 unshift @queue_t2, $s;
618 94838         163077 unshift @queue_d2, $d2;
619             }
620             }
621             else {
622 9204         7649 push @queue_t1, $t2;
623 9204         6028 push @queue_t2, $s;
624 9204         15507 push @queue_d2, 0;
625             }
626             }
627             }
628             }
629 418         1740 (@best_ixs, $best_d2)
630             }
631              
632             sub find_in_ball {
633 6496     6496 1 38463302 my ($self, $z, $d, $but) = @_;
634 6496 50 33     36444 if (defined $but and ref $but ne 'HASH') {
635 6496         21868 $but = { $but => 1 };
636             }
637 6496         21844 _find_in_ball($self->{vs}, $self->{tree}, $z, $d * $d, $but);
638             }
639              
640             sub _find_in_ball {
641 6496     6496   8795 my ($vs, $t, $z, $d2, $but) = @_;
642 6496         5544 my (@queue, @r);
643 6496         6159 my $r = 0;
644              
645 6496         4764 while (1) {
646 286240 100       4584866 if (defined (my $axis = $t->[_axis])) {
647 161868         124456 my $c = $z->[$axis];
648 161868         123276 my $cut = $t->[_cut];
649 161868 100       170305 ($t, my ($q)) = @{$t}[$c <= $cut ? (_s0, _s1) : (_s1, _s0)];
  161868         207905  
650 161868 100       115487 push @queue, $q if $z->dist2_to_box(@{$q}[_c0, _c1]) <= $d2;
  161868         263671  
651             }
652             else {
653 124372         99869 my $ixs = $t->[_ixs];
654 124372 50       115474 if (wantarray) {
655 124372         120517 push @r, grep { $vs->[$_]->dist2($z) <= $d2 } @$ixs;
  735912         7832418  
656             }
657             else {
658 0 0       0 $r += ( $but
659 0         0 ? grep { !$but->{$_} and $vs->[$_]->dist2($z) <= $d2 } @$ixs
660 0 0       0 : grep { $vs->[$_]->dist2($z) <= $d2 } @$ixs );
661             }
662              
663 124372 100       1632959 $t = pop @queue or last;
664             }
665             }
666              
667 6496 50       12386 if (wantarray) {
668 6496 50       12237 if ($but) {
669 6496         245630 return grep !$but->{$_}, @r;
670             }
671 0         0 return @r;
672             }
673 0         0 return $r;
674             }
675              
676             sub find_in_box {
677 0     0 1 0 my ($self, $a, $b, $but) = @_;
678 0         0 my ($a1, $b1) = Math::Vector::Real->box($a, $b);
679 0 0 0     0 if (defined $but and ref $but ne 'HASH') {
680 0         0 $but = { $but => 1 };
681             }
682 0         0 _find_in_box($self->{vs}, $self->{tree}, $a1, $b1, $but);
683             }
684              
685             sub _find_in_box {
686 0     0   0 my ($vs, $t, $a, $b, $but) = @_;
687 0         0 my (@r, $r);
688 0         0 my @queue;
689 0         0 while (1) {
690 0 0       0 if (defined (my $axis = $t->[_axis])) {
691 0         0 my $cut = $t->[_cut];
692 0 0       0 push @queue, $t->[_s0] if $cut >= $a->[$axis];
693 0 0       0 push @queue, $t->[_s1] if $cut <= $b->[$axis];
694             }
695             else {
696 0         0 my $ixs = $t->[_ixs];
697 0 0       0 if (wantarray) {
698 0         0 push @r, grep { $vs->[$_]->dist2_to_box($a, $b) <= 0 } @$ixs;
  0         0  
699             }
700             else {
701 0 0       0 $r += ( $but
702 0         0 ? grep { !$but->{$_} and $vs->[$_]->dist2_to_box($a, $b) <= 0 } @$ixs
703 0 0       0 : grep { $vs->[$_]->dist2_to_box($a, $b) <= 0 } @$ixs );
704             }
705             }
706 0 0       0 $t = pop @queue or last;
707             }
708              
709 0 0       0 if (wantarray) {
710 0 0       0 if ($but) {
711 0         0 return grep !$but->{$_}, @r;
712             }
713 0         0 return @r;
714             }
715 0         0 return $r;
716             }
717              
718             sub find_farthest_vector {
719 6496     6496 1 7884 my ($self, $v, $d, @but) = @_;
720 6496 50       10216 my $t = $self->{tree} or return;
721 6496         4894 my $vs = $self->{vs};
722 6496 50       8450 my $d2 = ($d ? $d * $d : -1);
723 6496         3888 my $but;
724 6496 50       8686 if (@but) {
725 6496 50 33     18386 if (@but == 1 and ref $but[0] eq 'HASH') {
726 0         0 $but = $but[0];
727             }
728             else {
729 6496         6419 my %but = map { $_ => 1 } @but;
  6496         16177  
730 6496         8580 $but = \%but;
731             }
732             }
733              
734 6496         9519 my ($rix, $rd2) = _find_farthest_vector($vs, $t, $v, $d2, undef, $but);
735 6496   50     8829 $rix // return;
736 6496 50       31547 wantarray ? ($rix, sqrt($d2)) : $rix;
737             }
738              
739             sub find_farthest_vector_internal {
740 6496     6496 1 16491266 my ($self, $ix, $d) = @_;
741 6496 50       10079 $ix >= 0 or croak "index out of range";
742 6496         12267 $self->find_farthest_vector($self->{vs}[$ix], $d, $ix);
743             }
744              
745             sub _find_farthest_vector {
746 6496     6496   6334 my ($vs, $t, $v, $best_d2, $best_ix, $but) = @_;
747              
748 6496         4130 my @queue;
749             my @queue_d2;
750              
751 6496         4416 while (1) {
752 196107 100       252061 if (defined (my $axis = $t->[_axis])) {
753             # substitute the current one by the best subtree and queue
754             # the worst for later
755 126964 100       170200 ($t, my ($q)) = @{$t}[($v->[$axis] >= $t->[_cut]) ? (_s0, _s1) : (_s1, _s0)];
  126964         147863  
756 126964         89250 my $q_d2 = $v->max_dist2_to_box(@{$q}[_c0, _c1]);
  126964         204832  
757 126964 100       5269507 if ($q_d2 >= $best_d2) {
758 88329         51777 my $j;
759 88329         125181 for ($j = $#queue_d2; $j >= 0; $j--) {
760 495875 100       730100 last if $queue_d2[$j] <= $q_d2;
761             }
762 88329         76908 splice @queue, ++$j, 0, $q;
763 88329         84978 splice @queue_d2, $j, 0, $q_d2;
764             }
765             }
766             else {
767 69143         42515 for (@{$t->[_ixs]}) {
  69143         93177  
768 444090 100 66     1137538 next if $but and $but->{$_};
769 443910         638891 my $d21 = $vs->[$_]->dist2($v);
770 443910 100       7803159 if ($d21 >= $best_d2) {
771 53073         32483 $best_d2 = $d21;
772 53073         48445 $best_ix = $_;
773             }
774             }
775              
776 69143 100       102827 if ($t = pop @queue) {
777 68173 100       81477 if ($best_d2 <= pop @queue_d2) {
778 62647         51086 next;
779             }
780             }
781 6496         14110 return ($best_ix, $best_d2);
782             }
783             }
784             }
785              
786             sub find_random_vector {
787 0     0 0 0 my $self = shift;
788 0 0       0 my $t = $self->{tree} or return;
789 0         0 my $vs = $self->{vs};
790 0         0 my $hidden = $self->{hidden};
791 0 0 0     0 if (not $hidden or @$vs > 20 * keys(%$hidden)) {
792             # pick directly when the hidden elements are less than 5% of the total
793 0         0 while (1) {
794 0         0 my $ix = int rand @$vs;
795 0 0 0     0 return $ix unless $hidden and $hidden->{$ix};
796             }
797             }
798 0         0 _find_random_vector($vs, $t);
799             }
800              
801             sub _find_random_vector {
802 0     0   0 my ($vs, $t) = @_;
803 0         0 while (defined $t->[_axis]) {
804 0 0       0 $t = $t->[rand($t->[_n]) < $t->[_s0][_n] ? _s0 : _s1];
805             }
806 0         0 $t->[_ixs][rand $t->[_n]]
807             }
808              
809             sub k_means_seed {
810 224     224 1 18266404 my ($self, $n_req) = @_;
811 224 50       765 $n_req = int($n_req) or return;
812 224 50       768 my $t = $self->{tree} or return;
813 224         351 my $vs = $self->{vs};
814 224         557 _k_means_seed($vs, $t, $n_req);
815             }
816              
817             *k_means_start = \&k_means_seed;
818              
819             sub _k_means_seed {
820 7616     7616   20033 my ($vs, $t, $n_req) = @_;
821 7616 100       7360 if ($n_req <= 1) {
822 2353 100       2705 return if $n_req < 1;
823             # print STDERR "returning centroid\n";
824 2337         4236 return $t->[_sum] / $t->[_n];
825             }
826             else {
827 5263         3813 my $n = $t->[_n];
828 5263 100       5990 if (defined $t->[_axis]) {
829 3696         2474 my ($s0, $s1) = @{$t}[_s0, _s1];
  3696         4657  
830 3696         4067 my $n0 = $s0->[_n];
831 3696         3266 my $n1 = $s1->[_n];
832 3696         3845 my $n0_req = int(0.5 + $n_req * ($n0 / $n));
833 3696 50       4254 $n0_req = $n0 if $n0_req > $n0;
834 3696         3995 return (_k_means_seed($vs, $s0, $n0_req),
835             _k_means_seed($vs, $s1, $n_req - $n0_req));
836             }
837             else {
838 1567         1423 my $ixs = $t->[_ixs];
839 1567         958 my @out;
840 1567         1872 for (0..$#$ixs) {
841 10383 100       18602 push @out, $vs->[$ixs->[$_]]
842             if rand($n - $_) < ($n_req - @out);
843             }
844             # print STDERR "asked for $n_req elements, returning ".scalar(@out)."\n";
845              
846 1567         4101 return @out;
847             }
848             }
849             }
850              
851             our $k_means_seed_pp_test;
852              
853             sub _k_means_seed_pp_test {
854 0     0   0 my ($self, $err, $kms, $players, $weights) = @_;
855 0         0 my @w;
856 0         0 my $last = 0;
857 0         0 for my $i (0..$#$players) {
858 0         0 my $p = $players->[$i];
859 0         0 my $w = $weights->[$i] - $last;
860 0         0 $last = $weights->[$i];
861              
862 0         0 my @store;
863 0 0       0 if (ref $p) {
864 0         0 _push_all($p, \@store);
865             }
866             else {
867 0         0 @store = $p
868             }
869 0 0       0 if (@store) {
870 0         0 $w /= @store;
871 0         0 $w[$_] = $w for @store;
872             }
873             }
874 0         0 my $vs = $self->{vs};
875 0   0     0 $w[$_] //= 0 for 0..$#$vs;
876              
877 0         0 $k_means_seed_pp_test->($self, $err, [map $self->{vs}[$_], @$kms], \@w);
878             }
879              
880             sub k_means_seed_pp {
881 0     0 0 0 my ($self, $n_req, $err) = @_;
882 0 0       0 $n_req = int($n_req) or return;
883 0   0     0 $err ||= 0.5;
884 0 0       0 my $t = $self->{tree} or return;
885 0         0 my $vs = $self->{vs};
886 0         0 my $km = $self->find_random_vector;
887              
888 0         0 my (@km, @d2);
889 0         0 idhash my %extra; # [$min_d2, $max_d2]
890              
891             # my (@player, @weight, @queue);
892             # $#player = @$vs; # preallocate memory
893              
894 0         0 my (@weight, @queue);
895 0         0 $#weight = @$vs; # preallocate memory
896              
897 0         0 while (1) {
898 0         0 push @km, $km;
899 0 0       0 last unless @km < $n_req;
900              
901             # update distances
902 0         0 @queue = $t;
903 0         0 while (my $p = pop @queue) {
904 0         0 my $kmv = $vs->[$km];
905 0         0 my ($c0, $c1) = @{$p}[_c0, _c1];
  0         0  
906 0   0     0 my $extra = $extra{$p} //= ['inf', 'inf'];
907 0         0 my ($min_d2, $max_d2) = @$extra;
908 0         0 my $min_d2_to_box = $kmv->dist2_to_box($c0, $c1);
909 0 0       0 if ($max_d2 > $min_d2_to_box) {
910 0 0       0 if (defined $p->[_axis]) {
911 0         0 push @queue, @{$p}[_s0, _s1];
  0         0  
912             }
913             else {
914 0         0 for (@{$p->[_ixs]}) {
  0         0  
915 0         0 my $d2 = $kmv->dist2($vs->[$_]);
916 0 0 0     0 if ($d2 < ($d2[$_] //= $d2)) {
917 0         0 $d2[$_] = $d2;
918             }
919             }
920             }
921              
922 0 0       0 if ($min_d2_to_box < $min_d2) {
923 0         0 $extra->[0] = $min_d2_to_box;
924             }
925              
926 0         0 my $max_d2_to_box = $kmv->max_dist2_to_box($c0, $c1);
927 0 0       0 if ($max_d2_to_box < $max_d2) {
928 0         0 $extra->[1] = $max_d2_to_box;
929             }
930             }
931             }
932              
933             # find players and weight them
934 0         0 my $weight = 0;
935             # @player = ();
936 0         0 @weight = ();
937              
938             # @queue = $t;
939             # while (my $p = pop @queue) {
940             # my $extra = $extra{$p} or die "internal error: extra information missing for $p";
941             # my ($min_d2, $max_d2) = @$extra;
942              
943             # if ($max_d2 * $err < $min_d2) {
944             # $weight += $p->[_n] * ($min_d2 + $max_d2) * 0.5;
945             # push @weight, $weight;
946             # push @player, $p;
947             # }
948             # else {
949             # if (defined $p->[_axis]) {
950             # push @queue, @{$p}[_s0, _s1];
951             # }
952             # else {
953             # for (@{$p->[_ixs]}) {
954             # if (my $d2 = $d2[$_]) {
955             # $weight += $d2;
956             # push @weight, $weight;
957             # push @player, $_;
958             # }
959             # }
960             # }
961             # }
962             # }
963              
964 0         0 for my $ix (0..@$vs) {
965 0   0     0 $weight += $d2[$ix] // 0;
966 0         0 $weight[$ix] += $weight;
967             }
968              
969             # in order to check the algorithm we have to tap it here
970             # $k_means_seed_pp_test and @km > 1 and
971             # $self->_k_means_seed_pp_test($err, \@km, \@player, \@weight);
972              
973             # to many k-means requested?
974             # @player or last;
975              
976             # select a position on the weight queue:
977 0         0 my $dice = rand($weight);
978              
979             # and use binary search to look for it:
980 0         0 my $i = 0;
981 0         0 my $j = @weight;
982 0         0 while ($i < $j) {
983 0         0 my $pivot = (($i + $j) >> 1);
984 0 0       0 if ($weight[$pivot] < $dice) {
985 0         0 $i = $pivot + 1;
986             }
987             else {
988 0         0 $j = $pivot;
989             }
990             }
991             #my $player = $player[$i];
992             #$km = (ref $player ? _find_random_vector($vs, $player) : $player);
993 0         0 $km = $i;
994             }
995 0         0 return @{$vs}[@km];
  0         0  
996             }
997              
998             sub k_means_loop {
999 224     224 1 90587 my ($self, @k) = @_;
1000 224 50       583 @k or next;
1001 224 50       659 my $t = $self->{tree} or next;
1002 224         422 my $vs = $self->{vs};
1003 224         329 while (1) {
1004 1158         1257 my $diffs;
1005 1158         4256 my @n = ((0) x @k);
1006 1158         2646 my @sum = ((undef) x @k);
1007              
1008 1158         7947 _k_means_step($vs, $t, \@k, [0..$#k], \@n, \@sum);
1009              
1010 1158         36118 for (0..$#k) {
1011 31048 100       40904 if (my $n = $n[$_]) {
1012 29478         40086 my $k = $sum[$_] / $n;
1013 29478 100       188863 $diffs++ if $k != $k[$_];
1014 29478         321120 $k[$_] = $k;
1015             }
1016             }
1017 1158 100       15584 unless ($diffs) {
1018 224 50       5047 return (wantarray ? @k : $k[0]);
1019             }
1020             }
1021             }
1022              
1023             sub k_means_step {
1024 0     0 1 0 my $self = shift;
1025 0 0       0 @_ or return;
1026 0 0       0 my $t = $self->{tree} or return;
1027 0         0 my $vs = $self->{vs};
1028              
1029 0         0 my @n = ((0) x @_);
1030 0         0 my @sum = ((undef) x @_);
1031              
1032 0         0 _k_means_step($vs, $t, \@_, [0..$#_], \@n, \@sum);
1033              
1034 0         0 for (0..$#n) {
1035 0 0       0 if (my $n = $n[$_]) {
1036 0         0 $sum[$_] /= $n;
1037             }
1038             else {
1039             # otherwise let the original value stay
1040 0         0 $sum[$_] = $_[$_];
1041             }
1042             }
1043 0 0       0 wantarray ? @sum : $sum[0];
1044             }
1045              
1046             sub _k_means_step {
1047 109606     109606   113746 my ($vs, $t, $centers, $cixs, $ns, $sums) = @_;
1048 109606         86689 my ($n, $sum, $c0, $c1) = @{$t}[_n, _sum, _c0, _c1];
  109606         185080  
1049 109606 50       167991 if ($n) {
1050 109606         206291 my $centroid = $sum/$n;
1051 109606     0   1256562 my $best = nkeyhead { $centroid->dist2($centers->[$_]) } @$cixs;
  2231698         32882164  
1052 109606         1726030 my $max_d2 = Math::Vector::Real::max_dist2_to_box($centers->[$best], $c0, $c1);
1053 109606         4285022 my @down = grep { Math::Vector::Real::dist2_to_box($centers->[$_], $c0, $c1) <= $max_d2 } @$cixs;
  2231738         72745958  
1054 109606 100       3580041 if (@down <= 1) {
1055 10438         10393 $ns->[$best] += $n;
1056             # FIXME: M::V::R objects should support this undef + vector logic natively!
1057 10438 100       12983 if (defined $sums->[$best]) {
1058 8870         17477 $sums->[$best] += $sum;
1059             }
1060             else {
1061 1568         3071 $sums->[$best] = V(@$sum);
1062             }
1063             }
1064             else {
1065 99168 100       169757 if (defined (my $axis = $t->[_axis])) {
1066 54224         44936 my ($s0, $s1) = @{$t}[_s0, _s1];
  54224         75514  
1067 54224         107297 _k_means_step($vs, $t->[_s0], $centers, \@down, $ns, $sums);
1068 54224         908782 _k_means_step($vs, $t->[_s1], $centers, \@down, $ns, $sums);
1069             }
1070             else {
1071 44944         34468 for my $ix (@{$t->[_ixs]}) {
  44944         78293  
1072 270081         2515976 my $v = $vs->[$ix];
1073 270081     0   950333 my $best = nkeyhead { $v->dist2($centers->[$_]) } @down;
  4610293         74405934  
1074 270081         4101200 $ns->[$best]++;
1075 270081 100       346538 if (defined $sums->[$best]) {
1076 242171         397608 $sums->[$best] += $v;
1077             }
1078             else {
1079 27910         60566 $sums->[$best] = V(@$v);
1080             }
1081             }
1082             }
1083             }
1084             }
1085             }
1086              
1087             sub k_means_assign {
1088 224     224 1 126448 my $self = shift;
1089 224 50       571 @_ or return;
1090 224 50       657 my $t = $self->{tree} or return;
1091 224         279 my $vs = $self->{vs};
1092              
1093 224         3759 my @out = ((undef) x @$vs);
1094 224         1712 _k_means_assign($vs, $t, \@_, [0..$#_], \@out);
1095 224         9627 @out;
1096             }
1097              
1098             sub _k_means_assign {
1099 14110     14110   15913 my ($vs, $t, $centers, $cixs, $outs) = @_;
1100 14110         12609 my ($n, $sum, $c0, $c1) = @{$t}[_n, _sum, _c0, _c1];
  14110         24651  
1101 14110 50       21728 if ($n) {
1102 14110         32808 my $centroid = $sum/$n;
1103 14110     0   168471 my $best = nkeyhead { $centroid->dist2($centers->[$_]) } @$cixs;
  426562         6151385  
1104 14110         215047 my $max_d2 = Math::Vector::Real::max_dist2_to_box($centers->[$best], $c0, $c1);
1105 14110         498707 my @down = grep { Math::Vector::Real::dist2_to_box($centers->[$_], $c0, $c1) <= $max_d2 } @$cixs;
  426602         13427211  
1106 14110 100       431254 if (@down <= 1) {
1107 1418         1928 _k_means_assign_1($t, $best, $outs);
1108             }
1109             else {
1110 12692 100       21731 if (defined (my $axis = $t->[_axis])) {
1111 6943         6460 my ($s0, $s1) = @{$t}[_s0, _s1];
  6943         10395  
1112 6943         14997 _k_means_assign($vs, $t->[_s0], $centers, \@down, $outs);
1113 6943         24752 _k_means_assign($vs, $t->[_s1], $centers, \@down, $outs);
1114             }
1115             else {
1116 5749         5356 for my $ix (@{$t->[_ixs]}) {
  5749         10479  
1117 34081         41277 my $v = $vs->[$ix];
1118 34081     0   125325 my $best = nkeyhead { $v->dist2($centers->[$_]) } @down;
  831649         13423979  
1119 34081         530220 $outs->[$ix] = $best;
1120             }
1121             }
1122             }
1123             }
1124             }
1125              
1126             sub _k_means_assign_1 {
1127 6452     6452   5331 my ($t, $best, $outs) = @_;
1128 6452 100       7809 if (defined (my $axis = $t->[_axis])) {
1129 2517         2858 _k_means_assign_1($t->[_s0], $best, $outs);
1130 2517         3236 _k_means_assign_1($t->[_s1], $best, $outs);
1131             }
1132             else {
1133 3935         2753 $outs->[$_] = $best for @{$t->[_ixs]};
  3935         19155  
1134             }
1135             }
1136              
1137             sub ordered_by_proximity {
1138 6536     6536 1 15923 my $self = shift;
1139 6536         4932 my @r;
1140 6536         4293 $#r = $#{$self->{vs}}; $#r = -1; # preallocate
  6536         13397  
  6536         8040  
1141 6536         10102 _ordered_by_proximity($self->{tree}, \@r);
1142 6536         181551 return @r;
1143             }
1144              
1145             sub _ordered_by_proximity {
1146 425866     425866   247750 my $t = shift;
1147 425866         232085 my $r = shift;
1148 425866 100       383227 if (defined $t->[_axis]) {
1149 209665         174044 _ordered_by_proximity($t->[_s0], $r);
1150 209665         199715 _ordered_by_proximity($t->[_s1], $r);
1151             }
1152             else {
1153 216201         126418 push @$r, @{$t->[_ixs]}
  216201         329898  
1154             }
1155             }
1156              
1157             sub _dump_to_string {
1158 0     0     my ($vs, $t, $indent, $opts) = @_;
1159 0           my ($n, $c0, $c1, $sum) = @{$t}[_n, _c0, _c1, _sum];
  0            
1160 0 0         my $id = ($opts->{pole_id} ? _pole_id($vs, $t)." " : '');
1161 0 0         if (defined (my $axis = $t->[_axis])) {
1162 0           my ($s0, $s1, $cut) = @{$t}[_s0, _s1, _cut];
  0            
1163 0           return ( "${indent}${id}n: $n, c0: $c0, c1: $c1, sum: $sum, axis: $axis, cut: $cut\n" .
1164             _dump_to_string($vs, $s0, "$indent$opts->{tab}", $opts) .
1165             _dump_to_string($vs, $s1, "$indent$opts->{tab}", $opts) );
1166             }
1167             else {
1168 0   0       my $remark = $opts->{remark} // [];
1169 0           my $o = ( "${indent}${id}n: $n, c0: $c0, c1: $c1, sum: $sum\n" .
1170             "${indent}$opts->{tab}ixs: [" );
1171 0           my @str;
1172 0           for my $ix (@{$t->[_ixs]}) {
  0            
1173 0   0       my $colored_ix = (@$remark and grep($ix == $_, @$remark)
1174             ? Term::ANSIColor::colored($ix, 'red')
1175             : $ix);
1176 0 0 0       if ($opts->{dump_vectors} // 1) {
1177 0           push @str, "$colored_ix $vs->[$ix]";
1178             }
1179             else {
1180 0           push @str, $colored_ix;
1181             }
1182             }
1183 0           return $o . join(', ', @str) . "]\n";
1184             }
1185             }
1186              
1187             sub dump_to_string {
1188 0     0 0   my ($self, %opts) = @_;
1189 0   0       my $tab = $opts{tab} //= ' ';
1190 0           my $vs = $self->{vs};
1191 0           my $nvs = @$vs;
1192 0 0         my $hidden = join ", ", keys %{$self->{hidden} || {}};
  0            
1193 0           my $o = "tree: n: $nvs, hidden: {$hidden}\n";
1194 0 0         if (my $t = $self->{tree}) {
1195 0 0         require Term::ANSIColor if $opts{remark};
1196 0           return $o . _dump_to_string($vs, $t, $tab, \%opts);
1197             }
1198             else {
1199 0           return "$o${tab}(empty)\n";
1200             }
1201             }
1202              
1203             sub dump {
1204 0     0 0   my $self = shift;
1205 0           print $self->dump_to_string(@_);
1206             }
1207              
1208             1;
1209             __END__