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 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   3478 use 5.004;
  3         12  
142 3     3   19 use strict;
  3         7  
  3         80  
143 3     3   16 use Carp 'croak';
  3         6  
  3         232  
144             #use List::Util 'max';
145             *max = \&Math::PlanePath::_max;
146              
147 3     3   18 use vars '$VERSION', '@ISA';
  3         6  
  3         189  
148             $VERSION = 127;
149 3     3   879 use Math::PlanePath;
  3         7  
  3         128  
150             @ISA = ('Math::PlanePath');
151              
152             use Math::PlanePath::Base::Generic
153 3         214 'is_infinite',
154 3     3   19 'round_nearest';
  3         7  
155             use Math::PlanePath::Base::Digits
156 3         201 'round_down_pow',
157             'bit_split_lowtohigh',
158 3     3   636 'digit_join_lowtohigh';
  3         7  
159             *_divrem = \&Math::PlanePath::_divrem;
160              
161 3     3   1194 use Math::PlanePath::CoprimeColumns;
  3         9  
  3         235  
162             *_coprime = \&Math::PlanePath::CoprimeColumns::_coprime;
163              
164             # uncomment this to run the ### lines
165             #use Smart::Comments;
166              
167              
168 3         212 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   52 ];
  3         9  
178              
179 3     3   20 use constant class_x_negative => 0;
  3         7  
  3         133  
180 3     3   18 use constant class_y_negative => 0;
  3         6  
  3         284  
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   21 use constant y_minimum => 1;
  3         6  
  3         163  
186 3     3   20 use constant gcdxy_maximum => 1; # no common factor
  3         7  
  3         156  
187 3     3   18 use constant tree_num_children_list => (2); # complete binary tree
  3         6  
  3         158  
188 3     3   19 use constant tree_n_to_subheight => undef; # complete tree, all infinity
  3         7  
  3         4692  
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 9083 my $self = shift->SUPER::new(@_);
261              
262 33   100     178 my $tree_type = ($self->{'tree_type'} ||= 'SB');
263 33   33     129 my $attributes = $attributes{$tree_type}
264             || croak "Unrecognised tree type: ",$tree_type;
265 33         216 %$self = (%$self, @$attributes);
266              
267 33         94 return $self;
268             }
269              
270             sub n_to_xy {
271 2877     2877 1 45821 my ($self, $n) = @_;
272             ### RationalsTree n_to_xy(): "$n"
273              
274 2877 50       5718 if ($n < $self->{'n_start'}) { return; }
  0         0  
275 2877 50       5990 if (is_infinite($n)) { return ($n,$n); }
  0         0  
276              
277             # what to do for fractional $n?
278             {
279 2877         5977 my $int = int($n);
  2877         4000  
280 2877 50       5086 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         5158 $n = $int;
294             }
295              
296 2877         3910 my $zero = ($n * 0); # inherit bignum 0
297 2877         4433 my $one = $zero + 1; # inherit bignum 1
298              
299 2877 100       6091 if ($self->{'n_start'} == 0) {
300             # L tree adjust;
301 15         21 $n += 2;
302             }
303              
304 2877         5818 my @nbits = bit_split_lowtohigh($n);
305 2877         4388 pop @nbits;
306             ### lowtohigh sans high: @nbits
307              
308 2877 100       6095 if (! $self->{'reverse_bits'}) {
309 451         679 @nbits = reverse @nbits;
310             ### reverse to: @nbits
311             }
312              
313 2877         3845 my $x = $one;
314 2877         3641 my $y = $one;
315              
316 2877 100       6529 if ($self->{'sep1s'}) {
    100          
    100          
317 174         284 foreach my $nbit (@nbits) {
318 930         1290 $x += $y;
319 930 100       14600 if ($nbit) {
320 336         636 ($x,$y) = ($y,$x);
321             }
322             }
323              
324             } elsif ($self->{'alternating'}) {
325 294         481 foreach my $nbit (@nbits) {
326 1164         1838 ($x,$y) = ($y,$x);
327 1164 100       1802 if ($nbit) {
328 582         839 $x += $y; # (x,y) -> (x+y,x), including swap
329             } else {
330 582         893 $y += $x; # (x,y) -> (y,x+y), including swap
331             }
332             }
333              
334             } elsif ($self->{'tree_type'} eq 'L') {
335 15         21 my $sub = 2;
336 15         26 foreach my $nbit (@nbits) {
337 38 100       61 if ($nbit) {
338 17         19 $y += $x; # (x,y) -> (x,x+y)
339 17         28 $sub = 0;
340             } else {
341 21         34 $x += $y; # (x,y) -> (x+y,y)
342             }
343             }
344 15         21 $x -= $sub; # -2 at N=00...000 all zero bits
345              
346             } else {
347             ### nbits apply CW: @nbits
348 2394         3703 foreach my $nbit (@nbits) { # high to low
349 20307 100       55335 if ($nbit) {
350 10400         14577 $x += $y; # (x,y) -> (x+y,y)
351             } else {
352 9907         13776 $y += $x; # (x,y) -> (x,x+y)
353             }
354             }
355             }
356             ### result: "$x, $y"
357 2877         7868 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 521033 my ($self, $x, $y) = @_;
377 3905         8074 $x = round_nearest ($x);
378 3905         7924 $y = round_nearest ($y);
379             ### RationalsTree xy_to_n(): "$x,$y $self->{'tree_type'}"
380              
381 3905 100 100     12599 if ($x < $self->{'n_start'} || $y < 1) {
382 720         1309 return undef;
383             }
384 3185 50       108333 if (is_infinite($x)) {
385 0         0 return $x;
386             }
387 3185 50       188943 if (is_infinite($y)) {
388 0         0 return $y;
389             }
390              
391 3185 100       185653 my @quotients = _xy_to_quotients($x,$y)
392             or return undef; # $x,$y have a common factor
393             ### @quotients
394              
395 2270         3338 my @nbits;
396 2270 100       4899 if ($self->{'sep1s'}) {
397 467         778 $quotients[0]++; # the integer part, making it 1 or more
398 467         4589 foreach my $q (@quotients) {
399 1317         24569 push @nbits, (0) x ($q-1), 1; # runs of "000..0001"
400             }
401 467         21945 pop @nbits; # no high 1-bit separator
402              
403             } else {
404 1803 100       3533 if ($quotients[0] < 0) { # X=0,Y=1 in tree_type="L"
405 1         3 return $self->{'n_start'};
406             }
407              
408 1802         65330 my $bit = 1;
409 1802         3251 foreach my $q (@quotients) {
410 5060         9015 push @nbits, ($bit) x $q;
411 5060         25330 $bit ^= 1; # alternate runs of "00000" or "11111"
412             }
413             ### nbits in quotient order: @nbits
414              
415 1802 100       3671 if ($self->{'alternating'}) {
416             # Flip every second bit, starting from the second lowest.
417 882         2090 for (my $i = 1; $i <= $#nbits; $i += 2) {
418 7342         13026 $nbits[$i] ^= 1;
419             }
420             }
421              
422 1802 100       3876 if ($self->{'tree_type'} eq 'L') {
423             # Flip all bits.
424 14         18 my $anyones = 0;
425 14         26 foreach my $nbit (@nbits) {
426 31         37 $nbit ^= 1; # mutate array
427 31   100     74 $anyones ||= $nbit;
428             }
429 14 100       26 unless ($anyones) {
430 3         7 push @nbits, 0,0;
431             }
432             }
433             }
434              
435 2269 100       4551 if ($self->{'reverse_bits'}) {
436 939         1534 @nbits = reverse @nbits;
437             }
438 2269         3366 push @nbits, 1; # high 1-bit
439              
440             ### @nbits
441 2269         5950 my $n = digit_join_lowtohigh (\@nbits, 2,
442             $x*0*$y); # inherit bignum 0
443 2269 100       6152 if ($self->{'tree_type'} eq 'L') {
444 14         31 return $n-2;
445             } else {
446 2255         7044 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   5507 my ($x,$y) = @_;
464 3186         4418 my @ret;
465 3186         4422 for (;;) {
466 8293         16748 my ($q, $r) = _divrem($x,$y);
467 8293         13725 push @ret, $q;
468 8293 100       14745 last unless $r;
469 5107         7346 $x = $y;
470 5107         7253 $y = $r;
471             }
472              
473 3186 100       6382 if ($y > 1) {
474             ### found Y>1 common factor, no N at this X,Y ...
475 915         2476 return;
476             }
477 2271         4637 $ret[-1]--;
478 2271         30697 return @ret;
479             }
480              
481              
482             # not exact
483             sub rect_to_n_range {
484 691     691 1 29515 my ($self, $x1,$y1, $x2,$y2) = @_;
485             ### rect_to_n_range()
486              
487 691         2306 $x1 = round_nearest ($x1);
488 691         1789 $y1 = round_nearest ($y1);
489 691         1606 $x2 = round_nearest ($x2);
490 691         1678 $y2 = round_nearest ($y2);
491              
492 691 100       1790 ($x1,$x2) = ($x2,$x1) if $x1 > $x2;
493 691 100       16408 ($y1,$y2) = ($y2,$y1) if $y1 > $y2;
494             ### $x2
495             ### $y2
496              
497 691 100 66     14978 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         103867 my $zero = ($x1 * 0 * $y1 * $x2 * $y2); # inherit bignum
507             ### $zero
508              
509 690 100       198405 if ($x1 < 1) { $x1 = 1; }
  194         262  
510 690 100       53545 if ($y1 < 1) { $y1 = 1; }
  194         229  
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         51174 my $level = max($x1,$x2,$y1,$y2);
523              
524             return ($self->{'n_start'},
525 690         2371 $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   24 use constant tree_num_roots => 1;
  3         7  
  3         1536  
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 693 my ($self, $n) = @_;
569 14         28 my $n_start = $self->{'n_start'};
570 14 50       28 if ($n >= $n_start) {
571 14         20 $n = 2*$n - $n_start;
572 14         49 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 617 my ($self, $n) = @_;
583 13         24 $n = $n - $self->{'n_start'}; # N=0 basis, and warn if $n==undef
584 13 100       27 if ($n > 0) {
585 12         47 return int(($n-1)/2) + $self->{'n_start'};
586             } else {
587 1         3 return undef;
588             }
589             }
590             sub tree_n_to_depth {
591 33     33 1 2519 my ($self, $n) = @_;
592             ### RationalsTree tree_n_to_depth(): $n
593 33         65 $n = $n - $self->{'n_start'}; # N=0 basis, and warn if $n==undef
594 33 50       83 unless ($n >= 0) {
595 0         0 return undef;
596             }
597 33         96 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 1062 my ($self, $depth) = @_;
603             return ($depth >= 0
604 11 50       53 ? 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 21 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__