File Coverage

blib/lib/Math/PlanePath/QuadricCurve.pm
Criterion Covered Total %
statement 113 130 86.9
branch 43 56 76.7
condition 6 11 54.5
subroutine 18 19 94.7
pod 5 5 100.0
total 185 221 83.7


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             # cf http://mathcurve.com/fractals/minkowski/minkowski.shtml
20              
21              
22             package Math::PlanePath::QuadricCurve;
23 2     2   8976 use 5.004;
  2         14  
24 2     2   11 use strict;
  2         4  
  2         60  
25              
26 2     2   12 use vars '$VERSION', '@ISA';
  2         5  
  2         136  
27             $VERSION = 128;
28 2     2   712 use Math::PlanePath;
  2         5  
  2         66  
29 2     2   815 use Math::PlanePath::Base::NSEW;
  2         5  
  2         81  
30             @ISA = ('Math::PlanePath::Base::NSEW',
31             'Math::PlanePath');
32              
33             use Math::PlanePath::Base::Generic
34 2         93 'is_infinite',
35 2     2   13 'round_nearest';
  2         3  
36             use Math::PlanePath::Base::Digits
37 2         153 'round_down_pow',
38             'round_up_pow',
39 2     2   436 'digit_split_lowtohigh';
  2         5  
40             *_divrem_mutate = \&Math::PlanePath::_divrem_mutate;
41              
42             # uncomment this to run the ### lines
43             #use Devel::Comments;
44              
45 2     2   13 use constant n_start => 0;
  2         4  
  2         92  
46 2     2   11 use constant class_x_negative => 0;
  2         4  
  2         73  
47 2     2   11 use constant y_negative_at_n => 5;
  2         3  
  2         72  
48 2     2   10 use constant sumxy_minimum => 0; # triangular X>=-Y
  2         4  
  2         81  
49 2     2   11 use constant diffxy_minimum => 0; # triangular Y<=X so X-Y>=0
  2         4  
  2         1863  
50              
51              
52             #------------------------------------------------------------------------------
53              
54             # 2---3
55             # | |
56             # 0---1 4 7---8
57             # | |
58             # 5---6
59             #
60             sub n_to_xy {
61 1014     1014 1 7310 my ($self, $n) = @_;
62             ### QuadricCurve n_to_xy(): $n
63              
64 1014 50       1879 if ($n < 0) { return; }
  0         0  
65 1014 50       1871 if (is_infinite($n)) { return ($n,$n); }
  0         0  
66              
67 1014         1547 my $x;
68             {
69 1014         1311 my $int = int($n);
  1014         1476  
70 1014         1393 $x = $n - $int; # frac
71 1014         1456 $n = $int; # BigFloat/BigRat int() gives BigInt, use that
72             }
73 1014         1402 my $y = $x * 0; # inherit bignum 0
74 1014         1474 my $len = $y + 1; # inherit bignum 1
75              
76 1014         1983 foreach my $digit (digit_split_lowtohigh($n,8)) {
77             ### at: "$x,$y digit=$digit"
78              
79 3436 100       8285 if ($digit == 0) {
    100          
    100          
    100          
    100          
    100          
    100          
    50          
80              
81             } elsif ($digit == 1) {
82 869         1397 ($x,$y) = (-$y + $len, # rotate +90 and offset
83             $x);
84              
85             } elsif ($digit == 2) {
86 381         555 $x += $len; # offset
87 381         510 $y += $len;
88              
89             } elsif ($digit == 3) {
90 381         691 ($x,$y) = ($y + 2*$len, # rotate -90 and offset
91             -$x + $len);
92              
93             } elsif ($digit == 4) {
94 381         647 ($x,$y) = ($y + 2*$len, # rotate -90 and offset
95             -$x);
96              
97             } elsif ($digit == 5) {
98 373         506 $x += 2*$len; # offset
99 373         482 $y -= $len;
100              
101             } elsif ($digit == 6) {
102 373         656 ($x,$y) = (-$y + 3*$len, # rotate +90 and offset
103             $x - $len);
104              
105             } elsif ($digit == 7) {
106             ### assert: $digit==7
107 370         543 $x += 3*$len; # offset
108             }
109 3436         5060 $len *= 4;
110             }
111              
112             ### final: "$x,$y"
113 1014         2208 return ($x,$y);
114             }
115              
116              
117             # 8
118             # |
119             # 7---6
120             # |
121             # 3---4---5
122             # |
123             # 2---1
124             # |
125             # 0
126             #
127             # |
128             # * 11--12--13
129             # / \ |
130             # 2---3 10---9
131             # / | | \ |
132             # 0---1 4 7---8
133             # \ | | /
134             # 5---6
135             # \ /
136             # *
137             #
138             sub xy_to_n {
139 5618     5618 1 45486 my ($self, $x, $y) = @_;
140             ### QuadricCurve xy_to_n(): "$x, $y"
141              
142 5618         11103 $x = round_nearest ($x);
143 5618         10273 $y = round_nearest ($y);
144 5618 100       11371 if ($x < 0) {
145             ### neg x ...
146 265         514 return undef;
147             }
148 5353   100     14553 my ($len,$level) = round_down_pow (($x+abs($y)) || 1, 4);
149             ### $level
150             ### $len
151 5353 50       11467 if (is_infinite($level)) {
152 0         0 return $level;
153             }
154              
155             my $diamond_p = sub {
156             ### diamond_p(): "$x,$y len=$len is ".(($x == 0 && $y == 0) || ($y <= $x && $y > -$x && $y < $len-$x && $y >= $x-$len))
157 51656   66 51656   226398 return (($x == 0 && $y == 0)
158             || ($y <= $x
159             && $y > -$x
160             && $y < $len-$x
161             && $y >= $x-$len));
162 5353         19314 };
163              
164 5353         8642 my $n = 0;
165 5353         9749 foreach (0 .. $level) {
166 8756         11482 $n *= 8;
167             ### at: "level=$level len=$len x=$x,y=$y n=$n"
168 8756 100       13370 if (&$diamond_p()) {
169             # digit 0 ...
170             } else {
171 8389         16170 ($x,$y) = ($y, -($x-$len)); # shift and rotate -90
172              
173 8389 100       12152 if (&$diamond_p()) {
174             # digit 1 ...
175 1295         1915 $n += 1;
176             } else {
177 7094         12520 ($x,$y) = (-$y, $x-$len); # shift and rotate +90
178              
179 7094 100       10426 if (&$diamond_p()) {
180             # digit 2 ...
181 1060         1669 $n += 2;
182             } else {
183 6034         10338 ($x,$y) = (-$y, $x-$len); # shift and rotate +90
184              
185 6034 100       9306 if (&$diamond_p()) {
186             # digit 3 ...
187 268         453 $n += 3;
188             } else {
189 5766         8071 $x -= $len;
190              
191 5766 100       8203 if (&$diamond_p()) {
192             # digit 4 ...
193 360         552 $n += 4;
194             } else {
195 5406         9629 ($x,$y) = ($y, -($x-$len)); # shift and rotate -90
196              
197 5406 100       7735 if (&$diamond_p()) {
198             # digit 5 ...
199 148         220 $n += 5;
200             } else {
201 5258         8987 ($x,$y) = ($y, -($x-$len)); # shift and rotate -90
202              
203 5258 100       7575 if (&$diamond_p()) {
204             # digit 6 ...
205 305         475 $n += 6;
206             } else {
207 4953         8911 ($x,$y) = (-$y, $x-$len); # shift and rotate +90
208              
209 4953 100       7288 if (&$diamond_p()) {
210             # digit 7 ...
211 201         311 $n += 7;
212              
213             } else {
214 4752         20102 return undef;
215             }
216             }
217             }
218             }
219             }
220             }
221             }
222             }
223 4004         8136 $len /= 4;
224             }
225             ### end at: "x=$x,y=$y n=$n"
226 601 50 33     1731 if ($x != 0 || $y != 0) {
227 0         0 return undef;
228             }
229 601         2149 return $n;
230             }
231              
232             # level extends to x= 4^level
233             # level = log4(x)
234             #
235             # not exact
236             sub rect_to_n_range {
237 1     1 1 15 my ($self, $x1,$y1, $x2,$y2) = @_;
238             ### QuadricCurve rect_to_n_range(): "$x1,$y1 $x2,$y2"
239              
240 1         6 $x1 = round_nearest ($x1);
241 1         3 $x2 = round_nearest ($x2);
242 1 50       5 if ($x2 < $x1) {
243 0         0 $x2 = $x1; # x2 bigger
244             }
245 1 50       4 if ($x2 < 0) {
246 0         0 return (1,0); # rect all x negative, no points
247             }
248 1         4 $y1 = abs (round_nearest ($y1));
249 1         3 $y2 = abs (round_nearest ($y2));
250 1 50       3 if ($y2 < $y1) {
251 0         0 $y2 = $y1; # y2 bigger abs
252             }
253              
254 1         2 my $p4 = $x2+$y2+1;
255             ### $p4
256 1         4 return (0, $p4*$p4);
257             }
258              
259             #------------------------------------------------------------------------------
260             # levels
261              
262             sub level_to_n_range {
263 3     3 1 206 my ($self, $level) = @_;
264 3         11 return (0, 8**$level);
265             }
266             sub n_to_level {
267 0     0 1 0 my ($self, $n) = @_;
268 0 0       0 if ($n < 0) { return undef; }
  0         0  
269 0 0       0 if (is_infinite($n)) { return $n; }
  0         0  
270 0         0 $n = round_nearest($n);
271 0         0 my ($pow, $exp) = round_up_pow ($n, 8);
272 0         0 return $exp;
273             }
274              
275             #------------------------------------------------------------------------------
276              
277             {
278             # 0 1 2 3 4 5 6 7
279             my @_UNDOCUMENTED__n_to_turn_LSR = (undef, 1,-1,-1, 0, 1,1,-1);
280             sub _UNDOCUMENTED__n_to_turn_LSR {
281 998     998   12343 my ($self, $n) = @_;
282 998 50 33     2689 if ($n < 1 || is_infinite($n)) { return undef; }
  0         0  
283 998         2105 while ($n) {
284 1138 100       2101 if (my $digit = _divrem_mutate($n,8)) { # lowest non-zero digit
285 998         1984 return $_UNDOCUMENTED__n_to_turn_LSR[$digit];
286             }
287             }
288 0           return undef;
289             }
290             }
291              
292              
293             #------------------------------------------------------------------------------
294             1;
295             __END__