File Coverage

blib/lib/Math/PlanePath/FibonacciWordFractal.pm
Criterion Covered Total %
statement 169 216 78.2
branch 78 106 73.5
condition 12 18 66.6
subroutine 11 12 91.6
pod 3 3 100.0
total 273 355 76.9


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             # math-image --path=FibonacciWordFractal --output=numbers_dash
20              
21              
22             package Math::PlanePath::FibonacciWordFractal;
23 1     1   9789 use 5.004;
  1         9  
24 1     1   7 use strict;
  1         2  
  1         24  
25              
26 1     1   5 use vars '$VERSION', '@ISA';
  1         2  
  1         68  
27             $VERSION = 129;
28 1     1   742 use Math::PlanePath;
  1         3  
  1         29  
29 1     1   470 use Math::PlanePath::Base::NSEW;
  1         3  
  1         41  
30             @ISA = ('Math::PlanePath::Base::NSEW',
31             'Math::PlanePath');
32              
33             use Math::PlanePath::Base::Generic
34 1         43 'is_infinite',
35 1     1   6 'round_nearest';
  1         2  
36              
37             # uncomment this to run the ### lines
38             #use Smart::Comments;
39              
40              
41 1     1   5 use constant n_start => 0;
  1         2  
  1         43  
42 1     1   5 use constant class_x_negative => 0;
  1         2  
  1         36  
43 1     1   4 use constant class_y_negative => 0;
  1         2  
  1         1427  
44              
45              
46             #------------------------------------------------------------------------------
47              
48             my @dir4_to_dx = (0,-1,0,1);
49             my @dir4_to_dy = (1,0,-1,0);
50              
51             my $moffset = 0;
52              
53             sub n_to_xy {
54 539     539 1 2047 my ($self, $n) = @_;
55             ### FibonacciWordFractal n_to_xy(): $n
56              
57 539 50       1037 if ($n < 0) { return; }
  0         0  
58 539 50       1040 if (is_infinite($n)) { return ($n, $n); }
  0         0  
59              
60             # my $frac;
61             # {
62             # my $int = int($n);
63             # $frac = $n - $int; # inherit possible BigFloat
64             # $n = $int; # BigFloat int() gives BigInt, use that
65             # }
66             {
67 539         936 my $int = int($n);
  539         789  
68             ### $int
69             ### $n
70 539 100       938 if ($n != $int) {
71 90         169 my ($x1,$y1) = $self->n_to_xy($int);
72 90         182 my ($x2,$y2) = $self->n_to_xy($int+1);
73 90         150 my $frac = $n - $int; # inherit possible BigFloat
74 90         159 my $dx = $x2-$x1;
75 90         125 my $dy = $y2-$y1;
76 90         306 return ($frac*$dx + $x1, $frac*$dy + $y1);
77             }
78 449         633 $n = $int; # BigFloat int() gives BigInt, use that
79             }
80              
81 449         605 my $zero = ($n * 0); # inherit bignum 0
82 449         627 my $one = $zero + 1; # inherit bignum 0
83              
84 449         741 my @f = ($one, 2+$zero);
85 449         700 my @xend = ($zero, $zero, $one); # F3 N=2 X=1,Y=1
86 449         655 my @yend = ($zero, $one, $one);
87 449         654 my $level = 2;
88 449         788 while ($f[-1] < $n) {
89 2865         4077 push @f, $f[-1] + $f[-2];
90              
91 2865         4038 my ($x,$y);
92 2865         4029 my $m = (($level+$moffset) % 6);
93 2865 100       6060 if ($m == 1) {
    100          
    100          
    100          
    100          
    50          
94 341         467 $x = $yend[-2]; # T (transpose)
95 341         435 $y = $xend[-2];
96             } elsif ($m == 2) {
97 712         1002 $x = $yend[-2]; # -90
98 712         1009 $y = - $xend[-2];
99             } elsif ($m == 3) {
100 602         849 $x = $xend[-2]; # T -90
101 602         852 $y = - $yend[-2];
102              
103             } elsif ($m == 4) {
104             ### T ...
105 423         597 $x = $yend[-2]; # T
106 423         581 $y = $xend[-2];
107             } elsif ($m == 5) {
108 406         573 $x = - $yend[-2]; # +90
109 406         567 $y = $xend[-2];
110             } elsif ($m == 0) {
111 381         530 $x = - $xend[-2]; # T +90
112 381         506 $y = $yend[-2];
113             }
114              
115 2865         4131 push @xend, $xend[-1] + $x;
116 2865         3877 push @yend, $yend[-1] + $y;
117 2865         5173 $level++;
118             ### new: ($level%6)." add $x,$y for $xend[-1],$yend[-1] for $f[-1]"
119             }
120              
121 449         623 my $x = $zero;
122 449         620 my $y = $zero;
123 449         594 my $rot = 0;
124 449         616 my $transpose = 0;
125              
126 449         819 while (@xend > 1) {
127             ### at: "$x,$y rot=$rot transpose=$transpose level=$level n=$n consider f=$f[-1]"
128 3763         5265 my $xo = pop @xend;
129 3763         5227 my $yo = pop @yend;
130              
131 3763 100       6271 if ($n >= $f[-1]) {
132 1183         1758 my $m = (($level+$moffset) % 6);
133 1183         1546 $n -= $f[-1];
134             ### offset: "$xo, $yo for levelmod=$m"
135              
136 1183 100       1917 if ($transpose) {
137 412         675 ($xo,$yo) = ($yo,$xo);
138             }
139 1183 100       2040 if ($rot & 2) {
140 475         656 $xo = -$xo;
141 475         671 $yo = -$yo;
142             }
143 1183 100       2051 if ($rot & 1) {
144 475         755 ($xo,$yo) = (-$yo,$xo);
145             }
146             ### apply rot to offset: "$xo, $yo"
147              
148 1183         1600 $x += $xo;
149 1183         1538 $y += $yo;
150 1183 100       2383 if ($m == 1) { # F7 N=13 etc
    100          
    100          
    100          
    100          
151 302         445 $transpose ^= 3; # T
152             } elsif ($m == 2) { # F8 N=21 etc
153             # -90
154 210 100       354 if ($transpose) {
155 50         74 $rot++;
156             } else {
157 160         230 $rot--; # -90
158             }
159             } elsif ($m == 3) { # F3 N=2 etc
160             # T -90
161 300 100       460 if ($transpose) {
162 60         83 $rot++;
163             } else {
164 240         313 $rot--; # -90
165             }
166 300         443 $transpose ^= 3;
167              
168             } elsif ($m == 4) { # F4 N=3 etc
169 126         185 $transpose ^= 3; # T
170             } elsif ($m == 5) { # F5 N=5 etc
171             # +90
172 125 100       204 if ($transpose) {
173 50         63 $rot--;
174             } else {
175 75         110 $rot++; # +90
176             }
177             } else { # ($m == 0) # F6 N=8 etc
178             # T +90
179 120 100       189 if ($transpose) {
180 40         54 $rot--;
181             } else {
182 80         132 $rot++; # +90
183             }
184 120         177 $transpose ^= 3;
185             }
186             }
187 3763         4865 pop @f;
188 3763         7043 $level--;
189             }
190              
191             # mod 6 twist ?
192             # ### final rot: "$rot transpose=$transpose gives ".(($rot^$transpose)&3)
193             # $rot = ($rot ^ $transpose) & 3;
194             # $x = $frac * $dir4_to_dx[$rot] + $x;
195             # $y = $frac * $dir4_to_dy[$rot] + $y;
196              
197             ### final with frac: "$x,$y"
198 449         1023 return ($x,$y);
199             }
200              
201             sub xy_to_n {
202 625     625 1 2892 my ($self, $x, $y) = @_;
203             ### FibonacciWordFractal xy_to_n(): "$x, $y"
204              
205 625         1257 $x = round_nearest($x);
206 625 50       1229 if (is_infinite($x)) {
207 0         0 return $x;
208             }
209              
210 625         1402 $y = round_nearest($y);
211 625 50       1202 if (is_infinite($y)) {
212 0         0 return $y;
213             }
214              
215 625         1105 my $zero = ($x * 0 * $y); # inherit bignum 0
216 625         887 my $one = $zero + 1; # inherit bignum 0
217              
218 625         1048 my @f = ($one, $zero+2);
219 625         937 my @xend = ($zero, $one); # F3 N=2 X=1,Y=1
220 625         919 my @yend = ($one, $one);
221 625         822 my $level = 3;
222              
223 625         834 for (;;) {
224 4067         5561 my ($xo,$yo);
225 4067         5544 my $m = ($level % 6);
226 4067 100       8774 if ($m == 2) {
    100          
    100          
    100          
    100          
    50          
227 453         622 $xo = $yend[-2]; # T
228 453         593 $yo = $xend[-2];
229             } elsif ($m == 3) {
230 1003         1388 $xo = $yend[-2]; # -90
231 1003         1486 $yo = - $xend[-2];
232             } elsif ($m == 4) {
233 780         1062 $xo = $xend[-2]; # T -90
234 780         1080 $yo = - $yend[-2];
235              
236             } elsif ($m == 5) {
237             ### T
238 732         958 $xo = $yend[-2]; # T
239 732         988 $yo = $xend[-2];
240             } elsif ($m == 0) {
241 630         903 $xo = - $yend[-2]; # +90
242 630         847 $yo = $xend[-2];
243             } elsif ($m == 1) {
244 469         676 $xo = - $xend[-2]; # T +90
245 469         630 $yo = $yend[-2];
246             }
247              
248 4067         5367 $xo += $xend[-1];
249 4067         5191 $yo += $yend[-1];
250 4067 100 100     9249 last if ($xo > $x && $yo > $y);
251              
252 3442         5428 push @f, $f[-1] + $f[-2];
253 3442         4631 push @xend, $xo;
254 3442         4811 push @yend, $yo;
255 3442         4698 $level++;
256             ### new: "level=$level $xend[-1],$yend[-1] for N=$f[-1]"
257             }
258              
259 625         951 my $n = 0;
260 625         1087 while ($level >= 2) {
261             ### at: "$x,$y n=$n level=$level consider $xend[-1],$yend[-1] for $f[-1]"
262              
263 3553         5243 my $m = (($level+$moffset) % 6);
264 3553 100       5478 if ($m >= 3) {
265             ### 3,4,5 X ...
266 1818 100       3139 if ($x >= $xend[-1]) {
267 586         779 $n += $f[-1];
268 586         822 $x -= $xend[-1];
269 586         769 $y -= $yend[-1];
270             ### shift to: "$x,$y levelmod ".$m
271              
272 586 100       1150 if ($m == 3) { # F3 N=2 etc
    100          
    50          
273 269         499 ($x,$y) = (-$y,$x); # +90
274             } elsif ($m == 4) { # F4 N=3 etc
275 157         237 $y = -$y; # +90 T
276             } elsif ($m == 5) { # F5 N=5 etc
277 160         267 ($x,$y) = ($y,$x); # T
278             }
279             ### rot to: "$x,$y"
280 586 100 100     1547 if ($x < 0 || $y < 0) {
281 118         303 return undef;
282             }
283             }
284             } else {
285             ### 0,1,2 Y ...
286 1735 100       2902 if ($y >= $yend[-1]) {
287 828         1100 $n += $f[-1];
288 828         1095 $x -= $xend[-1];
289 828         1042 $y -= $yend[-1];
290             ### shift to: "$x,$y levelmod ".$m
291              
292 828 100       1648 if ($m == 0) { # F6 N=8 etc
    100          
    50          
293 368         615 ($x,$y) = ($y,-$x); # -90
294             } elsif ($m == 1) { # F7 N=13 etc
295 76         107 $x = -$x; # -90 T
296             } elsif ($m == 2) { # F8 N=21 etc, incl F2 N=1
297 384         629 ($x,$y) = ($y,$x); # T
298             }
299             ### rot to: "$x,$y"
300 828 100 100     2210 if ($x < 0 || $y < 0) {
301 222         591 return undef;
302             }
303             }
304             }
305              
306 3213         4444 pop @f;
307 3213         4287 pop @xend;
308 3213         4192 pop @yend;
309 3213         5466 $level--;
310             }
311              
312 285 100 100     651 if ($x != 0 || $y != 0) {
313 196         450 return undef;
314             }
315 89         203 return $n;
316             }
317              
318             # not exact
319             sub rect_to_n_range {
320 0     0 1   my ($self, $x1,$y1, $x2,$y2) = @_;
321             ### FibonacciWordFractal rect_to_n_range(): "$x1,$y1 $x2,$y2"
322              
323 0           $x1 = round_nearest ($x1);
324 0           $y1 = round_nearest ($y1);
325 0           $x2 = round_nearest ($x2);
326 0           $y2 = round_nearest ($y2);
327 0 0         ($x1,$x2) = ($x2,$x1) if $x1 > $x2;
328 0 0         ($y1,$y2) = ($y2,$y1) if $y1 > $y2;
329             ### rect_to_n_range(): "$x1,$y1 to $x2,$y2"
330              
331 0 0 0       if ($x2 < 0 || $y2 < 0) {
332 0           return (1, 0);
333             }
334 0           foreach ($x1,$x2,$y1,$y2) {
335 0 0         if (is_infinite($_)) { return (0, $_); }
  0            
336             }
337              
338 0           my $zero = ($x1 * 0 * $y1 * $x2 * $y2); # inherit bignum 0
339 0           my $one = $zero + 1; # inherit bignum 0
340              
341 0           my $f0 = 1;
342 0           my $f1 = 2;
343 0           my $xend0 = $zero;
344 0           my $xend1 = $one;
345 0           my $yend0 = $one;
346 0           my $yend1 = $one;
347 0           my $level = 3;
348              
349 0           for (;;) {
350 0           my ($xo,$yo);
351 0           my $m = (($level+$moffset) % 6);
352 0 0         if ($m == 3) { # at F3 N=2 etc
    0          
    0          
    0          
    0          
353 0           $xo = $yend0; # -90
354 0           $yo = - $xend0;
355             } elsif ($m == 4) { # at F4 N=3 etc
356 0           $xo = $xend0; # T -90
357 0           $yo = - $yend0;
358              
359             } elsif ($m == 5) { # at F5 N=5 etc
360 0           $xo = $yend0; # T
361 0           $yo = $xend0;
362             } elsif ($m == 0) { # at F6 N=8 etc
363 0           $xo = - $yend0; # +90
364 0           $yo = $xend0;
365             } elsif ($m == 1) { # at F7 N=13 etc
366 0           $xo = - $xend0; # T +90
367 0           $yo = $yend0;
368             } else { # if ($m == 2) { # at F8 N=21 etc
369 0           $xo = $yend0; # T
370 0           $yo = $xend0;
371             }
372              
373 0           ($f1,$f0) = ($f1+$f0,$f1);
374 0           ($xend1,$xend0) = ($xend1+$xo,$xend1);
375 0           ($yend1,$yend0) = ($yend1+$yo,$yend1);
376 0           $level++;
377              
378             ### consider: "f1=$f1 xy end $xend1,$yend1"
379 0 0 0       if ($xend1 > $x2 && $yend1 > $y2) {
380 0           return (0, $f1 - 1);
381             }
382             }
383             }
384              
385             #------------------------------------------------------------------------------
386              
387             # ENHANCE-ME: is this good?
388             #
389             # Points which are on alternate X and Y axes
390             # n=0
391             # 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144
392             # ^ ^ ^ ^
393             # 0 4 20 88
394             # F(2+3*level)
395             # or F(2+6*level) to go X axis each time
396             #
397             # sub level_to_n_range {
398             # my ($self, $level) = @_;
399             # return (0, F(2+3*$level)-1);
400             # }
401              
402             1;
403             __END__