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             # A057114 - permutation SB X -> X+1
39             # A057115 - permutation SB X -> X-1
40             #
41              
42             # high-to-low low-to-high
43             # (X+Y)/Y Y/(X+Y) HCS AYT
44             # X/(X+Y) (X+Y)/Y CW SB \ alt bit flips
45             # Y/(X+Y) (X+Y)/X Drib Bird /
46             #
47             # 9 10 12 10
48             # 8 11 8 14
49             # 12 13 9 13
50             # 14 11
51             # 15 15
52             #
53             # Stern-Brocot Calkin-Wilf
54              
55              
56             #------------------------------------------------------------------------------
57             # HCS turn left when even number of 1-bits in N+1
58             # turn right when odd number of 1-bits in N+1
59             #
60             # 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
61             # 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
62             # PlanePathTurn planepath=RationalsTree,tree_type=HCS, turn_type=Left
63             #
64             # 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
65             # 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
66             # PlanePathTurn planepath=RationalsTree,tree_type=HCS, turn_type=Right
67             #
68             # 10 | 768 50 58 896
69             # 9 | 384 49 52 60 57 448 640
70             # 8 | 192 27 31 224 320
71             # 7 | 96 25 26 30 29 112 160 41 42
72             # 6 | 48 56 80
73             # 5 | 24 13 15 28 40 21 23 44
74             # 4 | 12 14 20 22 36
75             # 3 | 6 7 10 11 18 19 34
76             # 2 | 3 5 9 17 33
77             # 1 | 1 2 4 8 16 32 64 128 256 512
78             # Y=0 |
79             # +-----------------------------------------------------
80             # X=0 1 2 3 4 5 6 7 8 9 10
81             #
82             # 1/1
83             # /------------- -------------\
84             # 2/1 1/2 2,3 L,R
85             # /---- ----\ /---- ----\
86             # 3/1 3/2 1/3 2/3 4,5,6,7 L,L,R,R
87             # / \ / \ / \ / \ 8 12
88             # 4/1 5/2 4/3 5/3 1/4 2/5 3/4 3/5 L,L,R,L, R,R,L,R
89             # / \ / \ / \ / \ / \ / \ / \ / \
90             # 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
91             #
92             # *
93             # / \ U=0 = X+Y, Y shear
94             # / * D=1 = Y, X+Y shear+transpose
95             # / \a = 0.1^k.1
96             # N
97             # \ /b = 1.0^k.0
98             # \ *
99             # \ / \c = 1.0^k.1 c=even bits, left
100             # *
101             #
102             # F[-1]=1 F[0]=0 F[1]=1 F[2]=1 F[3]=2 F[4]=3 F[5]=5 ...
103             # 1^k is F[k-1]*X+F[k]*Y, F[k]*X+F[k+1]*Y
104             # X , Y 0
105             # Y, X+ Y 1
106             # X+ Y, X+2Y 2
107             # X+2Y, 2X+3Y 3
108             # 2X+3Y, 3X+5Y 4
109             #
110             # then aX = F[k]*X+F[k+1]*Y + F[k+1]*X+F[k+2]*Y
111             # = (F[k]+F[k+1])*X + (F[k+1]+F[k+2])*Y
112             # = F[k+2]*X + F[k+3]*Y
113             # aY = F[k+1]*X + F[k+2]*Y near X=phi*Y big
114             #
115             # 0^k is X+k*Y, Y
116             # so bX = Y
117             # bY = X+k*Y + Y = X+(k+1)*Y near Y axis
118             #
119             # c1X = Y
120             # c1Y = X+Y
121             # c2X = Y + k*(X+Y) = k*X + (k+1)*Y
122             # c2Y = X+Y
123             # cX = X+Y
124             # cY = k*X + (k+1)*Y + X+Y = (k+1)X + (k+2)Y near X=Y
125              
126             #
127             # *
128             # / \ /a = 0.1^k.0
129             # / *
130             # / \b = 0.1^k.1
131             # N
132             # \ /c = 1.0^k.1 c=even bits, left
133             # \ *
134             # \ /
135             # *
136             #------------------------------------------------------------------------------
137              
138             package Math::PlanePath::RationalsTree;
139 3     3   3739 use 5.004;
  3         12  
140 3     3   16 use strict;
  3         7  
  3         95  
141 3     3   21 use Carp 'croak';
  3         7  
  3         238  
142             #use List::Util 'max';
143             *max = \&Math::PlanePath::_max;
144              
145 3     3   18 use vars '$VERSION', '@ISA';
  3         12  
  3         205  
146             $VERSION = 129;
147 3     3   867 use Math::PlanePath;
  3         7  
  3         128  
148             @ISA = ('Math::PlanePath');
149              
150             use Math::PlanePath::Base::Generic
151 3         163 'is_infinite',
152 3     3   20 'round_nearest';
  3         7  
153             use Math::PlanePath::Base::Digits
154 3         214 'round_down_pow',
155             'bit_split_lowtohigh',
156 3     3   651 'digit_join_lowtohigh';
  3         8  
157             *_divrem = \&Math::PlanePath::_divrem;
158              
159 3     3   1368 use Math::PlanePath::CoprimeColumns;
  3         8  
  3         261  
160             *_coprime = \&Math::PlanePath::CoprimeColumns::_coprime;
161              
162             # uncomment this to run the ### lines
163             #use Smart::Comments;
164              
165              
166 3         218 use constant parameter_info_array =>
167             [ { name => 'tree_type',
168             share_key => 'tree_type_rationalstree',
169             display => 'Tree Type',
170             type => 'enum',
171             default => 'SB',
172             choices => ['SB','CW','AYT','HCS','Bird','Drib','L'],
173             choices_display => ['SB','CW','AYT','HCS','Bird','Drib','L'],
174             },
175 3     3   25 ];
  3         7  
176              
177 3     3   18 use constant class_x_negative => 0;
  3         7  
  3         188  
178 3     3   17 use constant class_y_negative => 0;
  3         19  
  3         311  
179             sub x_minimum {
180 0     0 1 0 my ($self) = @_;
181 0 0       0 return ($self->{'tree_type'} eq 'L' ? 0 : 1);
182             }
183 3     3   27 use constant y_minimum => 1;
  3         17  
  3         192  
184 3     3   20 use constant gcdxy_maximum => 1; # no common factor
  3         6  
  3         162  
185 3     3   20 use constant tree_num_children_list => (2); # complete binary tree
  3         6  
  3         174  
186 3     3   18 use constant tree_n_to_subheight => undef; # complete tree, all infinity
  3         6  
  3         4972  
187              
188             {
189             my %absdy_minimum = (# SB => 0,
190             CW => 1,
191             # AYT => 0,
192             # Bird => 0,
193             # Drib => 0,
194             L => 1);
195             sub absdy_minimum {
196 0     0 1 0 my ($self) = @_;
197 0   0     0 return $absdy_minimum{$self->{'tree_type'}} || 0;
198             }
199             }
200              
201             {
202             # Drib apparent minimum dX=k dY=2*k+1 approaches dX=1,dY=2
203             my %dir_minimum_dxdy = (CW => [0,1],
204             Drib => [1,2],
205             L => [1,1], # at N=0 dX=1,dY=1
206             );
207             sub dir_minimum_dxdy {
208 0     0 1 0 my ($self) = @_;
209 0 0       0 return @{$dir_minimum_dxdy{$self->{'tree_type'}} || [1,0]};
  0         0  
210             }
211             }
212             {
213             my %dir_maximum_dxdy
214             = (SB => [1,-1],
215             # CW => [0,0],
216              
217             Bird => [1,-1],
218             # Drib => [0,0],
219              
220             HCS => [2,-1],
221             # AYT => [0,0],
222             # L => [0,0], # at 2^k-1 dX=k+1,dY=-1 so approach Dir=4
223             );
224             sub dir_maximum_dxdy {
225 0     0 1 0 my ($self) = @_;
226 0 0       0 return @{$dir_maximum_dxdy{$self->{'tree_type'}} || [0,0]};
  0         0  
227             }
228             }
229              
230             {
231             my %turn_any_straight = (# SB => 0,
232             # CW => 0,
233             Bird => 1, # straight at N=7 and N=8
234             # Drib => 0,
235             AYT => 1, # straight at N=7
236             # HCS => 0,
237             # L => 0,
238             );
239             sub turn_any_straight {
240 0     0 1 0 my ($self) = @_;
241 0         0 return $turn_any_straight{$self->{'tree_type'}};
242             }
243             }
244              
245              
246             #------------------------------------------------------------------------------
247              
248             my %attributes = (CW => [ n_start => 1, ],
249             SB => [ n_start => 1, reverse_bits => 1 ],
250             Drib => [ n_start => 1, alternating => 1 ],
251             Bird => [ n_start => 1, alternating => 1, reverse_bits => 1 ],
252             AYT => [ n_start => 1, sep1s => 1 ],
253             HCS => [ n_start => 1, sep1s => 1, reverse_bits => 1 ],
254             L => [ n_start => 0 ],
255             );
256              
257             sub new {
258 33     33 1 8650 my $self = shift->SUPER::new(@_);
259              
260 33   100     158 my $tree_type = ($self->{'tree_type'} ||= 'SB');
261 33   33     125 my $attributes = $attributes{$tree_type}
262             || croak "Unrecognised tree type: ",$tree_type;
263 33         1476 %$self = (%$self, @$attributes);
264              
265 33         108 return $self;
266             }
267              
268             sub n_to_xy {
269 2877     2877 1 45049 my ($self, $n) = @_;
270             ### RationalsTree n_to_xy(): "$n"
271              
272 2877 50       5576 if ($n < $self->{'n_start'}) { return; }
  0         0  
273 2877 50       5734 if (is_infinite($n)) { return ($n,$n); }
  0         0  
274              
275             # what to do for fractional $n?
276             {
277 2877         6612 my $int = int($n);
  2877         3985  
278 2877 50       5357 if ($n != $int) {
279             ### frac ...
280 0         0 my $frac = $n - $int; # inherit possible BigFloat/BigRat
281 0         0 my ($x1,$y1) = $self->n_to_xy($int);
282 0         0 my ($x2,$y2) = $self->n_to_xy($int+1);
283 0         0 my $dx = $x2-$x1;
284 0         0 my $dy = $y2-$y1;
285             ### x1,y1: "$x1, $y1"
286             ### x2,y2: "$x2, $y2"
287             ### dx,dy: "$dx, $dy"
288             ### result: ($frac*$dx + $x1).', '.($frac*$dy + $y1)
289 0         0 return ($frac*$dx + $x1, $frac*$dy + $y1);
290             }
291 2877         4265 $n = $int;
292             }
293              
294 2877         3934 my $zero = ($n * 0); # inherit bignum 0
295 2877         4640 my $one = $zero + 1; # inherit bignum 1
296              
297 2877 100       5608 if ($self->{'n_start'} == 0) {
298             # L tree adjust;
299 15         25 $n += 2;
300             }
301              
302 2877         5523 my @nbits = bit_split_lowtohigh($n);
303 2877         4536 pop @nbits;
304             ### lowtohigh sans high: @nbits
305              
306 2877 100       6016 if (! $self->{'reverse_bits'}) {
307 451         679 @nbits = reverse @nbits;
308             ### reverse to: @nbits
309             }
310              
311 2877         3949 my $x = $one;
312 2877         4131 my $y = $one;
313              
314 2877 100       6820 if ($self->{'sep1s'}) {
    100          
    100          
315 174         272 foreach my $nbit (@nbits) {
316 930         1344 $x += $y;
317 930 100       14585 if ($nbit) {
318 336         600 ($x,$y) = ($y,$x);
319             }
320             }
321              
322             } elsif ($self->{'alternating'}) {
323 294         484 foreach my $nbit (@nbits) {
324 1164         1818 ($x,$y) = ($y,$x);
325 1164 100       1735 if ($nbit) {
326 582         866 $x += $y; # (x,y) -> (x+y,x), including swap
327             } else {
328 582         832 $y += $x; # (x,y) -> (y,x+y), including swap
329             }
330             }
331              
332             } elsif ($self->{'tree_type'} eq 'L') {
333 15         25 my $sub = 2;
334 15         25 foreach my $nbit (@nbits) {
335 38 100       63 if ($nbit) {
336 17         19 $y += $x; # (x,y) -> (x,x+y)
337 17         30 $sub = 0;
338             } else {
339 21         30 $x += $y; # (x,y) -> (x+y,y)
340             }
341             }
342 15         22 $x -= $sub; # -2 at N=00...000 all zero bits
343              
344             } else {
345             ### nbits apply CW: @nbits
346 2394         3698 foreach my $nbit (@nbits) { # high to low
347 20307 100       54781 if ($nbit) {
348 10400         14573 $x += $y; # (x,y) -> (x+y,y)
349             } else {
350 9907         13793 $y += $x; # (x,y) -> (x,x+y)
351             }
352             }
353             }
354             ### result: "$x, $y"
355 2877         8074 return ($x,$y);
356             }
357              
358             sub xy_is_visited {
359 0     0 1 0 my ($self, $x, $y) = @_;
360 0         0 $x = round_nearest ($x);
361 0         0 $y = round_nearest ($y);
362 0 0 0     0 if ($self->{'tree_type'} eq 'L' && $x == 0 && $y == 1) {
      0        
363 0         0 return 1;
364             }
365 0 0 0     0 if ($x < 1
      0        
366             || $y < 1
367             || ! _coprime($x,$y)) {
368 0         0 return 0;
369             }
370 0         0 return 1;
371             }
372              
373             sub xy_to_n {
374 3905     3905 1 514069 my ($self, $x, $y) = @_;
375 3905         8456 $x = round_nearest ($x);
376 3905         8011 $y = round_nearest ($y);
377             ### RationalsTree xy_to_n(): "$x,$y $self->{'tree_type'}"
378              
379 3905 100 100     12072 if ($x < $self->{'n_start'} || $y < 1) {
380 720         1287 return undef;
381             }
382 3185 50       107766 if (is_infinite($x)) {
383 0         0 return $x;
384             }
385 3185 50       186713 if (is_infinite($y)) {
386 0         0 return $y;
387             }
388              
389 3185 100       186772 my @quotients = _xy_to_quotients($x,$y)
390             or return undef; # $x,$y have a common factor
391             ### @quotients
392              
393 2270         3273 my @nbits;
394 2270 100       4847 if ($self->{'sep1s'}) {
395 467         766 $quotients[0]++; # the integer part, making it 1 or more
396 467         4422 foreach my $q (@quotients) {
397 1317         24322 push @nbits, (0) x ($q-1), 1; # runs of "000..0001"
398             }
399 467         21709 pop @nbits; # no high 1-bit separator
400              
401             } else {
402 1803 100       3468 if ($quotients[0] < 0) { # X=0,Y=1 in tree_type="L"
403 1         90 return $self->{'n_start'};
404             }
405              
406 1802         65225 my $bit = 1;
407 1802         3260 foreach my $q (@quotients) {
408 5060         8724 push @nbits, ($bit) x $q;
409 5060         25143 $bit ^= 1; # alternate runs of "00000" or "11111"
410             }
411             ### nbits in quotient order: @nbits
412              
413 1802 100       3854 if ($self->{'alternating'}) {
414             # Flip every second bit, starting from the second lowest.
415 882         1986 for (my $i = 1; $i <= $#nbits; $i += 2) {
416 7342         13022 $nbits[$i] ^= 1;
417             }
418             }
419              
420 1802 100       4027 if ($self->{'tree_type'} eq 'L') {
421             # Flip all bits.
422 14         18 my $anyones = 0;
423 14         24 foreach my $nbit (@nbits) {
424 31         41 $nbit ^= 1; # mutate array
425 31   100     71 $anyones ||= $nbit;
426             }
427 14 100       29 unless ($anyones) {
428 3         6 push @nbits, 0,0;
429             }
430             }
431             }
432              
433 2269 100       4669 if ($self->{'reverse_bits'}) {
434 939         1413 @nbits = reverse @nbits;
435             }
436 2269         3407 push @nbits, 1; # high 1-bit
437              
438             ### @nbits
439 2269         5928 my $n = digit_join_lowtohigh (\@nbits, 2,
440             $x*0*$y); # inherit bignum 0
441 2269 100       6025 if ($self->{'tree_type'} eq 'L') {
442 14         32 return $n-2;
443             } else {
444 2255         6926 return $n;
445             }
446             }
447              
448             # Return a list of the quotients from Euclid's greatest common divisor
449             # algorithm on X,Y. This is also the terms of the continued fraction
450             # expansion of rational X/Y.
451             #
452             # The last term, the last in the list, is decremented since this is what the
453             # code above requires. This term is the top-most quotient in for example
454             # gcd(7,1) is 7=7*1+0 with q=7 returned as 6.
455             #
456             # If $x,$y have a common factor then the return is an empty list.
457             # If $x,$y have no common factor then the returned list is always one or
458             # more quotients.
459             #
460             sub _xy_to_quotients {
461 3186     3186   5829 my ($x,$y) = @_;
462 3186         4515 my @ret;
463 3186         4549 for (;;) {
464 8293         15879 my ($q, $r) = _divrem($x,$y);
465 8293         13499 push @ret, $q;
466 8293 100       14963 last unless $r;
467 5107         6855 $x = $y;
468 5107         7056 $y = $r;
469             }
470              
471 3186 100       6004 if ($y > 1) {
472             ### found Y>1 common factor, no N at this X,Y ...
473 915         2573 return;
474             }
475 2271         5047 $ret[-1]--;
476 2271         29857 return @ret;
477             }
478              
479              
480             # not exact
481             sub rect_to_n_range {
482 691     691 1 28507 my ($self, $x1,$y1, $x2,$y2) = @_;
483             ### rect_to_n_range()
484              
485 691         2236 $x1 = round_nearest ($x1);
486 691         1706 $y1 = round_nearest ($y1);
487 691         1719 $x2 = round_nearest ($x2);
488 691         1730 $y2 = round_nearest ($y2);
489              
490 691 100       1870 ($x1,$x2) = ($x2,$x1) if $x1 > $x2;
491 691 100       15943 ($y1,$y2) = ($y2,$y1) if $y1 > $y2;
492             ### $x2
493             ### $y2
494              
495 691 100 66     14682 if ($x2 < 1 || $y2 < 1) {
496             ### no values, rect below first quadrant
497 1 50       5 if ($self->{'n_start'}) {
498 0         0 return (1,0);
499             } else {
500 1         5 return (0,0);
501             }
502             }
503              
504 690         102870 my $zero = ($x1 * 0 * $y1 * $x2 * $y2); # inherit bignum
505             ### $zero
506              
507 690 100       198002 if ($x1 < 1) { $x1 = 1; }
  194         268  
508 690 100       52904 if ($y1 < 1) { $y1 = 1; }
  194         277  
509              
510             # # big x2, small y1
511             # # big y2, small x1
512             # my $level = _bingcd_max ($y2,$x1);
513             # ### $level
514             # {
515             # my $l2 = _bingcd_max ($x2,$y1);
516             # ### $l2
517             # if ($l2 > $level) { $level = $l2; }
518             # }
519              
520 690         50633 my $level = max($x1,$x2,$y1,$y2);
521              
522             return ($self->{'n_start'},
523 690         2411 $self->{'n_start'} + (2+$zero) ** ($level + 3));
524             }
525              
526             sub _bingcd_max {
527 0     0   0 my ($x,$y) = @_;
528             ### _bingcd_max(): "$x,$y"
529              
530 0 0       0 if ($x < $y) { ($x,$y) = ($y,$x) }
  0         0  
531              
532             ### div: int($x/$y)
533             ### bingcd: int($x/$y) + $y
534              
535 0         0 return int($x/$y) + $y + 1;
536             }
537              
538             # ### fib: _fib_log($y)
539             # # ENHANCE-ME: log base PHI, or something close for BigInt
540             # # 2*log2() means log base sqrt(2)=1.4 instead of PHI=1.6
541             # #
542             # # use constant 1.02; # for leading underscore
543             # # use constant _PHI => (1 + sqrt(5)) / 2;
544             # #
545             # sub _fib_log {
546             # my ($x) = @_;
547             # ### _fib_log(): $x
548             # my $f0 = ($x * 0);
549             # my $f1 = $f0 + 1;
550             # my $count = 0;
551             # while ($x > $f0) {
552             # $count++;
553             # ($f0,$f1) = ($f1,$f0+$f1);
554             # }
555             # return $count;
556             # }
557              
558             #------------------------------------------------------------------------------
559 3     3   27 use constant tree_num_roots => 1;
  3         7  
  3         1708  
560              
561             # N=1 basis children 2N,2N+1
562             # N=S basis 2(N-(S-1))+(S-1)
563             # = 2N - 2(S-1) + (S-1)
564             # = 2N - (S-1)
565             sub tree_n_children {
566 14     14 1 669 my ($self, $n) = @_;
567 14         25 my $n_start = $self->{'n_start'};
568 14 50       31 if ($n >= $n_start) {
569 14         21 $n = 2*$n - $n_start;
570 14         49 return ($n+1, $n+2);
571             } else {
572 0         0 return;
573             }
574             }
575             sub tree_n_num_children {
576 0     0 1 0 my ($self, $n) = @_;
577 0 0       0 return ($n >= $self->{'n_start'} ? 2 : undef);
578             }
579             sub tree_n_parent {
580 13     13 1 636 my ($self, $n) = @_;
581 13         23 $n = $n - $self->{'n_start'}; # N=0 basis, and warn if $n==undef
582 13 100       26 if ($n > 0) {
583 12         40 return int(($n-1)/2) + $self->{'n_start'};
584             } else {
585 1         4 return undef;
586             }
587             }
588             sub tree_n_to_depth {
589 33     33 1 2547 my ($self, $n) = @_;
590             ### RationalsTree tree_n_to_depth(): $n
591 33         64 $n = $n - $self->{'n_start'}; # N=0 basis, and warn if $n==undef
592 33 50       79 unless ($n >= 0) {
593 0         0 return undef;
594             }
595 33         91 my ($pow, $exp) = round_down_pow ($n+1, 2);
596 33         64 return $exp;
597             }
598              
599             sub tree_depth_to_n {
600 11     11 1 1005 my ($self, $depth) = @_;
601             return ($depth >= 0
602 11 50       53 ? 2**$depth + $self->{'n_start'}-1
603             : undef);
604             }
605             # (2^(d+1)+s-1)-1 = 2^(d+1)+s-2
606             sub tree_depth_to_n_end {
607 11     11 1 22 my ($self, $depth) = @_;
608             return ($depth >= 0
609 11 50       90 ? 2**($depth+1) + $self->{'n_start'}-2
610             : undef);
611             }
612             sub tree_depth_to_n_range {
613 0     0 1   my ($self, $depth) = @_;
614 0 0         if ($depth >= 0) {
615 0           my $pow = 2**$depth;
616 0           return ($pow + $self->{'n_start'}-1, 2*$pow + $self->{'n_start'}-2);
617             }
618 0           return; # no such $depth
619             }
620             sub tree_depth_to_width {
621 0     0 1   my ($self, $depth) = @_;
622 0 0         return ($depth >= 0
623             ? 2**$depth
624             : undef);
625             }
626              
627             1;
628             __END__