File Coverage

blib/lib/Math/PlanePath/OneOfEight.pm
Criterion Covered Total %
statement 347 889 39.0
branch 136 394 34.5
condition 30 98 30.6
subroutine 20 37 54.0
pod 21 21 100.0
total 554 1439 38.5


line stmt bran cond sub pod time code
1             # Copyright 2012, 2013, 2014, 2015 Kevin Ryde
2              
3             # This file is part of Math-PlanePath-Toothpick.
4             #
5             # Math-PlanePath-Toothpick is free software; you can redistribute it and/or
6             # modify it under the terms of the GNU General Public License as published
7             # by the Free Software Foundation; either version 3, or (at your option) any
8             # later version.
9             #
10             # Math-PlanePath-Toothpick is distributed in the hope that it will be
11             # useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
12             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
13             # Public License for more details.
14             #
15             # You should have received a copy of the GNU General Public License along
16             # with Math-PlanePath-Toothpick. If not, see .
17              
18              
19             # '1side' without log2 on lower side, is lower quad of 3mid
20             # '1side_up' mirror image, is upper quad of 3mid
21             # '1side with log2 from X=3*2^k,Y=2^k down, and middle of 3side
22              
23              
24             package Math::PlanePath::OneOfEight;
25 1     1   1808 use 5.004;
  1         3  
  1         29  
26 1     1   3 use strict;
  1         2  
  1         28  
27 1     1   3 use Carp 'croak';
  1         1  
  1         62  
28             #use List::Util 'max';
29             *max = \&Math::PlanePath::_max;
30              
31 1     1   28 use vars '$VERSION', '@ISA';
  1         1  
  1         57  
32             $VERSION = 17;
33 1     1   579 use Math::PlanePath;
  1         3930  
  1         35  
34             @ISA = ('Math::PlanePath');
35              
36             use Math::PlanePath::Base::Generic
37 1         35 'is_infinite',
38 1     1   5 'round_nearest';
  1         2  
39             use Math::PlanePath::Base::Digits 119 # v.119 for round_up_pow()
40 1         48 'round_up_pow',
41 1     1   384 'round_down_pow';
  1         1164  
42              
43             # uncomment this to run the ### lines
44             # use Smart::Comments;
45              
46              
47 1     1   4 use constant n_start => 0;
  1         1  
  1         60  
48 1         44 use constant parameter_info_array =>
49             [{ name => 'parts',
50             share_key => 'parts_oneofeight',
51             display => 'Parts',
52             type => 'enum',
53             default => '4',
54             choices => ['4','1','octant','octant_up','wedge','3mid', '3side',
55             # 'side'
56             ],
57             choices_display => ['4','1','Octant','Octant Up','Wedge','3 Mid','3 Side',
58             # 'Side'
59             ],
60             description => 'Which parts of the plane to fill.',
61             },
62 1     1   4 ];
  1         391  
63 1     1   5 use constant class_x_negative => 1;
  1         0  
  1         29  
64 1     1   3 use constant class_y_negative => 1;
  1         1  
  1         3242  
65              
66             {
67             my %x_negative = (4 => 1,
68             1 => 0,
69             octant => 0,
70             octant_up => 0,
71             wedge => 1,
72             '3mid' => 1,
73             '3side' => 1,
74             side => 0,
75             );
76             sub x_negative {
77 0     0 1 0 my ($self) = @_;
78 0         0 return $x_negative{$self->{'parts'}};
79             }
80             }
81             {
82             my %y_negative = (4 => 1,
83             1 => 0,
84             octant => 0,
85             octant_up => 0,
86             wedge => 0,
87             '3mid' => 1,
88             '3side' => 1,
89             side => 0,
90             );
91             sub y_negative {
92 0     0 1 0 my ($self) = @_;
93 0         0 return $y_negative{$self->{'parts'}};
94             }
95             }
96             {
97             my %y_minimum = (# 4 => undef,
98             1 => 0,
99             octant => 0,
100             octant_up => 0,
101             wedge => 0,
102             # '3mid' => undef,
103             # '3side' => undef,
104             side => 1,
105             );
106             sub y_minimum {
107 0     0 1 0 my ($self) = @_;
108 0         0 return $y_minimum{$self->{'parts'}};
109             }
110             }
111              
112             {
113             my %x_negative_at_n = (4 => 4,
114             1 => undef,
115             octant => undef,
116             octant_up => undef,
117             wedge => 3,
118             '3mid' => 5,
119             '3side' => 15,
120             side => undef,
121             );
122             sub x_negative_at_n {
123 0     0 1 0 my ($self) = @_;
124 0         0 return $x_negative_at_n{$self->{'parts'}};
125             }
126             }
127             {
128             my %y_negative_at_n = (4 => 6,
129             1 => undef,
130             octant => undef,
131             octant_up => undef,
132             wedge => undef,
133             '3mid' => 1,
134             '3side' => 1,
135             side => undef,
136             );
137             sub y_negative_at_n {
138 0     0 1 0 my ($self) = @_;
139 0         0 return $y_negative_at_n{$self->{'parts'}};
140             }
141             }
142              
143             {
144             my %sumxy_minimum = (1 => 0,
145             octant => 0,
146             octant_up => 0,
147             wedge => 0, # X>=-Y so X+Y>=0
148             );
149             sub sumxy_minimum {
150 0     0 1 0 my ($self) = @_;
151 0         0 return $sumxy_minimum{$self->{'parts'}};
152             }
153             }
154             {
155             my %diffxy_minimum = (octant => 0, # Y<=X so X-Y>=0
156             );
157             sub diffxy_minimum {
158 0     0 1 0 my ($self) = @_;
159 0         0 return $diffxy_minimum{$self->{'parts'}};
160             }
161             }
162             {
163             my %diffxy_maximum = (octant_up => 0, # X<=Y so X+Y<=0
164             wedge => 0, # X<=Y so X+Y<=0
165             );
166             sub diffxy_maximum {
167 0     0 1 0 my ($self) = @_;
168 0         0 return $diffxy_maximum{$self->{'parts'}};
169             }
170             }
171              
172             {
173             my %tree_num_children_list = (4 => [ 0, 1, 2, 3, 5, 8 ],
174             1 => [ 0, 1, 2, 3, 5 ],
175             octant => [ 0, 1, 2, 3 ],
176             octant_up => [ 0, 1, 2, 3 ],
177             wedge => [ 0, 1, 2, 3 ],
178             '3mid' => [ 0, 1, 2, 3, 5 ],
179             '3side' => [ 0, 2, 3 ],
180             side => [ 0, 2, 3 ],
181             );
182             sub tree_num_children_list {
183 0     0 1 0 my ($self) = @_;
184 0         0 return @{$tree_num_children_list{$self->{'parts'}}};
  0         0  
185             }
186             }
187              
188             # parts=1,3mid dx=2*2^k-3 dy=-2^k, it seems
189             # parts=3side dx=2*2^k-5 dy=-2^k-2, it seems
190             my %dir_maximum_dxdy
191             = (4 => [0,-1], # South
192             1 => [2,-1], # ESE, supremum
193             octant => [1,-1], # South-East
194             octant_up => [0,-1], # N=12 South
195             wedge => [0,-1], # South
196             '3mid' => [2,-1], # ESE, supremum
197             '3side' => [2,-1], # ESE, supremum
198             );
199             sub dir_maximum_dxdy {
200 0     0 1 0 my ($self) = @_;
201 0         0 return @{$dir_maximum_dxdy{$self->{'parts'}}};
  0         0  
202             }
203              
204             #------------------------------------------------------------------------------
205              
206             sub new {
207 11     11 1 767 my $self = shift->SUPER::new(@_);
208 11   100     69 my $parts = ($self->{'parts'} ||= '4');
209 11 50       20 if (! exists $dir_maximum_dxdy{$parts}) {
210 0         0 croak "Unrecognised parts: ",$parts;
211             }
212 11         15 return $self;
213             }
214              
215              
216             #------------------------------------------------------------------------------
217             # n_to_xy()
218              
219             my %initial_n_to_xy
220             = (4 => [ [0,0], [1,0], [1,1], [0,1],
221             [-1,1], [-1,0], [-1,-1], [0,-1], [1,-1] ],
222             1 => [ [0,0], [1,0], [1,1], [0,1] ],
223             octant => [ [0,0], [1,0], [1,1] ],
224             octant_up => [ [0,0], [1,1], [0,1] ],
225             wedge => [ [0,0], [1,1], [0,1], [-1,1] ],
226             '3mid' => [ [0,0], [1,-1], [1,0], [1,1],
227             [0,1], [-1,1] ],
228              
229             # for 3side table up to N=8 because cell X=1,Y=2 at N=7
230             # is overlapped by two upper octants
231             '3side' => [ [0,0], [1,-1], [1,0], [1,1],
232             [1,-2], [2,-2], [2,2], [1,2], [0,2] ],
233              
234             side => [ [0,0], [1,0], [1,1], [2,2], [1,2] ],
235             );
236              
237             # depth=0 1 2 3
238             my @octant_small_n_to_v = ([0], [0,1], [2], [1,2,3]);
239             my @octant_mid_n_to_v = ([0], [-1,0,1]);
240              
241             sub n_to_xy {
242 92     92 1 2686 my ($self, $n) = @_;
243             ### OneOfEight n_to_xy(): $n
244              
245 92 50       125 if ($n < 0) { return; }
  0         0  
246 92 50       142 if (is_infinite($n)) { return ($n,$n); }
  0         0  
247              
248             {
249 92         318 my $int = int($n);
  92         61  
250             ### $int
251             ### $n
252 92 50       109 if ($n != $int) {
253 0         0 my ($x1,$y1) = $self->n_to_xy($int);
254 0         0 my ($x2,$y2) = $self->n_to_xy($int+1);
255 0         0 my $frac = $n - $int; # inherit possible BigFloat
256 0         0 my $dx = $x2-$x1;
257 0         0 my $dy = $y2-$y1;
258 0         0 return ($frac*$dx + $x1, $frac*$dy + $y1);
259             }
260 92         68 $n = $int; # BigFloat int() gives BigInt, use that
261             }
262 92         70 my $zero = $n*0;
263              
264 92         74 my $parts = $self->{'parts'};
265             {
266 92         61 my $initial = $initial_n_to_xy{$parts};
  92         67  
267 92 100       135 if ($n <= $#$initial) {
268             ### initial_n_to_xy{}: $initial->[$n]
269 22         11 return @{$initial->[$n]};
  22         42  
270             }
271             }
272              
273 70         80 (my $depth, $n) = _n0_to_depth_and_rem($self, $n);
274             ### $depth
275             ### remainder n: $n
276             ### cf this depth n: $self->tree_depth_to_n($depth)
277             ### cf next depth n: $self->tree_depth_to_n($depth+1)
278              
279             # $hdx,$hdy is the dx,dy offsets which is "horizontal". Initially this is
280             # hdx=1,hdy=0 so horizontal along the X axis, but subsequent blocks rotate
281             # around or mirror to point other directions.
282             #
283             # $vdx,$vdy is similar dx,dy which is "vertical". Initially vdx=0,vdy=1
284             # so vertical along the Y axis.
285             #
286             # $mirror is true if in a "mirror image" such as upper octant 0<=X<=Y
287             # portion of the pattern. The difference is that $mirror false has points
288             # numbered anti-clockwise "upwards" from the ragged edge towards the
289             # diagonal, but when $mirror is true instead clockwise "down" from the
290             # diagonal towards the ragged edge.
291             #
292             # When $mirror is true the octant generated is still reckoned as 0<=Y<=X,
293             # but the $hdx,$hdy and $vdx,$vdy are suitably mangled so that this
294             # logical first octant ends up in whatever target is desired. For example
295             # the 0<=X<=Y second octant of the pattern starts with hdx=0,hdy=1 and
296             # vdx=1,vdy=0, so the "horizontal" is upwards and the "vertical" is to the
297             # right.
298             #
299             # $log2_extras is true if the extra cell at the log2 positions
300             # X=3,7,15,31,etc and Y=1 should be included in the pattern. Initially
301             # true, but later in the "lower" block there are no such extra cells.
302             #
303             # $top_no_extra_pow is a 2^k power if the top of the diagonal at
304             # X=pow-1,Y=pow-1 should not be included in the pattern. Or 0 if this
305             # diagonal cell should be included. Initially true, but later going
306             # "lower" followed by "upper" it's the end of the diagonal is not wanted.
307             # The first such is at X=8,Y=2 which should not be in the "upper"
308             # (mirrored) diagonal coming from X=11,Y=5. In general if $log2_extras is
309             # false then $top_no_extra_pow excludes that log2 cell when going to the
310             # "upper" block.
311             #
312 70         55 my $x = 0;
313 70         37 my $y = 0;
314 70         48 my $hdx = 1;
315 70         42 my $hdy = 0;
316 70         36 my $vdx = 0;
317 70         61 my $vdy = 1;
318 70         33 my $mirror = 0; # plain
319 70         45 my $log2_extras = 1; # include cells X=3,7,15,31;Y=1 etc
320 70         35 my $top_no_extra_pow = 0;
321              
322 70 50 66     307 if ($parts eq 'octant') {
    50 66        
    50          
    50          
    0          
    0          
    0          
323             ### parts=octant ...
324              
325             } elsif ($parts eq 'octant_up') {
326             ### parts=octant_up ...
327 0         0 $hdx = 0;
328 0         0 $hdy = 1;
329 0         0 $vdx = 1;
330 0         0 $vdy = 0;
331 0         0 $mirror = 1;
332              
333             } elsif ($parts eq 'wedge') {
334             ### parts=wedge ...
335 0         0 my $add = _depth_to_octant_added([$depth],[1],$zero);
336 0 0       0 if ($n < $add) {
337 0         0 $hdx = 0; # same as octant_up
338 0         0 $hdy = 1;
339 0         0 $vdx = 1;
340 0         0 $vdy = 0;
341 0         0 $mirror = 1;
342             } else {
343 0         0 $n -= $add;
344 0         0 $hdx = 0; # rotate +90
345 0         0 $hdy = 1;
346 0         0 $vdx = -1;
347 0         0 $vdy = 0;
348             }
349              
350             } elsif ($parts eq '1' || $parts eq '2' || $parts eq '4') {
351 70         128 my $add = _depth_to_octant_added([$depth],[1],$zero);
352             ### octant add: $add
353              
354 70 100       110 if ($parts eq '4') {
355             # Half-plane is 4 octants, less 2 for duplicate diagonal.
356 42         26 my $hadd = 4*$add-2;
357 42 100       58 if ($n >= $hadd) {
358             ### initial rotate 180 ...
359 18         18 $n -= $hadd;
360 18         11 $hdx = -1;
361 18         16 $vdy = -1;
362             }
363             }
364 70 100 66     179 if ($parts eq '2' || $parts eq '4') {
365             # Each quadrant is 2 octants, less 1 for duplicate diagonal.
366 42         31 my $qadd = 2*$add-1;
367 42 100       46 if ($n >= $qadd) {
368             ### initial rotate +90 ...
369 19         15 $n -= $qadd;
370 19         12 ($hdx,$hdy) = (-$hdy,$hdx);
371 19         20 ($vdx,$vdy) = (-$vdy,$vdx);
372             }
373             }
374 70 100       90 if ($n >= $add) {
375             ### initial mirror ...
376 24         17 $mirror = 1;
377 24         27 ($hdx,$hdy, $vdx,$vdy) # mirror by transpose
378             = ($vdx,$vdy, $hdx,$hdy);
379 24         17 $n -= $add;
380 24         21 $n += 1; # excluding diagonal
381             }
382              
383             } elsif ($parts eq '3mid') {
384 0 0       0 my $add = _depth_to_octant_added([$depth+1],[1],$zero)
385             - (_is_pow2($depth+2) ? 2 : 1);
386             ### lower of side 1, excluding diagonal: "depth=".($depth+1)." add=".$add
387 0 0       0 if ($n < $add) {
388             ### lower of side 1 ...
389 0         0 $hdx = 0; $hdy = -1; $vdx = 1; $vdy = 0;
  0         0  
  0         0  
  0         0  
390 0         0 $log2_extras = 0;
391 0         0 $depth += 1;
392 0         0 $x = -1; $y = 1;
  0         0  
393             } else {
394 0         0 $n -= $add;
395             ### past side 1 lower, not past diagonal: "n=$n"
396              
397 0         0 $add = _depth_to_octant_added([$depth],[1],$zero);
398 0 0       0 if ($n < $add) {
399             ### upper of side 1 ...
400 0         0 $vdy = -1;
401 0         0 $mirror = 1;
402             } else {
403 0         0 $n -= $add;
404              
405 0 0       0 if ($n < $add) {
406             ### lower of centre ...
407             } else {
408 0         0 $n -= $add;
409 0         0 $n += 1; # past diagonal
410              
411 0 0       0 if ($n < $add) {
412             ### upper of centre ...
413 0         0 $hdx = 0;
414 0         0 $hdy = 1;
415 0         0 $vdx = 1;
416 0         0 $vdy = 0;
417 0         0 $mirror = 1;
418             } else {
419 0         0 $n -= $add;
420              
421 0 0       0 if ($n < $add) {
422             ### upper of side 3 ...
423 0         0 $hdx = 0;
424 0         0 $hdy = 1;
425 0         0 $vdx = -1;
426 0         0 $vdy = 0;
427             } else {
428 0         0 $n -= $add;
429 0         0 $n += 1; # past diagonal
430              
431             ### lower of side 3 ...
432 0         0 $hdx = -1;
433 0         0 $depth += 1;
434 0         0 $x = 1; $y = -1;
  0         0  
435 0         0 $log2_extras = 0;
436 0         0 $mirror =1;
437             }
438             }
439             }
440             }
441             }
442              
443             } elsif ($parts eq '3side') {
444 0 0       0 my $add = (_depth_to_octant_added([$depth+1],[1],$zero)
445             - (_is_pow2($depth+2) ? 2 : 1));
446             ### lower of side 1, excluding diagonal: "depth=".($depth+1)." add=".$add
447 0 0       0 if ($n < $add) {
448             ### lower of side 1 ...
449 0         0 $hdx = 0;
450 0         0 $hdy = -1;
451 0         0 $vdx = 1;
452 0         0 $vdy = 0;
453 0         0 $log2_extras = 0;
454 0         0 $depth += 1;
455 0         0 $x = -1; $y = 1;
  0         0  
456             } else {
457 0         0 $n -= $add;
458              
459 0         0 $add = _depth_to_octant_added([$depth],[1],$zero);
460             ### plain add, including diagonal: "add=$add cf n=$n"
461 0 0       0 if ($n < $add) {
462             ### upper of side 1 ...
463 0         0 $vdy = -1;
464 0         0 $mirror = 1;
465             } else {
466 0         0 $n -= $add;
467             ### not upper of side 1, leaving n: $n
468              
469 0 0       0 if ($n < $add) {
470             ### lower of centre, including diagonal ...
471             } else {
472 0         0 $n -= $add;
473 0         0 $n += 1; # past diagonal
474             ### not lower of centre, and past diagonal to n: $n
475              
476 0         0 $add = _depth_to_octant_added([$depth-1],[1],$zero);
477             ### upper of centre, excluding diagonal: "depth=".($depth-1)." add-1=".$add
478 0 0       0 if ($n < $add) {
479             ### upper of centre ...
480 0         0 $hdx = 0; $hdy = 1; $vdx = 1; $vdy = 0;
  0         0  
  0         0  
  0         0  
481 0         0 $x = 1; $y = 1;
  0         0  
482 0         0 $mirror = 1;
483 0         0 $depth -= 1;
484             } else {
485 0         0 $n -= $add;
486             ### not upper of centre, to n: $n
487              
488 0 0       0 if ($n < $add) {
489             ### upper of side 3 ...
490 0         0 $hdx = 0; $hdy = 1; $vdx = -1; $vdy = 0; # rotate -90
  0         0  
  0         0  
  0         0  
491 0         0 $x = 1; $y = 1;
  0         0  
492 0         0 $depth -= 1;
493             } else {
494 0         0 $n -= $add;
495 0         0 $n += 1; # past diagonal
496             ### not upper of side 3, and past diagonal to n: $n
497              
498             ### lower of side 3 ...
499 0         0 $hdx = -1;
500 0         0 $x = 2;
501 0         0 $log2_extras = 0;
502 0         0 $mirror =1;
503             }
504             }
505             }
506             }
507             }
508              
509             } elsif ($parts eq 'side') {
510 0         0 my $add = _depth_to_octant_added([$depth],[1],$zero);
511             ### first octant add: $add
512 0 0       0 if ($n < $add) {
513             ### first octant ...
514             } else {
515             ### second octant ...
516 0         0 $n -= $add;
517 0         0 $n += 1; # past diagonal
518 0         0 $hdx = 0; $hdy = 1; $vdx = 1; $vdy = 0;
  0         0  
  0         0  
  0         0  
519 0         0 $depth += 1;
520 0         0 $log2_extras = 0;
521 0         0 $mirror = 1;
522 0         0 $x = -1; $y = -1;
  0         0  
523             }
524             }
525              
526             ### adjusted to octant style: "depth=$depth remainder n=$n"
527              
528 70         112 my ($pow,$exp) = round_down_pow ($depth+1, 2);
529             ### initial exp: $exp
530             ### initial pow: $pow
531              
532 70         424 for ( ; $exp >= 0; $pow/=2, $exp--) {
533             ### at: "pow=$pow exp=$exp depth=$depth n=$n mirror=$mirror log2extras=$log2_extras topnopow=$top_no_extra_pow xy=$x,$y h=$hdx,$hdy v=$vdx,$vdy"
534             ### assert: $depth >= 1
535             ### assert: $mirror == 0 || $mirror == 1
536              
537 122 100       153 if ($depth < $pow) {
538             ### block 0 ...
539 36         24 $top_no_extra_pow = 0;
540 36         51 next;
541             }
542              
543 86 100       97 if ($depth <= 3) {
544 46 100       44 if ($mirror) {
545             ### mirror small depth ...
546 17 50       24 if ($depth == $top_no_extra_pow-1) {
547 0         0 $n += 1;
548             ### inc n for top_no_extra_pow: "to n=$n"
549             }
550             ### assert: $n <= $#{$octant_small_n_to_v[$depth]}
551 17         14 $n = -1-$n; # perl negative index to read array in reverse
552             } else {
553             ### small depth ...
554 29 100 66     48 if (! $log2_extras && $depth == 3) {
555 1         1 $n += 1;
556             ### inc n for no log2_extras: "to n=$n"
557             }
558             ### assert: $n <= $#{$octant_small_n_to_v[$depth]}
559             }
560 46         44 my $v = $octant_small_n_to_v[$depth][$n];
561             ### hv: "h=$depth, v=$v"
562 46         44 $x += $depth*$hdx + $v*$vdx; # $depth is "$h" horizontal position
563 46         36 $y += $depth*$hdy + $v*$vdy;
564 46         34 last;
565             }
566              
567 40         37 $x += $pow * ($hdx + $vdx); # $pow along diagonal
568 40         30 $y += $pow * ($hdy + $vdy);
569 40         28 $depth -= $pow;
570             ### diagonal to: "depth=$depth xy=$x,$y"
571              
572 40 100       43 if ($depth <= 1) {
573             ### mid two levels ...
574 24 100       31 if ($mirror) {
575             ### negative perl array index to reverse for mirror state ...
576 7         8 $n = -1-$n;
577             }
578 24         22 my $v = $octant_mid_n_to_v[$depth][$n];
579             ### hv: "h=$depth v=$v"
580 24         20 $x += $depth*$hdx + $v*$vdx; # $depth is "$h" horizontal position
581 24         21 $y += $depth*$hdy + $v*$vdy;
582 24         21 last;
583             }
584              
585 16 100       20 if ($mirror == 0) { # plain
586              
587             # See if $n within lower.
588             # Not at depth+1==pow since lower has already finished then.
589             #
590 9 100       10 if ($depth+1 < $pow) {
591 3         5 my $add = _depth_to_octant_added([$depth+1],[1],$zero);
592 3 50       5 if (_is_pow2($depth+2)) {
593             ### add lower decreased for remaining depth+2 a power-of-2 ...
594 3         4 $add -= 1;
595             }
596 3         3 $add -= 1;
597             ### add in lower, excluding diagonal: $add
598 3 100       5 if ($n < $add) {
599             ### lower, rotate +90 ...
600 1         1 $top_no_extra_pow = 0;
601 1         0 $log2_extras = 0;
602 1         2 $depth += 1;
603             ### assert: $depth < $pow
604 1         1 ($hdx,$hdy, $vdx,$vdy) # rotate 90 in direction v toward h
605             = (-$vdx,-$vdy, $hdx,$hdy);
606 1         2 $x -= $hdx + $vdx;
607 1         1 $y -= $hdy + $vdy;
608 1         2 next;
609             }
610 2         3 $n -= $add;
611             } else {
612             ### skip lower at depth==pow-1 ...
613             }
614              
615             # See if $n within upper.
616             #
617 8         14 my $add = _depth_to_octant_added([$depth],[1],$zero);
618 8 50 33     16 if (! $log2_extras && $depth+1 == $pow) {
619             ### add upper decreased for no log2_extras at depth=pow-1 ...
620 0         0 $add -= 1;
621             }
622             ### add in upper, including diagonal: $add
623 8 100       15 if ($n < $add) {
624             ### upper, mirror ...
625 4         3 $mirror = 1;
626 4         5 $vdx = -$vdx; # flip vertically
627 4         7 $vdy = -$vdy;
628 4 50       7 $top_no_extra_pow = ($log2_extras ? 0 : $pow);
629 4         1 $log2_extras = 1;
630 4         7 next;
631             }
632 4         4 $n -= $add;
633             ### assert: $n < $add
634              
635             # Otherwise $n is within extend.
636             #
637             ### extend ...
638 4         5 $top_no_extra_pow /= 2;
639 4         5 $log2_extras = 1;
640              
641             } else {
642             # $mirror == 1, mirrored
643              
644             # See if $n within extend.
645             #
646 7         11 my $eadd = my $add = _depth_to_octant_added([$depth],[1],$zero);
647 7         8 $top_no_extra_pow /= 2; # since after $depth+=$pow
648 7 50       13 if ($depth == $top_no_extra_pow - 1) {
649             ### add extend decreased for no top extra ...
650 0         0 $eadd -= 1;
651             }
652             ### add in extend: $eadd
653 7 100       11 if ($n < $eadd) {
654             ### extend ...
655 2         2 $log2_extras = 1;
656 2         3 next;
657             }
658 5         3 $n -= $eadd;
659              
660             # See if $n within upper.
661             #
662             ### add in upper, including diagonal: "$add cf n=$n"
663 5 100       9 if ($n < $add) {
664             ### upper, unmirror ...
665 4 50       6 $top_no_extra_pow = ($log2_extras ? 0 : $pow);
666 4         4 $log2_extras = 1;
667 4         2 $mirror = 0;
668 4         4 $vdx = -$vdx; # flip vertically
669 4         3 $vdy = -$vdy;
670 4         6 next;
671             }
672 1         1 $n -= $add;
673              
674             # Otherwise $n is within lower.
675             #
676 1         1 $n += 1; # past diagonal
677             ### lower, rotate: "n=$n"
678             ### assert: $n < _depth_to_octant_added([$depth+1],[1],$zero)
679 1         2 $top_no_extra_pow = 0;
680 1         1 $log2_extras = 0;
681 1         1 $depth += 1;
682             ### assert: $depth < $pow
683 1         2 ($hdx,$hdy, $vdx,$vdy) # rotate 90 in direction v toward h
684             = (-$vdx,-$vdy, $hdx,$hdy);
685 1         1 $x -= $hdx + $vdx;
686 1         8 $y -= $vdx + $vdy;
687             }
688             }
689              
690             ### n_to_xy() return: "$x,$y (depth=$depth n=$n)"
691 70         108 return ($x,$y);
692             }
693              
694             # ($depth, $nrem) = _n0_to_depth_and_rem($self,$n)
695             #
696             # _n0_to_depth_and_rem() finds the tree $depth level containing $n and
697             # returns that $depth and the offset of $n into that level, being
698             # $n - $self->tree_depth_to_n($depth).
699             #
700             # The current approach is a binary search for the bits of depth which have
701             # tree_depth_to_n($depth) <= $n.
702             #
703             # Ndepth grows as roughly depth*depth, so this is about log4(N) many bsearch
704             # compares. Maybe for modest N a table of depth->N could be used for the
705             # search (and for tree_depth_to_n()). It would cover up to about sqrt(N),
706             # so for large N would still need some searching code.
707             #
708             # quadrant(2^k) = (4*4^k + 6*k + 14) / 9
709             # N*9/4 = 4^k + 6/4*k + 14/4
710             # parts=1 N*9 to round up to next power
711             # parts=octant N*18
712             # parts=4 N*9/4 = N*3 as estimate
713             # parts=3 N*9/4 = N*3 too
714             #
715             my %parts_to_depth_multiplier = (4 => 3,
716             1 => 9,
717             octant => 18,
718             octant_up => 18,
719             wedge => 9,
720             '3mid' => 3,
721             '3side' => 3,
722             side => 9,
723             );
724             sub _n0_to_depth_and_rem {
725 70     70   48 my ($self, $n) = @_;
726             ### _n0_to_depth_and_rem(): "n=$n parts=$self->{'parts'}"
727              
728 70         127 my ($pow,$exp) = round_down_pow
729             ($n * $parts_to_depth_multiplier{$self->{'parts'}},
730             4);
731 70 50       463 if (is_infinite($exp)) {
732 0         0 return ($exp,0);
733             }
734             ### $pow
735             ### $exp
736              
737 70         221 my $depth = 0;
738 70         49 my $n_depth = 0;
739 70         46 $pow = 2 ** $exp; # pow=2^exp down to 1, inclusive
740              
741 70         96 while ($exp-- >= 0) {
742 266         191 my $try_depth = $depth + $pow;
743 266         293 my $try_n_depth = $self->tree_depth_to_n($try_depth);
744              
745             ### $depth
746             ### $pow
747             ### $try_depth
748             ### $try_n_depth
749              
750 266 100       321 if ($try_n_depth <= $n) {
751             ### use this tried depth ...
752 141         83 $depth = $try_depth;
753 141         100 $n_depth = $try_n_depth;
754             }
755 266         337 $pow /= 2;
756             }
757              
758             ### _n0_to_depth_and_rem() final ...
759             ### $depth
760             ### remainder: $n - $n_depth
761              
762 70         90 return ($depth, $n - $n_depth);
763             }
764              
765             #------------------------------------------------------------------------------
766             # xy_to_n()
767              
768             my @yxoct_to_n = ([ 0, 1 ], # Y=0
769             [ undef, 2 ]); # Y=1
770             my @yxoctup_to_n = ([ 0, undef ], # Y=0
771             [ 2, 1 ]); # Y=1
772             my @yxwedge_to_n = ([ 0, undef, undef ], # Y=0 X=0,1,-1
773             [ 2, 1, 3 ]); # Y=1
774             my @yx1_to_n = ([ 0, 1 ], # Y=0
775             [ 3, 2 ]); # Y=1
776             my @yx3_to_n = ([ 0, 2, undef ], # Y=0 X=0,1,-1
777             [ 4, 3, 5 ], # Y=1
778             [ undef, 1, undef ]); # Y=-1
779             my @yx4_to_n = ([ 0, 1, 5 ], # Y=0 X=0,1,-1
780             [ 3, 2, 4 ], # Y=1
781             [ 7, 8, 6 ]); # Y=-1
782             my @yx3mid_to_n = ([ 0, 2, undef ], # Y=0 X=0,1,-1
783             [ 4, 3, 5 ], # Y=1
784             [ undef, 1, undef ]); # Y=-1
785             my @yx3side_to_n = ([ 0, 2, undef ], # Y=0 X=0,1,-1
786             [ undef, 3, undef ], # Y=1
787             [ 8, 7, 16 ], # Y=2
788             [ undef, 4, undef ], # Y=-2
789             [ undef, 1, undef ]); # Y=-1
790             my @yxside_to_n = ([ 0, 1 ], # Y=0 X=0,1,-1
791             [ undef, 2 ]); # Y=1
792              
793             # N values relative to tree_depth_to_n() start of the depth level
794             my @yx_to_n = ([ [ 0, 0, ], # plain
795             [ undef, 1, undef, 0 ],
796             [ undef, undef, 0, 1 ],
797             [ undef, undef, undef, 2 ] ],
798             [ [ 0, 1, ], # mirror
799             [ undef, 0, undef, 2 ],
800             [ undef, undef, 0, 1 ],
801             [ undef, undef, undef, 0 ] ]);
802              
803             #use Smart::Comments;
804              
805             sub xy_to_n {
806 60     60 1 595 my ($self, $x, $y) = @_;
807             ### OneOfEight xy_to_n(): "$x, $y"
808              
809             # {
810             # require Math::PlanePath::OneOfEightByCells;
811             # my $cells = ($self->{'cells'} ||= Math::PlanePath::OneOfEightByCells->new (parts => $self->{'parts'}));
812             # return $cells->xy_to_n($x,$y);
813             # }
814              
815 60         84 $x = round_nearest ($x);
816 60         246 $y = round_nearest ($y);
817 60 50       199 if (is_infinite($x)) {
818 0         0 return $x;
819             }
820 60 50       232 if (is_infinite($y)) {
821 0         0 return $y;
822             }
823              
824 60         249 my ($pow,$exp) = round_down_pow (max(abs($x),abs($y))+2, 2);
825             ### initial pow: "exp=$exp pow=$pow"
826             ### from abs(x): abs($x)
827             ### from abs(y): abs($y)
828             ### from max: max(abs($x),abs($y))
829              
830 60 50       636 if (is_infinite($exp)) {
831 0         0 return $exp;
832             }
833              
834 60         213 my $zero = $x * 0 * $y;
835 60         40 my @add_offset;
836             my @add_mult;
837 0         0 my @add_log2_extras;
838 0         0 my @add_top_no_extra_pow;
839 60         38 my $mirror = 0;
840 60         38 my $log2_extras = 1;
841 60         39 my $top_extra = 1;
842 60         35 my $top_no_extra_pow = 0;
843 60         35 my $depth = 0;
844 60         42 my $n = $zero;
845              
846 60         52 my $parts = $self->{'parts'};
847 60 50 33     225 if ($parts eq 'octant') {
    50          
    50          
    50          
    0          
    0          
    0          
    0          
848             ### parts==octant ...
849 0 0 0     0 if ($y < 0 || $y > $x) {
850 0         0 return undef;
851             }
852 0 0 0     0 if ($x <= 1 && $y <= 1) {
853 0         0 return $yxoct_to_n[$y][$x];
854             }
855              
856             } elsif ($parts eq 'octant_up') {
857             ### parts==octant_up ...
858 0 0 0     0 if ($x < 0 || $x > $y) {
859             ### outside upper octant ...
860 0         0 return undef;
861             }
862 0 0 0     0 if ($x <= 1 && $y <= 1) {
863             ### yxoctup_to_n[] table ...
864 0         0 return $yxoctup_to_n[$y][$x];
865             }
866             # transpose and mirror
867 0         0 ($x,$y) = ($y,$x);
868 0         0 $mirror = 1;
869              
870             } elsif ($parts eq 'wedge') {
871             ### parts==wedge ...
872 0 0 0     0 if ($x > $y || $x < -$y) {
873 0         0 return undef;
874             }
875 0 0 0     0 if (abs($x) <= 1 && $y <= 1) {
876 0         0 return $yxwedge_to_n[$y][$x];
877             }
878 0 0       0 if ($x >= 0) {
879 0         0 ($x,$y) = ($y,$x); # transpose and mirror
880 0         0 $mirror = 1;
881             } else {
882 0         0 ($x,$y) = ($y,-$x); # rotate -90
883 0         0 push @add_offset, 0;
884 0         0 push @add_mult, 1;
885 0         0 push @add_top_no_extra_pow, 0;
886 0         0 push @add_log2_extras, 1;
887             }
888              
889             } elsif ($parts eq '1' || $parts eq '4') {
890 60         45 my $mult = 0;
891 60 50       60 if ($parts eq '1') {
892             ### parts==1 ...
893 0 0 0     0 if ($x < 0 || $y < 0) {
894 0         0 return undef;
895             }
896 0 0 0     0 if ($x <= 1 && $y <= 1) {
897 0         0 return $yx1_to_n[$y][$x];
898             }
899             } else {
900             ### parts==4 ...
901 60 100 100     111 if (abs($x) <= 1 && abs($y) <= 1) {
902 18         34 return $yx4_to_n[$y][$x];
903             }
904 42 100       52 if ($y < 0) {
905             ### quad 3 or 4, rotate 180 ...
906 18         15 $mult = 4; # past first,second quads
907 18         14 $n -= 2; # unduplicate diagonals
908 18         9 $x = -$x; # rotate 180
909 18         16 $y = -$y;
910             }
911 42 100       49 if ($x < 0) {
912             ### quad 2 (or 4), rotate 90 ...
913 19         17 $mult += 2;
914 19         14 $n -= 1; # unduplicate diagonal
915 19         23 ($x,$y) = ($y,-$x); # rotate -90
916             }
917             }
918              
919             ### now in first quadrant: "x=$x y=$y"
920 42 100       50 if ($y > $x) {
921             ### second octant, transpose and mirror ...
922 13         14 ($x,$y) = ($y,$x);
923 13         9 $mult++;
924 13         7 $n -= 1; # unduplicate diagonal
925 13         12 $mirror = 1;
926             }
927 42 100       49 if ($mult) {
928 34         25 push @add_offset, 0;
929 34         25 push @add_mult, $mult;
930 34         22 push @add_top_no_extra_pow, 0;
931 34         31 push @add_log2_extras, 1;
932             }
933              
934             } elsif ($parts eq '3mid') {
935             ### parts==3mid ...
936 0 0 0     0 if (abs($x) <= 1 && abs($y) <= 1) {
937             ### 3mid small: $yx3mid_to_n[$y][$x]
938 0         0 return $yx3mid_to_n[$y][$x];
939             }
940 0 0       0 if ($y < 0) {
941 0 0       0 if ($x < 0) {
942             ### third quadrant, no such point ...
943 0         0 return undef;
944             }
945 0         0 $y = -$y;
946 0 0       0 if ($y >= $x) {
947             ### block 0 lower ...
948 0         0 $log2_extras = 0;
949 0         0 ($x,$y) = ($y+1,$x+1);
950 0         0 $depth = -1;
951             } else {
952             ### block 1 upper ...
953 0         0 $mirror = 1;
954              
955             ### past block 0 lower, excluding diagonal ...
956 0         0 push @add_offset, -1;
957 0         0 push @add_mult, 1;
958 0         0 push @add_top_no_extra_pow, 0;
959 0         0 push @add_log2_extras, 0;
960 0         0 $n -= 1; # excluding diagonal
961             }
962             } else {
963 0 0       0 if ($x >= 0) {
964 0 0       0 if ($y <= $x) {
965             ### block 2 first octant ...
966              
967             ### past block 0 lower, excluding diagonal ...
968 0         0 push @add_offset, -1;
969 0         0 push @add_mult, 1;
970 0         0 push @add_top_no_extra_pow, 0;
971 0         0 push @add_log2_extras, 0;
972 0         0 $n -= 1; # excluding diagonal
973              
974             ### past block 1 ...
975 0         0 push @add_offset, 0;
976 0         0 push @add_mult, 1;
977 0         0 push @add_top_no_extra_pow, 0;
978 0         0 push @add_log2_extras, 1;
979              
980             } else {
981             ### block 3 second octant ...
982 0         0 ($x,$y) = ($y,$x);
983 0         0 $mirror = 1;
984              
985             ### past block 0 lower, excluding diagonal ...
986 0         0 push @add_offset, -1;
987 0         0 push @add_mult, 1;
988 0         0 push @add_top_no_extra_pow, 0;
989 0         0 push @add_log2_extras, 0;
990 0         0 $n -= 1; # excluding diagonal
991              
992             ### past blocks 1,2, excluding leading diagonal ...
993 0         0 push @add_offset, 0;
994 0         0 push @add_mult, 2;
995 0         0 push @add_top_no_extra_pow, 0;
996 0         0 push @add_log2_extras, 1;
997 0         0 $n -= 1; # excluding leading diagonal
998             }
999             } else {
1000             ### second quadrant ...
1001 0         0 $x = -$x;
1002 0 0       0 if ($y >= $x) {
1003             ### block 4 third octant ...
1004 0         0 ($x,$y) = ($y,$x);
1005              
1006             ### past block 0 lower, excluding diagonal ...
1007 0         0 push @add_offset, -1;
1008 0         0 push @add_mult, 1;
1009 0         0 push @add_top_no_extra_pow, 0;
1010 0         0 push @add_log2_extras, 0;
1011 0         0 $n -= 1; # excluding diagonal
1012              
1013             ### past blocks 1,2,3 excluding leading diagonal ...
1014 0         0 push @add_offset, 0;
1015 0         0 push @add_mult, 3;
1016 0         0 push @add_top_no_extra_pow, 0;
1017 0         0 push @add_log2_extras, 1;
1018 0         0 $n -= 1; # excluding leading diagonal
1019              
1020             } else {
1021             ### block 5 fourth octant ...
1022 0         0 $x += 1; $y += 1;
  0         0  
1023 0         0 $mirror = 1;
1024 0         0 $depth = -1;
1025 0         0 $log2_extras = 0;
1026              
1027             ### past block 0 lower, excluding diagonal ...
1028 0         0 push @add_offset, -1;
1029 0         0 push @add_mult, 1;
1030 0         0 push @add_top_no_extra_pow, 0;
1031 0         0 push @add_log2_extras, 0;
1032 0         0 $n -= 1; # excluding diagonal
1033              
1034 0         0 push @add_offset, 0;
1035 0         0 push @add_mult, 4;
1036 0         0 push @add_top_no_extra_pow, 0;
1037 0         0 push @add_log2_extras, 1;
1038 0         0 $n -= 2; # unduplicate two diagonals
1039             }
1040             }
1041             }
1042              
1043             } elsif ($parts eq '3side') {
1044             ### parts==3side ...
1045 0 0 0     0 if (abs($x) <= 1 && abs($y) <= 2) {
1046             ### 3side small: $yx3side_to_n[$y][$x]
1047 0         0 return $yx3side_to_n[$y][$x];
1048             }
1049 0 0       0 if ($y < 0) {
1050 0 0       0 if ($x < 0) {
1051             ### third quadrant, no such point ...
1052 0         0 return undef;
1053             }
1054 0         0 $y = -$y;
1055 0 0       0 if ($y >= $x) {
1056             ### block 0 lower ...
1057 0         0 $log2_extras = 0;
1058 0         0 ($x,$y) = ($y+1,$x+1);
1059 0         0 $depth = -1;
1060             } else {
1061             ### block 1 upper ...
1062 0         0 $mirror = 1;
1063              
1064             ### past block 0 lower, excluding diagonal ...
1065 0         0 push @add_offset, -1;
1066 0         0 push @add_mult, 1;
1067 0         0 push @add_top_no_extra_pow, 0;
1068 0         0 push @add_log2_extras, 0;
1069 0         0 $n -= 1; # excluding diagonal
1070             }
1071             } else {
1072 0 0       0 if ($x > 0) {
1073 0 0       0 if ($y <= $x) {
1074             ### block 2 first octant ...
1075              
1076             ### past block 0 lower, excluding diagonal ...
1077 0         0 push @add_offset, -1;
1078 0         0 push @add_mult, 1;
1079 0         0 push @add_top_no_extra_pow, 0;
1080 0         0 push @add_log2_extras, 0;
1081 0         0 $n -= 1; # excluding diagonal
1082              
1083             ### past block 1 ...
1084 0         0 push @add_offset, 0;
1085 0         0 push @add_mult, 1;
1086 0         0 push @add_top_no_extra_pow, 0;
1087 0         0 push @add_log2_extras, 1;
1088              
1089             } else {
1090             ### block 3 second octant ...
1091 0         0 ($x,$y) = ($y-1,$x-1);
1092 0         0 $depth = 1;
1093 0         0 $mirror = 1;
1094              
1095             ### past block 0 lower, excluding diagonal ...
1096 0         0 push @add_offset, -1;
1097 0         0 push @add_mult, 1;
1098 0         0 push @add_top_no_extra_pow, 0;
1099 0         0 push @add_log2_extras, 0;
1100 0         0 $n -= 1; # excluding diagonal
1101              
1102             ### past block 1,2, excluding leading diagonal ...
1103 0         0 push @add_offset, 0;
1104 0         0 push @add_mult, 2;
1105 0         0 push @add_top_no_extra_pow, 0;
1106 0         0 push @add_log2_extras, 1;
1107 0         0 $n -= 1; # excluding leading diagonal
1108             }
1109             } else {
1110             ### second quadrant ...
1111 0         0 $x = 2-$x;
1112             ### X mirror to: "x=$x y=$y"
1113              
1114 0 0       0 if ($y >= $x) {
1115             ### block 4 third octant ...
1116 0         0 ($x,$y) = ($y-1,$x-1);
1117             ### transpose to: "x=$x y=$y"
1118 0         0 $depth = 1;
1119              
1120             ### past block 0 lower, excluding diagonal ...
1121 0         0 push @add_offset, -1;
1122 0         0 push @add_mult, 1;
1123 0         0 push @add_top_no_extra_pow, 0;
1124 0         0 push @add_log2_extras, 0;
1125 0         0 $n -= 1; # excluding diagonal
1126              
1127             ### past block 1,2, excluding leading diagonal ...
1128 0         0 push @add_offset, 0;
1129 0         0 push @add_mult, 2;
1130 0         0 push @add_top_no_extra_pow, 0;
1131 0         0 push @add_log2_extras, 1;
1132 0         0 $n -= 1; # excluding leading diagonal
1133              
1134             ### past block 3 ...
1135 0         0 push @add_offset, 1;
1136 0         0 push @add_mult, 1;
1137 0         0 push @add_top_no_extra_pow, 0;
1138 0         0 push @add_log2_extras, 1;
1139              
1140             } else {
1141             ### block 5 fourth octant ...
1142 0         0 $mirror = 1;
1143 0         0 $log2_extras = 0;
1144              
1145             ### past block 0 lower, excluding diagonal ...
1146 0         0 push @add_offset, -1;
1147 0         0 push @add_mult, 1;
1148 0         0 push @add_top_no_extra_pow, 0;
1149 0         0 push @add_log2_extras, 0;
1150 0         0 $n -= 1; # excluding diagonal
1151              
1152             ### past block 1,2, excluding leading diagonal ...
1153 0         0 push @add_offset, 0;
1154 0         0 push @add_mult, 2;
1155 0         0 push @add_top_no_extra_pow, 0;
1156 0         0 push @add_log2_extras, 1;
1157 0         0 $n -= 1; # unduplicate leading diagonal
1158              
1159             ### past block 3,4 ...
1160 0         0 push @add_offset, 1;
1161 0         0 push @add_mult, 2;
1162 0         0 push @add_top_no_extra_pow, 0;
1163 0         0 push @add_log2_extras, 1;
1164 0         0 $n -= 1; # excluding block4 diagonal
1165             }
1166             }
1167             }
1168              
1169             } elsif ($parts eq 'side') {
1170             ### parts==side ...
1171 0 0 0     0 if ($x < 0 || $y < 0) {
1172 0         0 return undef;
1173             }
1174 0 0 0     0 if ($x <= 1 && $y <= 1) {
1175 0         0 return $yxside_to_n[$y][$x];
1176             }
1177              
1178 0 0       0 if ($y > $x) {
1179             ### second octant ...
1180 0         0 ($x,$y) = ($y+1,$x+1);
1181 0         0 $depth = -1;
1182 0         0 $mirror = 1;
1183 0         0 $log2_extras = 0;
1184 0         0 $n -= 1; # excluding diagonal
1185              
1186             ### past block 1 ...
1187 0         0 push @add_offset, 0;
1188 0         0 push @add_mult, 1;
1189 0         0 push @add_top_no_extra_pow, 0;
1190 0         0 push @add_log2_extras, 1;
1191             }
1192              
1193              
1194             } elsif ($parts eq '2') {
1195             ### parts==2 ...
1196             # if ($x == 0) {
1197             # if ($y == 1) { return 0; }
1198             # }
1199             # if ($y == 1) {
1200             # if ($x == 1) { return 1; }
1201             # if ($x == -1) { return 2; }
1202             # }
1203             # if ($x < 0) {
1204             # ### initial mirror second quadrant ...
1205             # $x = -$x;
1206             # $mirror = 1;
1207             # push @add_offset, -1;
1208             # push @add_mult, 1;
1209             # }
1210             }
1211              
1212 42 50 33     102 if ($x == 0 || $y == 0) {
1213             ### nothing on axes after origin ...
1214 0         0 return undef;
1215             }
1216              
1217 42         30 for (;;) {
1218             ### at: "x=$x,y=$y n=$n pow=$pow depth=$depth mirror=$mirror log2_extras=$log2_extras top_extra=$top_extra top_no_extra_pow=$top_no_extra_pow"
1219             ### assert: $x >= 0
1220             ### assert: $x < 2 * $pow
1221             ### assert: $y >= 0
1222             ### assert: $y <= $x
1223              
1224 42 100       49 if ($x <= 3) {
1225             ### loop small XY ...
1226             ### $top_no_extra_pow
1227              
1228 24 100       28 if ($x == 3) {
1229 20 50       25 if (! $log2_extras) {
1230 0 0       0 if ($y == 1) {
1231             ### no log2_extras ...
1232 0         0 return undef;
1233             }
1234 0 0       0 if (! $mirror) {
1235             ### no log2_extras, N decrement, (not mirrored) ...
1236 0         0 $n -= 1;
1237             }
1238             }
1239 20 50       22 if ($top_no_extra_pow == 4) {
1240 0 0       0 if ($y == 3) {
1241             ### no top extra, so no such point ...
1242 0         0 return undef;
1243             }
1244             ### top_no_extra_pow, N decrement by mirror: $mirror
1245 0         0 $n -= $mirror;
1246             }
1247             }
1248              
1249 24         28 my $nyx = $yx_to_n[$mirror][$y][$x];
1250             ### $nyx
1251 24 50       32 if (! defined $nyx) {
1252             ### no such point ...
1253 0         0 return undef;
1254             }
1255 24         25 $n += $nyx;
1256 24         14 $depth += $x;
1257 24         20 last;
1258             }
1259              
1260 18 100       36 if ($x == $pow) {
    50          
1261 4 50       6 if ($y == $pow) {
1262             ### mid X=pow,Y=pow, stop ...
1263 4         3 $depth += $pow;
1264 4         4 last;
1265             }
1266             ### X=pow no such point ...
1267 0         0 return undef;
1268             } elsif ($x == $pow+1) {
1269 14 100       19 if ($y == $pow-1) {
1270             ### mid X=pow+1,Y=pow-1, stop ...
1271 5         5 $depth += $pow+1;
1272 5 100       7 $n += ($mirror ? 2 : 0);
1273 5         8 last;
1274             }
1275 9 100       11 if ($y == $pow) {
1276             ### mid X=pow+1,Y=pow, stop ...
1277 6         6 $depth += $pow+1;
1278 6         7 $n += 1;
1279 6         4 last;
1280             }
1281 3 50       6 if ($y == $pow+1) {
1282             ### mid X=pow+1,Y=pow+1, stop ...
1283 3         1 $depth += $pow+1;
1284 3 50       5 $n += ($mirror ? 0 : 2);
1285 3         3 last;
1286             }
1287             }
1288              
1289 0 0       0 if ($x < $pow) {
1290             ### base block ...
1291 0         0 $top_no_extra_pow = 0;
1292              
1293             } else {
1294 0         0 $x -= $pow;
1295 0         0 $depth += $pow;
1296 0 0       0 if ($y < $pow) {
1297 0         0 $y = $pow-$y;
1298             ### Y flip to: $y
1299              
1300 0 0       0 if ($y > $x) {
1301             ### block lower, excluding diagonal ...
1302 0         0 ($x,$y) = ($y+1,$x+1);
1303             ### rotate to: "x=$x y=$y"
1304             ### assert: $y >= 0
1305 0 0 0     0 unless ($y && $x < $pow) {
1306             ### Y=0 or X>=pow, no such point ...
1307 0         0 return undef;
1308             }
1309 0         0 $top_no_extra_pow = 0;
1310 0         0 $log2_extras = 0;
1311 0         0 $depth -= 1;
1312 0 0       0 if ($mirror) {
1313             ### offset past extend,upper, undup diagonal, (mirrored) ...
1314 0         0 push @add_offset, $depth+1;
1315 0         0 push @add_mult, 2;
1316 0         0 push @add_top_no_extra_pow, $top_no_extra_pow/2;
1317 0         0 push @add_log2_extras, 1;
1318 0         0 $n -= 1; # duplicated diagonal upper,lower
1319             }
1320              
1321             } else {
1322             ### block upper ...
1323 0 0       0 if ($mirror) {
1324             ### offset past extend (mirrored) ...
1325 0         0 push @add_offset, $depth;
1326 0         0 push @add_mult, 1;
1327 0         0 push @add_top_no_extra_pow, $top_no_extra_pow/2;
1328 0         0 push @add_log2_extras, 1;
1329             } else {
1330 0 0       0 if ($x < $pow-1) {
1331             ### offset past lower, unduplicate diagonal, (not mirrored) ...
1332 0         0 push @add_offset, $depth-1;
1333 0         0 push @add_mult, 1;
1334 0         0 push @add_top_no_extra_pow, 0;
1335 0         0 push @add_log2_extras, 0;
1336 0         0 $n -= 1; # duplicated diagonal upper,lower
1337             }
1338             }
1339 0 0       0 $top_no_extra_pow = ($log2_extras ? 0 : $pow);
1340 0         0 $log2_extras = 1;
1341 0         0 $mirror ^= 1;
1342             }
1343             } else {
1344             ### extend, same ...
1345 0 0       0 unless ($x) {
1346             ### on X=0, past block3, no such point ...
1347 0         0 return undef;
1348             }
1349 0 0       0 if ($mirror) {
1350             ### no offset past lower at X=pow-1 ...
1351             } else {
1352 0 0       0 if ($x < $pow-1) {
1353             ### offset past lower (not mirrored) ...
1354 0         0 push @add_offset, $depth-1;
1355 0         0 push @add_mult, 1;
1356 0         0 push @add_top_no_extra_pow, 0;
1357 0         0 push @add_log2_extras, 0;
1358 0         0 $n -= 1; # duplicated diagonal
1359             }
1360             ### offset past upper (not mirrored) ...
1361 0         0 push @add_offset, $depth;
1362 0         0 push @add_mult, 1;
1363 0 0       0 push @add_top_no_extra_pow, ($log2_extras ? 0 : $pow);
1364 0         0 push @add_log2_extras, 1;
1365             # if (! $log2_extras) {
1366             # ### no log2_extras so N decrement ...
1367             # $n -= 1;
1368             # }
1369             }
1370 0         0 $y -= $pow;
1371 0         0 $log2_extras = 1;
1372 0         0 $top_extra = 1;
1373 0         0 $top_no_extra_pow /= 2;
1374             }
1375             }
1376              
1377 0 0       0 if (--$exp < 0) {
1378             ### final xy: "$x,$y"
1379 0 0 0     0 if ($x == 1 && $y == 1) {
    0 0        
1380             } elsif ($x == 1 && $y == 2) {
1381 0         0 $depth += 1;
1382             } else {
1383             ### not in final position ...
1384 0         0 return undef;
1385             }
1386 0         0 last;
1387             }
1388 0         0 $pow /= 2;
1389             }
1390              
1391              
1392             ### final depth: $depth
1393             ### $n
1394             ### depth_to_n: $self->tree_depth_to_n($depth)
1395             ### add_offset: join(',',@add_offset)
1396             ### add_mult: join(',',@add_mult)
1397             ### assert: scalar(@add_offset) == scalar(@add_mult)
1398             ### assert: scalar(@add_offset) == scalar(@add_log2_extras)
1399             ### assert: scalar(@add_offset) == scalar(@add_top_no_extra_pow)
1400              
1401 42         49 $n += $self->tree_depth_to_n($depth);
1402              
1403 42 100       59 if (@add_offset) {
1404 34         40 foreach my $i (0 .. $#add_offset) {
1405 34         35 my $d = $add_offset[$i] = $depth - $add_offset[$i];
1406              
1407 34 50       47 if ($d+1 == $add_top_no_extra_pow[$i]) {
1408             ### no top_extra, decrement applied: "d=$d"
1409 0         0 $n -= 1;
1410             }
1411 34 0 33     61 if (! $add_log2_extras[$i] && $d >= 3 && _is_pow2($d+1)) {
      33        
1412             ### no log2_extras, decrement applied: "depth d=$d"
1413 0         0 $n -= 1;
1414             }
1415              
1416             ### add: "depth=$add_offset[$i] is "._depth_to_octant_added([$add_offset[$i]],[1],$zero)." x $add_mult[$i] log2_extras=$add_log2_extras[$i] top_no_extra_pow=$add_top_no_extra_pow[$i]"
1417             }
1418              
1419             ### total add: _depth_to_octant_added ([@add_offset], [@add_mult], $zero)
1420 34         52 $n += _depth_to_octant_added (\@add_offset, \@add_mult, $zero);
1421             }
1422              
1423             ### xy_to_n() return n: $n
1424 42         66 return $n;
1425             }
1426              
1427              
1428             #------------------------------------------------------------------------------
1429             # rect_to_n_range()
1430              
1431             # not exact
1432             sub rect_to_n_range {
1433 0     0 1 0 my ($self, $x1,$y1, $x2,$y2) = @_;
1434             ### OneOfEight rect_to_n_range(): "$x1,$y1 $x2,$y2"
1435              
1436 0         0 $x1 = round_nearest ($x1);
1437 0         0 $y1 = round_nearest ($y1);
1438 0         0 $x2 = round_nearest ($x2);
1439 0         0 $y2 = round_nearest ($y2);
1440 0 0       0 ($x1,$x2) = ($x2,$x1) if $x1 > $x2;
1441 0 0       0 ($y1,$y2) = ($y2,$y1) if $y1 > $y2;
1442 0         0 my $parts = $self->{'parts'};
1443              
1444 0 0       0 my $extra = ($parts eq '3side' ? 2 : 0);
1445 0         0 my ($pow,$exp) = round_down_pow (max(1,
1446             abs($x1),
1447             abs($x2)+$extra,
1448             abs($y1),
1449             abs($y2)+$extra),
1450             2);
1451              
1452 0 0       0 if ($parts eq '1') {
1453             # (total(2^k)+3)/4 = ((16*4^k + 24*k - 7)/9 + 3)/4
1454             # = (16*4^k + 24*k - 7 + 27)/9/4
1455             # = (16*4^k + 24*k + 20)/9/4
1456             # = (4*4^k + 6*k + 5)/9
1457             # applied to k=exp+1 2*pow=2^k
1458             # = (4* 2*pow * 2*pow + 6*(exp+1) + 5)/9
1459             # = (16*pow*pow + 6*exp + 11)/9
1460 0         0 return (0, (16*$pow*$pow + 6*$exp + 11) / 9);
1461             }
1462              
1463             # $parts eq '4'
1464             # total(2^k) = (16*4^k + 24*k - 7)/9
1465             # applied to k=exp+1 2*pow=2^k
1466             # = (16 * 2*pow * 2*pow + 24*(exp+1) - 7) / 9
1467             # = (64*pow*pow + 24*exp + 24-7) / 9
1468             # = (64*pow*pow + 24*exp + 17) / 9
1469 0         0 return (0, (64*$pow*$pow + 24*$exp + 17) / 9);
1470             }
1471              
1472             #------------------------------------------------------------------------------
1473             # tree
1474              
1475 1     1   4 use constant tree_num_roots => 1;
  1         1  
  1         1660  
1476              
1477             sub tree_n_to_depth {
1478 0     0 1 0 my ($self, $n) = @_;
1479             ### tree_n_to_depth(): "$n"
1480              
1481 0 0       0 if ($n < 0) {
1482 0         0 return undef;
1483             }
1484 0         0 my ($depth) = _n0_to_depth_and_rem($self, int($n));
1485             ### n0 depth: $depth
1486 0         0 return $depth;
1487             }
1488              
1489             my @surround8_dx = (1, 1, 0, -1, -1, -1, 0, 1);
1490             my @surround8_dy = (0, 1, 1, 1, 0, -1, -1, -1);
1491              
1492             sub tree_n_children {
1493 0     0 1 0 my ($self, $n) = @_;
1494             ### tree_n_children(): $n
1495              
1496 0 0       0 my ($x,$y) = $self->n_to_xy($n)
1497             or return;
1498             ### $x
1499             ### $y
1500              
1501 0         0 my $depth = $self->tree_n_to_depth($n) + 1;
1502             return
1503 0         0 sort {$a<=>$b}
  0         0  
1504 0         0 grep { $self->tree_n_to_depth($_) == $depth }
1505 0         0 map { $self->xy_to_n_list($x + $surround8_dx[$_],
1506             $y + $surround8_dy[$_]) }
1507             0 .. $#surround8_dx;
1508             }
1509             sub tree_n_parent {
1510 0     0 1 0 my ($self, $n) = @_;
1511              
1512 0 0       0 if ($n < 0) {
1513 0         0 return undef;
1514             }
1515 0 0       0 my ($x,$y) = $self->n_to_xy($n)
1516             or return undef;
1517 0         0 my $parent_depth = $self->tree_n_to_depth($n) - 1;
1518              
1519 0         0 foreach my $i (0 .. $#surround8_dx) {
1520 0         0 my $pn = $self->xy_to_n($x + $surround8_dx[$i],
1521             $y + $surround8_dy[$i]);
1522 0 0 0     0 if (defined $pn && $self->tree_n_to_depth($pn) == $parent_depth) {
1523 0         0 return $pn;
1524             }
1525             }
1526 0         0 return undef;
1527             }
1528              
1529              
1530             #------------------------------------------------------------------------------
1531             # tree_depth_to_n()
1532              
1533             # 1 1 1
1534             # 2 9 1001
1535             # 4 33 100001
1536             # 8 121 1111001
1537             # 16 465 111010001
1538             # 32 1833 11100101001
1539             # 64 7297 1110010000001
1540             # 128 29145 111000111011001
1541             # 256 116529 11100011100110001
1542             # 512 466057 1110001110010001001
1543             # 1024 1864161 111000111000111100001
1544             #
1545             # before 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
1546             # side = 0, 1,3, 6,9,14,21, 27,30,35,43,52,63,80,100, 112
1547             # 3,5,8,9,11,17,20,12
1548             #
1549             # side(5) = side(4) + side(2) + 2*side(1) + 2
1550             # = 6 + 1 + 2*0 + 2 = 9
1551             # side(9) = side(8) + side(1) + 2
1552             # side(10) = side(8) + side(3) + 2*side(2) + 3 = 27 + 3 + 2*1 + 3 = 35
1553             # side(11) = side(8) + side(4) + 2*side(3) + log2(4/4) + 3 = 27+6+2*3+1+3 = 42
1554             #
1555             # side(2^k) = 4*side(2^(k-1)) -1 block 1 missing one in corner
1556             # + k-2 block 2 extra lower
1557             # + 3 centre A,B,C
1558             # = 4*side(2^(k-1)) + k
1559             # = k + (k-1)*4^1 + (k-2)*4^2 + ... + 2*4^(k-1) + 4^k
1560             # eg. k=3 3+2*4+1*16 = 27
1561             # = 1 + 1+4 + 1+4+16 = 1 + 5 + 21
1562             # sum 1+4+...+4^(k-1) = (4^k-1)/3
1563             # side(2^k) = (4^k-1)/3 + (4^(k-1)-1)/3 + ... + (4^1-1)/3
1564             # = (4^k - 1 + 4^(k-1) - 1 + ... + 4^1 - 1)/3 # k terms 4^k to 4^1
1565             # = (4^k + 4^(k-1) + ... + 4^1 - k)/3
1566             # = (4^k + 4^(k-1) + ... + 4^1 + 4^0 - 1 - k)/3
1567             # = ((4^(k+1)-1)/3 - 1 - k)/3
1568             # = (4^(k+1)-1 - 3*k - 3)/9
1569             # = (4*4^k - 3*k - 4)/9
1570             #
1571             # side(2^1=2) = 1
1572             # side(2^2=4) = 1 + 1-1 + 1+0 + 1 + 3 = 6 = 4*1 + 2 = 4^1 + 2
1573             # side(2^3=8) = 6 + 6-1 + 6+1 + 6 + 3 = 27 = 4*6 + 3 = 4^2 + 4*2+3
1574             # side(2^4=16) = 27+27-1 +27+2 +27 + 3 = 112 = 4*27 + 4 = 4^3 + 16*2+4*3+4
1575             #
1576             #
1577             #
1578             # centre(2^k) = 2*side(2^(k-1)) + 2*centre(2^(k-1))
1579             # centre(1) = 1
1580             # centre(2) = 4
1581             # centre(4) = 2*side(2) + 2*centre(2)
1582             # = 2*side(2) + 2*4
1583             # = 2*1 + 2*4 = 10
1584             # centre(8) = 2*side(4) + 2*centre(4) = 2*6+2*10 = 32
1585             # = 2*side(4) + 2*(2*side(2) + 2*4)
1586             # = 2*side(4) + 4*side(2) + 4*4
1587             # = 2*6 + 4*1 + 4*4 = 32
1588             # centre(16) = 2*side(4) + 2*centre(4) = 2*6+2*10 = 32
1589             # = 2*side(8) + 4**side(4) + 8*side(2) + 8
1590             # = 2*27 + 4*6 + 8*1 + 8 = 94
1591             #
1592             # 4parts = 4*centre - 7
1593             # 4parts(4) = 4*10-7 = 33
1594             # 4parts(8) = 4*32-7 = 121
1595             #
1596             # 3side total 0,1, 4, 9,17
1597             # +1 +3 +5 +8
1598             #
1599             # centre(2^k)
1600             # = 2*side(2^(k-1)) + 2*centre(2^(k-1))
1601             # = 2*side(2^(k-1) + 2^2*side(2^(k-1) + ... + 2^(k-1)*side(2^1) + 2^(k-1)*4
1602             # k-1 many terms, and constant at end
1603             # side(2^k) = (4*4^k - 3*k - 4)/9
1604             #
1605             # constant part
1606             # 2 + 4 + ... + 2^(k-1)
1607             # = 2^k - 2
1608             # eg. k=2 2
1609             # eg. k=3 2 + 4 = 6
1610             # eg. k=4 2 + 4 + 8 = 14
1611             #
1612             # linear part
1613             # 2*(k-1) + 4*(k-2) + ... + 2^(k-1)*(1) + 2^k*(0)
1614             # = 2^(k-1)-1 + 2^(k-2)-1 + ... + 2-1
1615             # = 2*2^k - 2*k - 2
1616             # eg. k=2 2*1 = 2
1617             # eg. k=3 2*2 + 4*1 = 8
1618             # eg. k=4 2*3 + 4*2 + 8*1 = 22
1619             # eg. k=5 2*4 + 4*3 + 8*2 + 16*1 = 52
1620             #
1621             # exponential part
1622             # 2*4^(k-1) + 4*4^(k-2) + 8*4^(k-3) + ... + 2^(k-1)*4^1
1623             # = 2^(2k-2+1) + 2^(2k-4+2) + 2^(2k-6+3) + ... + 2^(k+1)
1624             # = 2^(2k-1) + 2^(2k-2) + 2^(2k-3) + ... + 2^(k+1)
1625             # = 2^(k+1) * [ 2^(k-2) + 2^(k-3) + 2^(k-4) + ... + 2^(0) ]
1626             # = 2^(k+1) * (2^(k-1) - 1)
1627             # = 2^k * (2^k - 2)
1628             # eg. k=2 2*4^1 = 8
1629             # eg. k=3 2*4^2 + 4*4^1 = 48
1630             # eg. k=4 2*4^3 + 4*4^2 + 8*4^1 = 224
1631             # eg. k=5 2*4^4 + 4*4^3 + 8*4^2 + 16*4^1 = 960
1632             #
1633             # centre(2^k) = (4*(2^k * (2^k - 2)) - 3*(2*2^k-2*k-2) - 4*(2^k-2)) / 9 + 2*2^k
1634             # eg. k=2 sidepart = 2*1 = 1 plus
1635             # eg. k=3 sidepart = 2*6 + 4*1 = 16
1636             # eg. k=4 sidepart = 2*27 + 4*6 + 8*1 = 86
1637             # = (4*(2^k * (2^k - 2)) - 3*(2*2^k-2*k-2) - 4*(2^k-2)) / 9 + 2*2^k
1638             # = (4*2^k*(2^k - 2) - 6*2^k + 3*2*k + 6 - 4*2^k + 8 + 18*2^k) / 9
1639             # = (4*2^k*2^k - 8*2^k - 6*2^k + 3*2*k - 4*2^k + 18*2^k + 14) / 9
1640             # = (4*2^k*2^k + 6*k + 14) / 9
1641             # = (4*depth^2 + 6*k + 14) / 9
1642             #
1643             # centre(2^k) = (4*4^k + 6*k + 14) / 9
1644             # side(2^k) = (4*4^k - 3*k - 4) / 9
1645             # diff = (9k+18)/9 = k+2
1646             # double centre(2^(k+1)) - 4*centre(2^k)
1647             # = (4*4^(k+1) + 6*(k+1) + 14 - 4*(4*4^k + 6*k + 14)) / 9
1648             # = (4*4*4^k + 6*k + 6 + 14 - 4*4*4^k - 4*6*k - 4*14) / 9
1649             # = (6*k - 4*6*k + 6 + 14 - 4*14) / 9
1650             # = (-18*k - 36) / 9
1651             # = -2*k - 4
1652             # smaller than 4* on each doubling
1653             # 6k+14 term only adds extra 6, doesn't go 4*(6k+14)
1654             #
1655             # side(pow+rem) = side(pow) + side(rem+1) -1 if rem+1=pow
1656             # + side(rem)
1657             # + side(rem) + log2(rem+1) + 2
1658             # except rem==1 is side(pow)+3
1659             # eg side(5) = side(4) + 3
1660             # = 6 + 3 = 9
1661             # eg side(6) = side(4) + side(3) + 2*side(2) + log2(3)+2
1662             # = 6 + 3 + 2*1 +1 + 2 = 14
1663             #
1664             # centre(pow+rem) = centre(pow) + centre(rem) + 2*side(rem)
1665             # = 2*side(pow/2) + 4*side(pow/4) + ...
1666             # + centre(rem) + 2*side(rem)
1667              
1668             # d = p1+p2+p3+p4
1669             # C(d) = C(p1) + 2*S(p2+p3+p4) + C(p2+p3+p4)
1670             # = C(p1) + 2*S(p2+p3+p4) + C(p2) + 2*S(p3+p4) + C(p3+p4)
1671             # = C(p1) + C(p2) + 2*S(p2+p3+p4) + 2*S(p3+p4) + C(p3) + C(p4) + 2*S(p4)
1672             # = C(p1) + C(p2) + C(p3) + C(p4) + 2*S(p2+p3+p4) + 2*S(p3+p4) + 2*S(p4)
1673             # eg. C(4+1) = C(4) + C(1) + 2*S(1)
1674             # = 10 + 1 + 2*0 = 11
1675             # eg. C(4+1) = C(4) + C(2) + 2*S(2)
1676             # = 10 + 4 + 2*1 = 18
1677             # eg. C(8+1) = C(8) + C(1) + 2*S(1)
1678             # = 32 + 1 + 2*0 = 35
1679             # eg. C(8+2) = C(8) + C(2) + 2*S(2)
1680             # = 32 + 4 + 2*1 = 38
1681             # eg. C(8+4) = C(8) + C(4) + 2*S(4)
1682             # = 32 + 10 + 2*6 = 54
1683             # eg. C(8+4+1) = C(8) + C(4) + C(1) + 2*S(4+1) + 2*S(1)
1684             # = 32 + 10 + 1 + 2*9 + 2*0 = 61
1685             # eg. C(8+4+2) = C(8) + C(4) + C(2) + 2*S(4+2) + 2*S(2)
1686             # = 32 + 10 + 4 + 2*14 + 2*1 = 76
1687             #
1688             # A151735
1689             # before 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
1690             # centre = 0,1,4,5, 10,11,16,21, 32,33,38,43,54,61 76 95 118
1691             #
1692             # before 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
1693             # side = 0, 1,3, 6,9,14,21, 27,30,35,43,52,63,80,100, 112
1694             #
1695             # A151725 total cells 0,1,9,13, 33,37,57,77, 121,125,145,165,209,237,297,373,
1696             #
1697             #
1698             # 15 | 15 15 15 15 15 15 15 15 15 15 15 15
1699             # 14 | 14 14 14 14 15
1700             # 13 | 14 13 13 13 14 14 13 13 13 15
1701             # 12 | 14 12 12 13
1702             # 11 | 12 11 11 11 11 11 11 13 15
1703             # 10 | 14 12 10 10 11 14 14 15
1704             # 9 | 14 13 13 10 9 9 9 11 15
1705             # 8 | 8 9
1706             # 7 | 7 7 7 7 7 7 9 11 15
1707             # 6 | 6 6 7 10 10 11 14 14 15 19 18
1708             # 5 | 6 5 5 5 7 11 13 15 20 15 14 13
1709             # 4 | 4 5 13 12 12 12 13 10 12
1710             # 3 | 3 3 3 5 7 13 13 15 9 8 7 11
1711             # 2 | 2 3 6 6 7 14 14 14 14 14 15 4 6 16 17
1712             # 1 | 1 1 3 7 15 3 2 5
1713             # 0 | 0 1 0 1
1714             # +----------------------------------------------
1715             # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
1716             #
1717             # same mirror 1->9 same 1->9
1718             # extra log(d) in Y=8 row
1719             #
1720             # 16 | 16
1721             # 15 | 15 15 15 15 15 15 15 15 15 15 15 15 16 k=4 depth=16
1722             # 14 | 14 14 14 14 16
1723             # 13 | 14 13 13 13 14 14 13 13 13 14
1724             # 12 | 14 12 12 14
1725             # 11 | 12 11 11 11 11 11 11 12
1726             # 10 | 14 12 10 10 12 14
1727             # 9 | 14 13 13 10 9 9e 9d10 13 13 14
1728             # 8 | 8c 10 14
1729             # 7 | 7 7 7 7 7 7 8b
1730             # 6 | 6 6 8a 10 14 rotate -90 1->8
1731             # 5 | 6 5 5 5 6 9 9 10 13 13 14 miss one in corner
1732             # 4 | 4 6 10 12 14
1733             # 3 | 3 3 3 4 12 11 11 11 12
1734             # 2 | 2 4 6 12 12 14
1735             # 1 | 1 1 2 5 5 6 13 13 13 13 13 14
1736             # 0 | 0 . **** ****
1737             # +---------------------------------------------------
1738             # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
1739             #
1740             # Octant
1741             #
1742             # 16 |
1743             # 15 | 15
1744             # 14 | 14 15
1745             # 13 | 13 15
1746             # 12 | 12 13
1747             # 11 | 11 13 15
1748             # 10 | 10 11 14 14 15
1749             # 9 | 9 11 15
1750             # 8 | 8 9
1751             # 7 | 7 9 11 15
1752             # 6 | 6 7 10 10 11 14 14 15
1753             # 5 | 5 7 11 13 15
1754             # 4 | 4 5 13 12 12 12 13
1755             # 3 | 3 5 7 13 13 15
1756             # 2 | 2 3 6 6 7 14 14 14 14 14 15
1757             # 1 | 1 3 7 15
1758             # 0 | 0 1
1759             # +---------------------------------------------------
1760             # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
1761             #
1762             # oct(pow+rem) = oct(pow)
1763             # + oct(rem) # extend
1764             # + oct(rem) # upper
1765             # + oct(rem+1) # lower
1766             # - rem # undouble spine
1767             # + 2*floor(log2(rem+1)) # upper+extend log2_extras
1768             #
1769             # side(rem) = oct(rem) + oct(rem+1)
1770             # - rem # no double spine
1771             # + floor(log2(rem+1)) # upper log2_extras
1772             #
1773             # pow=2^k
1774             # oct(2*pow) = 4*oct(pow) + 2*(k-2) - (pow-2)
1775             # oct(2^0=1) = 0
1776             # oct(2^1=2) = 1
1777             # oct(2^2=4) = 4 = 4*1 - 0
1778             # oct(2^3=8) = 16 = 4*4 - 0
1779             # oct(2^4=16) = 16+7+4+7+3+4+5+4+3+3+3+2+1 = 62 = 4*16 - 2
1780              
1781             # 3side
1782             #
1783             # **** *** *** *** *** *** *** ***
1784             # * * * * * * * * *
1785             # ** ***** ***** ***** *****
1786             # * * * * * * * *
1787             # ** **** **** **** ****
1788             # * * * * * * * * * * * * *
1789             # ** *** ***** *** *** ***** ***
1790             # * * * * * * side side
1791             # ** *888 888 888 888* depth+1
1792             # * * * * 7 7 7 7 * * * upper | upper
1793             # *** *** 76667 76667 *** *** depth-1 | depth-1
1794             # * * * * 7 5 5 7 * * * \ |
1795             # ** ***** 5444 4445 ***** \ | /
1796             # * * * * 7 5 3 3 5 7 * * * lower \ | / lower
1797             # ** **** ** 766 32223 667 ** **** depth \ | / depth
1798             # 1 3 7 * ---------------------------
1799             # 01 | \ upper
1800             # 1 3 7 * | \ depth
1801             # 223 667 ** **** | \
1802             # 3 5 7 * * * | lower \
1803             # 54445 ***** | depth+1 side
1804             # 5 5 7 * * *
1805             # 66 6667 *** ***
1806             # 7 * * *
1807             # dcc 9888*
1808             # d b 9 * * *
1809             # baaa **** ***
1810             # e b * * *
1811             # dcccd *****
1812             # d d * * *
1813             # ee eee *** ****
1814             # *
1815              
1816             my @oct_to_n = (0, 1);
1817              
1818             my %tree_depth_to_n = (4 => [ 0, 1 ],
1819             1 => [ 0, 1 ],
1820             octant => [ 0, 1 ],
1821             wedge => [ 0, 1, 4 ],
1822             '3mid' => [ 0, 1 ],
1823             '3side' => [ 0, 1, 4 ],
1824             side => [ 0, 1 ]);
1825             my %tree_depth_to_n_extra_depth_pow = (4 => 0,
1826             1 => 0,
1827             octant => 0,
1828             octant_up => 0,
1829             wedge => 0,
1830             '3mid' => 1,
1831             '3side' => 1,
1832             side => 1);
1833              
1834             sub tree_depth_to_n {
1835 413     413 1 1620 my ($self, $depth) = @_;
1836             ### tree_depth_to_n(): "$depth parts=$self->{'parts'}"
1837              
1838 413         259 $depth = int($depth);
1839 413 50       466 if ($depth < 0) {
1840 0         0 return undef;
1841             }
1842              
1843 413         335 my $parts = $self->{'parts'};
1844             {
1845 413         269 my $initial = $tree_depth_to_n{$parts};
  413         295  
1846 413 100       584 if ($depth <= $#$initial) {
1847             ### table %tree_depth_to_n{}: $initial->[$depth]
1848 21         38 return $initial->[$depth];
1849             }
1850             }
1851              
1852 392         634 my ($pow,$exp) = round_down_pow
1853             ($depth + $tree_depth_to_n_extra_depth_pow{$parts},
1854             2);
1855 392 50       2370 if (is_infinite($exp)) {
1856 0         0 return $exp;
1857             }
1858             ### $pow
1859             ### $exp
1860              
1861 392         1305 my $zero = $depth * 0; # inherit bignum
1862 392         236 my $n = $zero;
1863              
1864             # @side is a list of depth values.
1865             # @mult is the multiple of T[depth] desired for that @side entry.
1866             #
1867             # @side is mostly high to low and growing by one more value at each
1868             # $exp level, but sometimes it's a bit more and some values not high to
1869             # low and possibly duplicated.
1870             #
1871 392         311 my @pending = ($depth);
1872 392         243 my @mult;
1873              
1874 392 100 100     637 if ($parts eq '4') {
    100          
    100          
    100          
    100          
    50          
    0          
1875 209         156 @mult = (8);
1876 209         172 $n -= 4*$depth + 7;
1877              
1878             } elsif ($parts eq '1') {
1879 123         93 @mult = (2);
1880 123         91 $n -= $depth;
1881              
1882             } elsif ($parts eq 'octant' || $parts eq 'octant_up') {
1883 27         24 @mult = (1);
1884              
1885             } elsif ($parts eq 'wedge') {
1886 9         6 push @mult, 2;
1887 9         7 $n -= 2; # unduplicate centre two
1888              
1889             } elsif ($parts eq '3mid') {
1890 12         12 unshift @pending, $depth+1;
1891 12         8 @mult = (2, 4);
1892             # Duplicated diagonals, and no log2_extras on two outermost octants.
1893             # Each log2 at depth=2^k-2, so another log2 decrease when depth=2^k-1.
1894             # $exp == _log2_floor($depth+1) so at $depth==2*$pow-1 one less.
1895 12         15 $n -= 3*$depth + 2*$exp + 6;
1896              
1897             } elsif ($parts eq '3side') {
1898 12         24 @pending = ($depth+1, $depth, $depth-1);
1899 12         12 @mult = (1, 3, 2);
1900             # Duplicated diagonals, and no log2_extras on two outermost octants.
1901             # For plain depth each log2 at depth=2^k-2, so another log2 decrease
1902             # when depth=2^k-1.
1903             # For depth+1 block each log2 at depth=2^k-2, so another log2 decrease
1904             # when depth=2^k-2.
1905             # $exp == _log2_floor($depth+1) so at $depth==2*$pow-1 one less.
1906 12 100       15 $n -= 3*$depth + 2*$exp + ($depth == $pow-1 ? 3 : 4);
1907              
1908             } elsif ($parts eq 'side') {
1909 0         0 unshift @pending, $depth+1;
1910 0         0 @mult = (1, 1);
1911             # $exp == _log2_floor($depth+1)
1912 0         0 $n -= $depth + 1 + $exp;
1913             }
1914              
1915 392   100     1028 while ($exp >= 0 && @pending) {
1916             ### at: "pow=$pow exp=$exp n=$n"
1917             ### assert: $pow == 2 ** $exp
1918             ### pending: join(',',@pending)
1919             ### mult: join(',',@mult)
1920              
1921 469         327 my @new_pending;
1922             my @new_mult;
1923 0         0 my $oct_pow;
1924 469         377 foreach my $depth (@pending) {
1925 566         396 my $mult = shift @mult;
1926             ### assert: $depth >= 0
1927              
1928 566 100       659 if ($depth <= 1) {
1929             ### small depth: "depth=$depth mult=$mult * $oct_to_n[$depth]"
1930 3         2 $n += $mult * $depth; # oct=0 at depth=0, oct=1 at depth=1
1931 3         4 next;
1932             }
1933 563         363 my $rem = $depth - $pow;
1934 563 100       631 if ($rem < 0) {
1935 24         16 push @new_pending, $depth;
1936 24         17 push @new_mult, $mult;
1937 24         23 next;
1938             }
1939              
1940             ### $depth
1941             ### $mult
1942             ### $rem
1943             ### assert: $rem >= 0 && $rem < $pow
1944              
1945 539         313 my $powmult = $mult;
1946 539 100       523 if ($rem <= 1) {
1947 474 100       465 if ($rem == 0) {
1948             ### rem=0, oct(pow) only ...
1949             } else { # $rem == 1
1950             ### rem=1, oct(pow)+1 ...
1951 177         124 $n += $mult;
1952             }
1953             } else {
1954             ### formula ...
1955             # oct(pow+rem) = oct(pow)
1956             # + oct(rem+1)
1957             # + 2*oct(rem)
1958             # - floor(log2(rem+1))
1959             # - rem - 3
1960              
1961 65         39 my $rem1 = $rem + 1;
1962             {
1963 65         35 my ($lpow,$lexp) = round_down_pow ($rem1, 2);
  65         88  
1964 65         359 $n -= ($lexp + $rem + 3)*$mult;
1965             ### sub also: ($lexp + $rem + 3). " *mult=$mult"
1966             }
1967 65 100 33     119 if ($rem1 == $pow) {
    50          
1968             ### rem+1 == pow, increase powmult ...
1969 16         12 $powmult *= 2; # oct(pow)+oct(rem+1) is 2*oct(pow)
1970             } elsif (@new_pending && $new_pending[-1] == $rem1) {
1971             ### merge into previously pushed new_pending[] ...
1972             # print "rem+1=$rem1 ",join(',',@new_pending),"\n";
1973 0         0 $new_mult[-1] += $mult;
1974             } else {
1975             ### push: "depth=$rem1 mult=$mult"
1976 49         40 push @new_pending, $rem1;
1977 49         28 push @new_mult, $mult;
1978             }
1979              
1980             ### push: "depth=$rem mult=".2*$mult
1981 65         45 push @new_pending, $rem;
1982 65         51 push @new_mult, 2*$mult;
1983             }
1984              
1985             # oct(pow) = (2*pow*pow + 3*exp + 7)/9 + pow/2
1986             # = ((4*pow+9)*pow + 6*exp + 14)/18
1987             #
1988 539   66     1114 $oct_pow ||= ((4*$pow+9)*$pow + 6*$exp + 14)/18;
1989 539         632 $n += $oct_pow * $powmult;
1990             ### oct(pow): "pow=$pow is $oct_pow * powmult=$powmult"
1991             }
1992 469         422 @pending = @new_pending;
1993 469         374 @mult = @new_mult;
1994              
1995 469         283 $exp--;
1996 469         1203 $pow /= 2;
1997             }
1998              
1999             ### return: $n
2000 392         445 return $n;
2001             }
2002              
2003              
2004             # _depth_to_octant_added() returns the number of cells added at a given
2005             # $depth level in parts=octant. This is the same as
2006             # $added = tree_depth_to_n(depth+1) - tree_depth_to_n(depth)
2007             #
2008             # @$depth_aref is a list of depth values.
2009             # @$mult_aref is the multiple of oct(depth) desired for each @depth_aref.
2010             #
2011             # On input @$depth_aref must have $depth_aref->[0] as the highest value.
2012             #
2013             # Within the code the depth list is mostly high to low and growing by one
2014             # extra depth value at each $exp level. But sometimes it grows a bit more
2015             # than that and sometimes the values are not high to low, and sometimes
2016             # there's duplication.
2017             #
2018             my @_depth_to_octant_added = (1, 2, 1); # depth=0to2 small values
2019              
2020             sub _depth_to_octant_added {
2021 122     122   129 my ($depth_aref, $mult_aref, $zero) = @_;
2022             ### _depth_to_octant_added(): join(',',@$depth_aref)
2023             ### mult_aref: join(',',@$mult_aref)
2024             ### assert: scalar(@$depth_aref) == scalar(@$mult_aref)
2025              
2026             # $depth_aref->[0] must be the biggest depth, to make the $pow finding easy
2027             ### assert: scalar(@$depth_aref) >= 1
2028             ### assert: max(@$depth_aref) == $depth_aref->[0]
2029              
2030 122         193 my ($pow,$exp) = round_down_pow ($depth_aref->[0], 2);
2031 122 50       749 if (is_infinite($exp)) {
2032 0         0 return $exp;
2033             }
2034             ### $pow
2035             ### $exp
2036              
2037 122         424 my $added = $zero;
2038              
2039             # running $pow down to 2 (inclusive)
2040 122   66     332 while ($exp >= 0 && @$depth_aref) {
2041             ### at: "pow=$pow exp=$exp"
2042             ### assert: $pow == 2 ** $exp
2043              
2044             ### depth: join(',',@$depth_aref)
2045             ### mult: join(',',@$mult_aref)
2046 127         78 my @new_depth;
2047             my @new_mult;
2048 127         109 foreach my $depth (@$depth_aref) {
2049 132         104 my $mult = shift @$mult_aref;
2050             ### assert: $depth >= 0
2051              
2052 132 100       178 if ($depth <= $#_depth_to_octant_added) {
2053             ### small depth: "depth=$depth mult=$mult * $_depth_to_octant_added[$depth]"
2054 17         15 $added += $mult * $_depth_to_octant_added[$depth];
2055 17         21 next;
2056             }
2057 115 50       136 if ($depth < $pow) {
2058 0         0 push @new_depth, $depth;
2059 0         0 push @new_mult, $mult;
2060 0         0 next;
2061             }
2062              
2063 115         89 my $rem = $depth - $pow;
2064              
2065             ### $depth
2066             ### $mult
2067             ### $rem
2068             ### assert: $rem >= 0 && $rem < $pow
2069              
2070 115 100       115 if ($rem <= 1) {
2071 99 100       99 if ($rem == 0) {
2072             ### rem=0, grow 1 ...
2073 8         11 $added += $mult;
2074             } else {
2075             ### rem=1, grow 3 ...
2076 91         111 $added += 3 * $mult;
2077             }
2078             } else {
2079 16         12 my $rem1 = $rem + 1;
2080 16 100       17 if ($rem1 == $pow) {
2081             ### rem+1=pow, no lower part, 3/2 of pow ...
2082 11         18 $added += ($pow/2) * (3*$mult);
2083             } else {
2084             ### formula ...
2085             # oadd(pow+rem) = oadd(rem+1) + 2*oadd(rem)
2086             # + (is_pow2($rem+2) ? -2 : -1)
2087              
2088             # upper/lower diagonal overlap, and no log2_extras in lower
2089 5 50       10 $added -= (_is_pow2($rem+2) ? 2*$mult : $mult);
2090              
2091 5 50 33     14 if (@new_depth && $new_depth[-1] == $rem1) {
2092             ### merge into previously pushed new_depth ...
2093             # print "rem=$rem ",join(',',@new_depth),"\n";
2094 0         0 $new_mult[-1] += $mult;
2095             } else {
2096             ### push: "rem+1 depth=$rem1 mult=$mult"
2097 5         7 push @new_depth, $rem1;
2098 5         4 push @new_mult, $mult;
2099             }
2100              
2101             ### push: "rem depth=$rem mult=".2*$mult
2102 5         3 push @new_depth, $rem;
2103 5         9 push @new_mult, 2*$mult;
2104             }
2105             }
2106             }
2107 127         127 $depth_aref = \@new_depth;
2108 127         86 $mult_aref = \@new_mult;
2109              
2110 127         82 $exp--;
2111 127         367 $pow /= 2;
2112             }
2113              
2114             ### return: $added
2115 122         147 return $added;
2116             }
2117              
2118              
2119             #------------------------------------------------------------------------------
2120             # tree_n_to_subheight()
2121              
2122             #use Smart::Comments;
2123              
2124             {
2125             my %tree_n_to_subheight
2126             = do {
2127             my $depth0 = [ ]; # depth=0
2128             (wedge => [ $depth0,
2129             [ undef, 0 ], # depth=1
2130             ],
2131             '3mid' => [ $depth0,
2132             [ undef, 0, undef, 0 ], # depth=1
2133             ],
2134             '3side' => [ $depth0,
2135             [ undef, 0, undef ], # depth=1
2136             [ 0, undef, undef, 0 ], # depth=2 N=4to8
2137             ],
2138             )
2139             };
2140              
2141             sub tree_n_to_subheight {
2142 0     0 1 0 my ($self, $n) = @_;
2143             ### tree_n_to_subheight(): $n
2144              
2145 0 0       0 if ($n < 0) { return undef; }
  0         0  
2146 0 0       0 if (is_infinite($n)) { return $n; }
  0         0  
2147              
2148 0         0 my $zero = $n * 0;
2149 0         0 (my $depth, $n) = _n0_to_depth_and_rem($self, int($n));
2150             ### $depth
2151             ### $n
2152              
2153 0         0 my $parts = $self->{'parts'};
2154 0 0       0 if (my $initial = $tree_n_to_subheight{$parts}->[$depth]) {
2155             ### $initial
2156 0         0 return $initial->[$n];
2157             }
2158              
2159 0 0       0 if ($parts eq 'octant') {
    0          
    0          
    0          
    0          
2160 0         0 my $add = _depth_to_octant_added ([$depth],[1], $zero);
2161 0         0 $n = $add-1 - $n;
2162             ### octant mirror numbering to n: $n
2163              
2164             } elsif ($parts eq 'octant_up') {
2165              
2166             } elsif ($parts eq 'wedge') {
2167 0         0 my $add = _depth_to_octant_added ([$depth],[1], $zero);
2168             ### assert: $n < 2*$add
2169 0 0       0 if ($n >= $add) {
2170             ### wedge second half ...
2171 0         0 $n = 2*$add-1 - $n; # mirror
2172             }
2173              
2174             } elsif ($parts eq '3mid') {
2175 0         0 my $add = _depth_to_octant_added ([$depth+1],[1], $zero);
2176 0 0       0 if (_is_pow2($depth+2)) { $add -= 1; }
  0         0  
2177             ### $add
2178              
2179 0         0 $n -= $add-1;
2180             ### n decrease to: $n
2181 0 0       0 if ($n < 0) {
2182             ### 3mid first octant, mirror ...
2183 0         0 $n = - $n;
2184 0         0 $depth += 1;
2185             }
2186              
2187 0         0 $add = _depth_to_octant_added ([$depth],[1], $zero);
2188 0         0 my $end = 4*$add - 2;
2189             ### $add
2190             ### $end
2191 0 0       0 if ($n >= $end) {
2192             ### 3mid last octant ...
2193 0         0 $n -= $end;
2194 0         0 $depth += 1;
2195             } else {
2196 0         0 $n %= 2*$add-1;
2197 0 0       0 if ($n >= $add) {
2198             ### 3mid second half, mirror ...
2199 0         0 $n = 2*$add-1 - $n;
2200             }
2201             }
2202              
2203             } elsif ($parts eq '3side') {
2204 0         0 my $add = _depth_to_octant_added ([$depth+1],[1], $zero);
2205 0 0       0 if (_is_pow2($depth+2)) { $add -= 1; }
  0         0  
2206             ### $add
2207              
2208 0         0 $n -= $add-1;
2209             ### n decrease to: $n
2210 0 0       0 if ($n < 0) {
2211             ### 3side first octant, mirror ...
2212 0         0 $n = - $n;
2213 0         0 $depth += 1;
2214             }
2215              
2216 0         0 $add = _depth_to_octant_added ([$depth],[1], $zero);
2217 0 0       0 if ($n < 2*$add) {
2218 0 0       0 if ($n >= $add) {
2219 0         0 $n = 2*$add-1 - $n;
2220             }
2221             } else {
2222 0         0 $n -= 2*$add-1;
2223              
2224 0         0 $add = _depth_to_octant_added ([$depth-1],[1], $zero);
2225 0 0       0 if ($n < 2*$add) {
2226 0         0 $depth -= 1;
2227 0 0       0 if ($n >= $add) {
2228 0         0 $n = 2*$add-1 - $n;
2229             }
2230             } else {
2231 0         0 $n -= 2*$add-1;
2232             }
2233             }
2234              
2235             } else {
2236             ### assert: $parts eq '1' || $parts eq '4'
2237 0 0       0 if ($depth == 1) {
2238 0 0       0 return ($n % 2 ? undef : 0);
2239             }
2240 0         0 my $add = _depth_to_octant_added([$depth],[1], $zero);
2241              
2242             # quadrant rotate ...
2243 0         0 $n %= 2*$add-1;
2244              
2245 0         0 $n -= $add;
2246 0 0       0 if ($n < 0) {
2247             ### lower octant ...
2248 0         0 $n = -1-$n; # mirror
2249             } else {
2250             ### upper octant ...
2251 0         0 $n += 1; # undouble spine
2252             }
2253             }
2254              
2255 0         0 my $dbase;
2256 0         0 my ($pow,$exp) = round_down_pow ($depth, 2);
2257              
2258 0         0 for ( ; $exp-- >= 0; $pow /= 2) {
2259             ### at: "depth=$depth pow=$pow n=$n dbase=".($dbase||'inf')
2260             ### assert: $n >= 0
2261              
2262 0 0       0 if ($n == 0) {
2263             ### n=0 on spine ...
2264 0         0 last;
2265             }
2266 0 0       0 next if $depth < $pow;
2267              
2268 0 0       0 if (defined $dbase) { $dbase = $pow; }
  0         0  
2269 0         0 $depth -= $pow;
2270             ### depth remaining: $depth
2271              
2272 0 0       0 if ($depth == 1) {
2273             ### assert: 1 <= $n && $n <= 2
2274 0 0       0 if ($n == 1) {
2275             ### depth=1 and n=1 remaining ...
2276 0         0 return 0;
2277             }
2278 0         0 $n += 1;
2279             }
2280              
2281 0         0 my $add = _depth_to_octant_added ([$depth],[1], $zero);
2282             ### $add
2283              
2284 0 0       0 if ($n < $add) {
2285             ### extend part, unchanged ...
2286             } else {
2287 0         0 $dbase = $pow;
2288 0         0 $n -= 2*$add;
2289             ### sub 2*add to: $n
2290              
2291 0 0       0 if ($n < 0) {
2292             ### upper part, mirror to n: -1 - $n
2293 0         0 $n = -1 - $n; # mirror, $n = $add-1 - $n = -($n-$add) - 1
2294             } else {
2295             ### lower part ...
2296 0         0 $depth += 1;
2297 0         0 $n += 1; # undouble upper,lower spine
2298             }
2299             }
2300              
2301             }
2302              
2303             ### final ...
2304             ### $dbase
2305             ### $depth
2306 0 0       0 return (defined $dbase ? $dbase - $depth - 1 : undef);
2307             }
2308             }
2309              
2310             #------------------------------------------------------------------------------
2311             # levels
2312              
2313             sub level_to_n_range {
2314 70     70 1 1457 my ($self, $level) = @_;
2315 70         60 my $depth = 2**$level;
2316 70 100       109 unless ($self->{'parts'} eq '3side') { $depth -= 1; }
  60         47  
2317 70         97 return (0, $self->tree_depth_to_n_end($depth));
2318             }
2319             sub n_to_level {
2320 0     0 1 0 my ($self, $n) = @_;
2321 0         0 my $depth = $self->tree_n_to_depth($n);
2322 0 0       0 if (! defined $depth) { return undef; }
  0         0  
2323 0 0       0 unless ($self->{'parts'} eq '3side') { $depth += 1; }
  0         0  
2324 0         0 my ($pow, $exp) = round_up_pow ($depth, 2);
2325 0         0 return $exp;
2326             }
2327              
2328             #------------------------------------------------------------------------------
2329              
2330             # return true if $n is a power 2^k for k>=0
2331             sub _is_pow2 {
2332 8     8   7 my ($n) = @_;
2333 8         11 my ($pow,$exp) = round_down_pow ($n, 2);
2334 8         51 return ($n == $pow);
2335             }
2336             sub _log2_floor {
2337 0     0     my ($n) = @_;
2338 0 0         if ($n < 2) { return 0; }
  0            
2339 0           my ($pow,$exp) = round_down_pow ($n, 2);
2340 0           return $exp;
2341             }
2342              
2343             1;
2344             __END__