File Coverage

blib/lib/Math/PlanePath/RationalsTree.pm
Criterion Covered Total %
statement 176 222 79.2
branch 62 90 68.8
condition 11 28 39.2
subroutine 26 36 72.2
pod 18 18 100.0
total 293 394 74.3


line stmt bran cond sub pod time code
1             # Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Kevin Ryde
2              
3             # This file is part of Math-PlanePath.
4             #
5             # Math-PlanePath is free software; you can redistribute it and/or modify
6             # it under the terms of the GNU General Public License as published by the
7             # Free Software Foundation; either version 3, or (at your option) any later
8             # version.
9             #
10             # Math-PlanePath is distributed in the hope that it will be useful, but
11             # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12             # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13             # for more details.
14             #
15             # You should have received a copy of the GNU General Public License along
16             # with Math-PlanePath. If not, see .
17              
18              
19             # SB,CW N with same X,Y is those N which are palindromes below high 1-bit
20             # as noted Claudio Bonanno and Stefano Isola, ``Orderings of the Rationals
21             # and Dynamical Systems'', May 16, 2008.
22             # cf A006995 binary palindromes, so always odd
23             # A178225 characteristic of binary palindromes
24             # A048700 binary palindromes odd length
25             # A048701 binary palindromes even length
26             # A044051 binary palindromes (B+1)/2, B odd so B+1 even
27             # A044051-1 = (B-1)/2 strips low 1-bit to be palindromes below high 1-bit
28              
29             # Boyko B. Bantchev, "Fraction Space Revisited"
30             # http://www.math.bas.bg/bantchev/articles/fractions.pdf
31              
32             # cf Ronald L. Graham, Donald E. Knuth, and Oren Patashnik, Concrete
33             # Mathematics: A Foundation for Computer Science, Second
34             # Edition. Addison-Wesley. 1994.
35             # On Stern-Brocot tree.
36              
37             # cf A054429 permutation reverse within binary row
38             # A065249 - permutation SB X -> X/2
39             # A065250 - permutation SB X -> 2X
40             # A057114 - permutation SB X -> X+1
41             # A057115 - permutation SB X -> X-1
42             #
43              
44             # high-to-low low-to-high
45             # (X+Y)/Y Y/(X+Y) HCS AYT
46             # X/(X+Y) (X+Y)/Y CW SB \ alt bit flips
47             # Y/(X+Y) (X+Y)/X Drib Bird /
48             #
49             # 9 10 12 10
50             # 8 11 8 14
51             # 12 13 9 13
52             # 14 11
53             # 15 15
54             #
55             # Stern-Brocot Calkin-Wilf
56              
57              
58             #------------------------------------------------------------------------------
59             # HCS turn left when even number of 1-bits in N+1
60             # turn right when odd number of 1-bits in N+1
61             #
62             # A010059 start=0: 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0
63             # match 1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0
64             # PlanePathTurn planepath=RationalsTree,tree_type=HCS, turn_type=Left
65             #
66             # A010060 start=0: 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,1,0,1
67             # match 0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,0,1,1,0,1
68             # PlanePathTurn planepath=RationalsTree,tree_type=HCS, turn_type=Right
69             #
70             # 10 | 768 50 58 896
71             # 9 | 384 49 52 60 57 448 640
72             # 8 | 192 27 31 224 320
73             # 7 | 96 25 26 30 29 112 160 41 42
74             # 6 | 48 56 80
75             # 5 | 24 13 15 28 40 21 23 44
76             # 4 | 12 14 20 22 36
77             # 3 | 6 7 10 11 18 19 34
78             # 2 | 3 5 9 17 33
79             # 1 | 1 2 4 8 16 32 64 128 256 512
80             # Y=0 |
81             # +-----------------------------------------------------
82             # X=0 1 2 3 4 5 6 7 8 9 10
83             #
84             # 1/1
85             # /------------- -------------\
86             # 2/1 1/2 2,3 L,R
87             # /---- ----\ /---- ----\
88             # 3/1 3/2 1/3 2/3 4,5,6,7 L,L,R,R
89             # / \ / \ / \ / \ 8 12
90             # 4/1 5/2 4/3 5/3 1/4 2/5 3/4 3/5 L,L,R,L, R,R,L,R
91             # / \ / \ / \ / \ / \ / \ / \ / \
92             # 5/1 7/2 7/3 8/3 5/4 7/5 7/4 8/5 1/5 2/7 3/7 3/8 4/5 5/7 4/7 5/8
93             #
94             # *
95             # / \ U=0 = X+Y, Y shear
96             # / * D=1 = Y, X+Y shear+transpose
97             # / \a = 0.1^k.1
98             # N
99             # \ /b = 1.0^k.0
100             # \ *
101             # \ / \c = 1.0^k.1 c=even bits, left
102             # *
103             #
104             # F[-1]=1 F[0]=0 F[1]=1 F[2]=1 F[3]=2 F[4]=3 F[5]=5 ...
105             # 1^k is F[k-1]*X+F[k]*Y, F[k]*X+F[k+1]*Y
106             # X , Y 0
107             # Y, X+ Y 1
108             # X+ Y, X+2Y 2
109             # X+2Y, 2X+3Y 3
110             # 2X+3Y, 3X+5Y 4
111             #
112             # then aX = F[k]*X+F[k+1]*Y + F[k+1]*X+F[k+2]*Y
113             # = (F[k]+F[k+1])*X + (F[k+1]+F[k+2])*Y
114             # = F[k+2]*X + F[k+3]*Y
115             # aY = F[k+1]*X + F[k+2]*Y near X=phi*Y big
116             #
117             # 0^k is X+k*Y, Y
118             # so bX = Y
119             # bY = X+k*Y + Y = X+(k+1)*Y near Y axis
120             #
121             # c1X = Y
122             # c1Y = X+Y
123             # c2X = Y + k*(X+Y) = k*X + (k+1)*Y
124             # c2Y = X+Y
125             # cX = X+Y
126             # cY = k*X + (k+1)*Y + X+Y = (k+1)X + (k+2)Y near X=Y
127              
128             #
129             # *
130             # / \ /a = 0.1^k.0
131             # / *
132             # / \b = 0.1^k.1
133             # N
134             # \ /c = 1.0^k.1 c=even bits, left
135             # \ *
136             # \ /
137             # *
138             #------------------------------------------------------------------------------
139              
140             package Math::PlanePath::RationalsTree;
141 3     3   3263 use 5.004;
  3         12  
142 3     3   18 use strict;
  3         6  
  3         87  
143 3     3   17 use Carp 'croak';
  3         6  
  3         218  
144             #use List::Util 'max';
145             *max = \&Math::PlanePath::_max;
146              
147 3     3   19 use vars '$VERSION', '@ISA';
  3         12  
  3         195  
148             $VERSION = 128;
149 3     3   838 use Math::PlanePath;
  3         6  
  3         116  
150             @ISA = ('Math::PlanePath');
151              
152             use Math::PlanePath::Base::Generic
153 3         156 'is_infinite',
154 3     3   19 'round_nearest';
  3         8  
155             use Math::PlanePath::Base::Digits
156 3         239 'round_down_pow',
157             'bit_split_lowtohigh',
158 3     3   622 'digit_join_lowtohigh';
  3         7  
159             *_divrem = \&Math::PlanePath::_divrem;
160              
161 3     3   1184 use Math::PlanePath::CoprimeColumns;
  3         11  
  3         249  
162             *_coprime = \&Math::PlanePath::CoprimeColumns::_coprime;
163              
164             # uncomment this to run the ### lines
165             #use Smart::Comments;
166              
167              
168 3         195 use constant parameter_info_array =>
169             [ { name => 'tree_type',
170             share_key => 'tree_type_rationalstree',
171             display => 'Tree Type',
172             type => 'enum',
173             default => 'SB',
174             choices => ['SB','CW','AYT','HCS','Bird','Drib','L'],
175             choices_display => ['SB','CW','AYT','HCS','Bird','Drib','L'],
176             },
177 3     3   20 ];
  3         8  
178              
179 3     3   18 use constant class_x_negative => 0;
  3         7  
  3         133  
180 3     3   18 use constant class_y_negative => 0;
  3         5  
  3         274  
181             sub x_minimum {
182 0     0 1 0 my ($self) = @_;
183 0 0       0 return ($self->{'tree_type'} eq 'L' ? 0 : 1);
184             }
185 3     3   20 use constant y_minimum => 1;
  3         6  
  3         184  
186 3     3   21 use constant gcdxy_maximum => 1; # no common factor
  3         7  
  3         150  
187 3     3   19 use constant tree_num_children_list => (2); # complete binary tree
  3         6  
  3         151  
188 3     3   17 use constant tree_n_to_subheight => undef; # complete tree, all infinity
  3         8  
  3         4798  
189              
190             {
191             my %absdy_minimum = (# SB => 0,
192             CW => 1,
193             # AYT => 0,
194             # Bird => 0,
195             # Drib => 0,
196             L => 1);
197             sub absdy_minimum {
198 0     0 1 0 my ($self) = @_;
199 0   0     0 return $absdy_minimum{$self->{'tree_type'}} || 0;
200             }
201             }
202              
203             {
204             # Drib apparent minimum dX=k dY=2*k+1 approaches dX=1,dY=2
205             my %dir_minimum_dxdy = (CW => [0,1],
206             Drib => [1,2],
207             L => [1,1], # at N=0 dX=1,dY=1
208             );
209             sub dir_minimum_dxdy {
210 0     0 1 0 my ($self) = @_;
211 0 0       0 return @{$dir_minimum_dxdy{$self->{'tree_type'}} || [1,0]};
  0         0  
212             }
213             }
214             {
215             my %dir_maximum_dxdy
216             = (SB => [1,-1],
217             # CW => [0,0],
218              
219             Bird => [1,-1],
220             # Drib => [0,0],
221              
222             HCS => [2,-1],
223             # AYT => [0,0],
224             # L => [0,0], # at 2^k-1 dX=k+1,dY=-1 so approach Dir=4
225             );
226             sub dir_maximum_dxdy {
227 0     0 1 0 my ($self) = @_;
228 0 0       0 return @{$dir_maximum_dxdy{$self->{'tree_type'}} || [0,0]};
  0         0  
229             }
230             }
231              
232             {
233             my %turn_any_straight = (# SB => 0,
234             # CW => 0,
235             Bird => 1, # straight at N=7 and N=8
236             # Drib => 0,
237             AYT => 1, # straight at N=7
238             # HCS => 0,
239             # L => 0,
240             );
241             sub turn_any_straight {
242 0     0 1 0 my ($self) = @_;
243 0         0 return $turn_any_straight{$self->{'tree_type'}};
244             }
245             }
246              
247              
248             #------------------------------------------------------------------------------
249              
250             my %attributes = (CW => [ n_start => 1, ],
251             SB => [ n_start => 1, reverse_bits => 1 ],
252             Drib => [ n_start => 1, alternating => 1 ],
253             Bird => [ n_start => 1, alternating => 1, reverse_bits => 1 ],
254             AYT => [ n_start => 1, sep1s => 1 ],
255             HCS => [ n_start => 1, sep1s => 1, reverse_bits => 1 ],
256             L => [ n_start => 0 ],
257             );
258              
259             sub new {
260 33     33 1 8660 my $self = shift->SUPER::new(@_);
261              
262 33   100     159 my $tree_type = ($self->{'tree_type'} ||= 'SB');
263 33   33     118 my $attributes = $attributes{$tree_type}
264             || croak "Unrecognised tree type: ",$tree_type;
265 33         1161 %$self = (%$self, @$attributes);
266              
267 33         86 return $self;
268             }
269              
270             sub n_to_xy {
271 2877     2877 1 44648 my ($self, $n) = @_;
272             ### RationalsTree n_to_xy(): "$n"
273              
274 2877 50       5730 if ($n < $self->{'n_start'}) { return; }
  0         0  
275 2877 50       5595 if (is_infinite($n)) { return ($n,$n); }
  0         0  
276              
277             # what to do for fractional $n?
278             {
279 2877         5814 my $int = int($n);
  2877         3965  
280 2877 50       5042 if ($n != $int) {
281             ### frac ...
282 0         0 my $frac = $n - $int; # inherit possible BigFloat/BigRat
283 0         0 my ($x1,$y1) = $self->n_to_xy($int);
284 0         0 my ($x2,$y2) = $self->n_to_xy($int+1);
285 0         0 my $dx = $x2-$x1;
286 0         0 my $dy = $y2-$y1;
287             ### x1,y1: "$x1, $y1"
288             ### x2,y2: "$x2, $y2"
289             ### dx,dy: "$dx, $dy"
290             ### result: ($frac*$dx + $x1).', '.($frac*$dy + $y1)
291 0         0 return ($frac*$dx + $x1, $frac*$dy + $y1);
292             }
293 2877         4138 $n = $int;
294             }
295              
296 2877         4037 my $zero = ($n * 0); # inherit bignum 0
297 2877         4374 my $one = $zero + 1; # inherit bignum 1
298              
299 2877 100       5652 if ($self->{'n_start'} == 0) {
300             # L tree adjust;
301 15         23 $n += 2;
302             }
303              
304 2877         5386 my @nbits = bit_split_lowtohigh($n);
305 2877         4287 pop @nbits;
306             ### lowtohigh sans high: @nbits
307              
308 2877 100       5892 if (! $self->{'reverse_bits'}) {
309 451         627 @nbits = reverse @nbits;
310             ### reverse to: @nbits
311             }
312              
313 2877         3913 my $x = $one;
314 2877         3806 my $y = $one;
315              
316 2877 100       6305 if ($self->{'sep1s'}) {
    100          
    100          
317 174         281 foreach my $nbit (@nbits) {
318 930         1348 $x += $y;
319 930 100       14597 if ($nbit) {
320 336         614 ($x,$y) = ($y,$x);
321             }
322             }
323              
324             } elsif ($self->{'alternating'}) {
325 294         502 foreach my $nbit (@nbits) {
326 1164         1757 ($x,$y) = ($y,$x);
327 1164 100       1791 if ($nbit) {
328 582         849 $x += $y; # (x,y) -> (x+y,x), including swap
329             } else {
330 582         849 $y += $x; # (x,y) -> (y,x+y), including swap
331             }
332             }
333              
334             } elsif ($self->{'tree_type'} eq 'L') {
335 15         22 my $sub = 2;
336 15         25 foreach my $nbit (@nbits) {
337 38 100       57 if ($nbit) {
338 17         24 $y += $x; # (x,y) -> (x,x+y)
339 17         27 $sub = 0;
340             } else {
341 21         31 $x += $y; # (x,y) -> (x+y,y)
342             }
343             }
344 15         23 $x -= $sub; # -2 at N=00...000 all zero bits
345              
346             } else {
347             ### nbits apply CW: @nbits
348 2394         3926 foreach my $nbit (@nbits) { # high to low
349 20307 100       54518 if ($nbit) {
350 10400         14626 $x += $y; # (x,y) -> (x+y,y)
351             } else {
352 9907         13694 $y += $x; # (x,y) -> (x,x+y)
353             }
354             }
355             }
356             ### result: "$x, $y"
357 2877         7756 return ($x,$y);
358             }
359              
360             sub xy_is_visited {
361 0     0 1 0 my ($self, $x, $y) = @_;
362 0         0 $x = round_nearest ($x);
363 0         0 $y = round_nearest ($y);
364 0 0 0     0 if ($self->{'tree_type'} eq 'L' && $x == 0 && $y == 1) {
      0        
365 0         0 return 1;
366             }
367 0 0 0     0 if ($x < 1
      0        
368             || $y < 1
369             || ! _coprime($x,$y)) {
370 0         0 return 0;
371             }
372 0         0 return 1;
373             }
374              
375             sub xy_to_n {
376 3905     3905 1 519060 my ($self, $x, $y) = @_;
377 3905         8216 $x = round_nearest ($x);
378 3905         7834 $y = round_nearest ($y);
379             ### RationalsTree xy_to_n(): "$x,$y $self->{'tree_type'}"
380              
381 3905 100 100     12185 if ($x < $self->{'n_start'} || $y < 1) {
382 720         1246 return undef;
383             }
384 3185 50       107444 if (is_infinite($x)) {
385 0         0 return $x;
386             }
387 3185 50       186532 if (is_infinite($y)) {
388 0         0 return $y;
389             }
390              
391 3185 100       184528 my @quotients = _xy_to_quotients($x,$y)
392             or return undef; # $x,$y have a common factor
393             ### @quotients
394              
395 2270         3466 my @nbits;
396 2270 100       4863 if ($self->{'sep1s'}) {
397 467         755 $quotients[0]++; # the integer part, making it 1 or more
398 467         4488 foreach my $q (@quotients) {
399 1317         24599 push @nbits, (0) x ($q-1), 1; # runs of "000..0001"
400             }
401 467         21570 pop @nbits; # no high 1-bit separator
402              
403             } else {
404 1803 100       3547 if ($quotients[0] < 0) { # X=0,Y=1 in tree_type="L"
405 1         85 return $self->{'n_start'};
406             }
407              
408 1802         64670 my $bit = 1;
409 1802         3123 foreach my $q (@quotients) {
410 5060         8864 push @nbits, ($bit) x $q;
411 5060         25256 $bit ^= 1; # alternate runs of "00000" or "11111"
412             }
413             ### nbits in quotient order: @nbits
414              
415 1802 100       3683 if ($self->{'alternating'}) {
416             # Flip every second bit, starting from the second lowest.
417 882         2117 for (my $i = 1; $i <= $#nbits; $i += 2) {
418 7342         13128 $nbits[$i] ^= 1;
419             }
420             }
421              
422 1802 100       3776 if ($self->{'tree_type'} eq 'L') {
423             # Flip all bits.
424 14         21 my $anyones = 0;
425 14         23 foreach my $nbit (@nbits) {
426 31         42 $nbit ^= 1; # mutate array
427 31   100     72 $anyones ||= $nbit;
428             }
429 14 100       30 unless ($anyones) {
430 3         5 push @nbits, 0,0;
431             }
432             }
433             }
434              
435 2269 100       4317 if ($self->{'reverse_bits'}) {
436 939         1547 @nbits = reverse @nbits;
437             }
438 2269         3368 push @nbits, 1; # high 1-bit
439              
440             ### @nbits
441 2269         5913 my $n = digit_join_lowtohigh (\@nbits, 2,
442             $x*0*$y); # inherit bignum 0
443 2269 100       6369 if ($self->{'tree_type'} eq 'L') {
444 14         32 return $n-2;
445             } else {
446 2255         7074 return $n;
447             }
448             }
449              
450             # Return a list of the quotients from Euclid's greatest common divisor
451             # algorithm on X,Y. This is also the terms of the continued fraction
452             # expansion of rational X/Y.
453             #
454             # The last term, the last in the list, is decremented since this is what the
455             # code above requires. This term is the top-most quotient in for example
456             # gcd(7,1) is 7=7*1+0 with q=7 returned as 6.
457             #
458             # If $x,$y have a common factor then the return is an empty list.
459             # If $x,$y have no common factor then the returned list is always one or
460             # more quotients.
461             #
462             sub _xy_to_quotients {
463 3186     3186   5684 my ($x,$y) = @_;
464 3186         4313 my @ret;
465 3186         4494 for (;;) {
466 8293         15839 my ($q, $r) = _divrem($x,$y);
467 8293         13881 push @ret, $q;
468 8293 100       14816 last unless $r;
469 5107         6866 $x = $y;
470 5107         7146 $y = $r;
471             }
472              
473 3186 100       6204 if ($y > 1) {
474             ### found Y>1 common factor, no N at this X,Y ...
475 915         2552 return;
476             }
477 2271         4618 $ret[-1]--;
478 2271         30458 return @ret;
479             }
480              
481              
482             # not exact
483             sub rect_to_n_range {
484 691     691 1 29583 my ($self, $x1,$y1, $x2,$y2) = @_;
485             ### rect_to_n_range()
486              
487 691         2286 $x1 = round_nearest ($x1);
488 691         1633 $y1 = round_nearest ($y1);
489 691         1760 $x2 = round_nearest ($x2);
490 691         1739 $y2 = round_nearest ($y2);
491              
492 691 100       1897 ($x1,$x2) = ($x2,$x1) if $x1 > $x2;
493 691 100       16554 ($y1,$y2) = ($y2,$y1) if $y1 > $y2;
494             ### $x2
495             ### $y2
496              
497 691 100 66     15119 if ($x2 < 1 || $y2 < 1) {
498             ### no values, rect below first quadrant
499 1 50       6 if ($self->{'n_start'}) {
500 0         0 return (1,0);
501             } else {
502 1         5 return (0,0);
503             }
504             }
505              
506 690         103370 my $zero = ($x1 * 0 * $y1 * $x2 * $y2); # inherit bignum
507             ### $zero
508              
509 690 100       195553 if ($x1 < 1) { $x1 = 1; }
  194         317  
510 690 100       52962 if ($y1 < 1) { $y1 = 1; }
  194         257  
511              
512             # # big x2, small y1
513             # # big y2, small x1
514             # my $level = _bingcd_max ($y2,$x1);
515             # ### $level
516             # {
517             # my $l2 = _bingcd_max ($x2,$y1);
518             # ### $l2
519             # if ($l2 > $level) { $level = $l2; }
520             # }
521              
522 690         50924 my $level = max($x1,$x2,$y1,$y2);
523              
524             return ($self->{'n_start'},
525 690         2447 $self->{'n_start'} + (2+$zero) ** ($level + 3));
526             }
527              
528             sub _bingcd_max {
529 0     0   0 my ($x,$y) = @_;
530             ### _bingcd_max(): "$x,$y"
531              
532 0 0       0 if ($x < $y) { ($x,$y) = ($y,$x) }
  0         0  
533              
534             ### div: int($x/$y)
535             ### bingcd: int($x/$y) + $y
536              
537 0         0 return int($x/$y) + $y + 1;
538             }
539              
540             # ### fib: _fib_log($y)
541             # # ENHANCE-ME: log base PHI, or something close for BigInt
542             # # 2*log2() means log base sqrt(2)=1.4 instead of PHI=1.6
543             # #
544             # # use constant 1.02; # for leading underscore
545             # # use constant _PHI => (1 + sqrt(5)) / 2;
546             # #
547             # sub _fib_log {
548             # my ($x) = @_;
549             # ### _fib_log(): $x
550             # my $f0 = ($x * 0);
551             # my $f1 = $f0 + 1;
552             # my $count = 0;
553             # while ($x > $f0) {
554             # $count++;
555             # ($f0,$f1) = ($f1,$f0+$f1);
556             # }
557             # return $count;
558             # }
559              
560             #------------------------------------------------------------------------------
561 3     3   36 use constant tree_num_roots => 1;
  3         6  
  3         1531  
562              
563             # N=1 basis children 2N,2N+1
564             # N=S basis 2(N-(S-1))+(S-1)
565             # = 2N - 2(S-1) + (S-1)
566             # = 2N - (S-1)
567             sub tree_n_children {
568 14     14 1 646 my ($self, $n) = @_;
569 14         29 my $n_start = $self->{'n_start'};
570 14 50       29 if ($n >= $n_start) {
571 14         22 $n = 2*$n - $n_start;
572 14         48 return ($n+1, $n+2);
573             } else {
574 0         0 return;
575             }
576             }
577             sub tree_n_num_children {
578 0     0 1 0 my ($self, $n) = @_;
579 0 0       0 return ($n >= $self->{'n_start'} ? 2 : undef);
580             }
581             sub tree_n_parent {
582 13     13 1 636 my ($self, $n) = @_;
583 13         28 $n = $n - $self->{'n_start'}; # N=0 basis, and warn if $n==undef
584 13 100       27 if ($n > 0) {
585 12         32 return int(($n-1)/2) + $self->{'n_start'};
586             } else {
587 1         4 return undef;
588             }
589             }
590             sub tree_n_to_depth {
591 33     33 1 2560 my ($self, $n) = @_;
592             ### RationalsTree tree_n_to_depth(): $n
593 33         57 $n = $n - $self->{'n_start'}; # N=0 basis, and warn if $n==undef
594 33 50       81 unless ($n >= 0) {
595 0         0 return undef;
596             }
597 33         88 my ($pow, $exp) = round_down_pow ($n+1, 2);
598 33         62 return $exp;
599             }
600              
601             sub tree_depth_to_n {
602 11     11 1 999 my ($self, $depth) = @_;
603             return ($depth >= 0
604 11 50       51 ? 2**$depth + $self->{'n_start'}-1
605             : undef);
606             }
607             # (2^(d+1)+s-1)-1 = 2^(d+1)+s-2
608             sub tree_depth_to_n_end {
609 11     11 1 23 my ($self, $depth) = @_;
610             return ($depth >= 0
611 11 50       41 ? 2**($depth+1) + $self->{'n_start'}-2
612             : undef);
613             }
614             sub tree_depth_to_n_range {
615 0     0 1   my ($self, $depth) = @_;
616 0 0         if ($depth >= 0) {
617 0           my $pow = 2**$depth;
618 0           return ($pow + $self->{'n_start'}-1, 2*$pow + $self->{'n_start'}-2);
619             }
620 0           return; # no such $depth
621             }
622             sub tree_depth_to_width {
623 0     0 1   my ($self, $depth) = @_;
624 0 0         return ($depth >= 0
625             ? 2**$depth
626             : undef);
627             }
628              
629             1;
630             __END__