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   9482 use 5.004;
  2         13  
24 2     2   11 use strict;
  2         4  
  2         49  
25              
26 2     2   17 use vars '$VERSION', '@ISA';
  2         5  
  2         132  
27             $VERSION = 129;
28 2     2   757 use Math::PlanePath;
  2         4  
  2         72  
29 2     2   1322 use Math::PlanePath::Base::NSEW;
  2         6  
  2         82  
30             @ISA = ('Math::PlanePath::Base::NSEW',
31             'Math::PlanePath');
32              
33             use Math::PlanePath::Base::Generic
34 2         94 'is_infinite',
35 2     2   13 'round_nearest';
  2         3  
36             use Math::PlanePath::Base::Digits
37 2         137 'round_down_pow',
38             'round_up_pow',
39 2     2   496 'digit_split_lowtohigh';
  2         6  
40             *_divrem_mutate = \&Math::PlanePath::_divrem_mutate;
41              
42             # uncomment this to run the ### lines
43             #use Devel::Comments;
44              
45 2     2   14 use constant n_start => 0;
  2         3  
  2         90  
46 2     2   12 use constant class_x_negative => 0;
  2         3  
  2         130  
47 2     2   13 use constant y_negative_at_n => 5;
  2         4  
  2         77  
48 2     2   11 use constant sumxy_minimum => 0; # triangular X>=-Y
  2         4  
  2         85  
49 2     2   12 use constant diffxy_minimum => 0; # triangular Y<=X so X-Y>=0
  2         3  
  2         1963  
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 7115 my ($self, $n) = @_;
62             ### QuadricCurve n_to_xy(): $n
63              
64 1014 50       1860 if ($n < 0) { return; }
  0         0  
65 1014 50       1909 if (is_infinite($n)) { return ($n,$n); }
  0         0  
66              
67 1014         1630 my $x;
68             {
69 1014         1385 my $int = int($n);
  1014         1375  
70 1014         1335 $x = $n - $int; # frac
71 1014         1366 $n = $int; # BigFloat/BigRat int() gives BigInt, use that
72             }
73 1014         1364 my $y = $x * 0; # inherit bignum 0
74 1014         1422 my $len = $y + 1; # inherit bignum 1
75              
76 1014         2001 foreach my $digit (digit_split_lowtohigh($n,8)) {
77             ### at: "$x,$y digit=$digit"
78              
79 3436 100       8246 if ($digit == 0) {
    100          
    100          
    100          
    100          
    100          
    100          
    50          
80              
81             } elsif ($digit == 1) {
82 869         1444 ($x,$y) = (-$y + $len, # rotate +90 and offset
83             $x);
84              
85             } elsif ($digit == 2) {
86 381         501 $x += $len; # offset
87 381         488 $y += $len;
88              
89             } elsif ($digit == 3) {
90 381         661 ($x,$y) = ($y + 2*$len, # rotate -90 and offset
91             -$x + $len);
92              
93             } elsif ($digit == 4) {
94 381         1017 ($x,$y) = ($y + 2*$len, # rotate -90 and offset
95             -$x);
96              
97             } elsif ($digit == 5) {
98 373         501 $x += 2*$len; # offset
99 373         472 $y -= $len;
100              
101             } elsif ($digit == 6) {
102 373         685 ($x,$y) = (-$y + 3*$len, # rotate +90 and offset
103             $x - $len);
104              
105             } elsif ($digit == 7) {
106             ### assert: $digit==7
107 370         532 $x += 3*$len; # offset
108             }
109 3436         4997 $len *= 4;
110             }
111              
112             ### final: "$x,$y"
113 1014         2132 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 46754 my ($self, $x, $y) = @_;
140             ### QuadricCurve xy_to_n(): "$x, $y"
141              
142 5618         11534 $x = round_nearest ($x);
143 5618         10841 $y = round_nearest ($y);
144 5618 100       10992 if ($x < 0) {
145             ### neg x ...
146 265         443 return undef;
147             }
148 5353   100     14096 my ($len,$level) = round_down_pow (($x+abs($y)) || 1, 4);
149             ### $level
150             ### $len
151 5353 50       11099 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   222851 return (($x == 0 && $y == 0)
158             || ($y <= $x
159             && $y > -$x
160             && $y < $len-$x
161             && $y >= $x-$len));
162 5353         19001 };
163              
164 5353         8497 my $n = 0;
165 5353         10116 foreach (0 .. $level) {
166 8756         11593 $n *= 8;
167             ### at: "level=$level len=$len x=$x,y=$y n=$n"
168 8756 100       13405 if (&$diamond_p()) {
169             # digit 0 ...
170             } else {
171 8389         15688 ($x,$y) = ($y, -($x-$len)); # shift and rotate -90
172              
173 8389 100       12277 if (&$diamond_p()) {
174             # digit 1 ...
175 1295         1931 $n += 1;
176             } else {
177 7094         12729 ($x,$y) = (-$y, $x-$len); # shift and rotate +90
178              
179 7094 100       10938 if (&$diamond_p()) {
180             # digit 2 ...
181 1060         1608 $n += 2;
182             } else {
183 6034         10994 ($x,$y) = (-$y, $x-$len); # shift and rotate +90
184              
185 6034 100       8656 if (&$diamond_p()) {
186             # digit 3 ...
187 268         427 $n += 3;
188             } else {
189 5766         7963 $x -= $len;
190              
191 5766 100       8434 if (&$diamond_p()) {
192             # digit 4 ...
193 360         535 $n += 4;
194             } else {
195 5406         9497 ($x,$y) = ($y, -($x-$len)); # shift and rotate -90
196              
197 5406 100       7916 if (&$diamond_p()) {
198             # digit 5 ...
199 148         242 $n += 5;
200             } else {
201 5258         9071 ($x,$y) = ($y, -($x-$len)); # shift and rotate -90
202              
203 5258 100       7714 if (&$diamond_p()) {
204             # digit 6 ...
205 305         491 $n += 6;
206             } else {
207 4953         8672 ($x,$y) = (-$y, $x-$len); # shift and rotate +90
208              
209 4953 100       7293 if (&$diamond_p()) {
210             # digit 7 ...
211 201         332 $n += 7;
212              
213             } else {
214 4752         19271 return undef;
215             }
216             }
217             }
218             }
219             }
220             }
221             }
222             }
223 4004         8075 $len /= 4;
224             }
225             ### end at: "x=$x,y=$y n=$n"
226 601 50 33     1723 if ($x != 0 || $y != 0) {
227 0         0 return undef;
228             }
229 601         2073 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 11 my ($self, $x1,$y1, $x2,$y2) = @_;
238             ### QuadricCurve rect_to_n_range(): "$x1,$y1 $x2,$y2"
239              
240 1         5 $x1 = round_nearest ($x1);
241 1         3 $x2 = round_nearest ($x2);
242 1 50       4 if ($x2 < $x1) {
243 0         0 $x2 = $x1; # x2 bigger
244             }
245 1 50       3 if ($x2 < 0) {
246 0         0 return (1,0); # rect all x negative, no points
247             }
248 1         3 $y1 = abs (round_nearest ($y1));
249 1         3 $y2 = abs (round_nearest ($y2));
250 1 50       4 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         3 return (0, $p4*$p4);
257             }
258              
259             #------------------------------------------------------------------------------
260             # levels
261              
262             sub level_to_n_range {
263 3     3 1 212 my ($self, $level) = @_;
264 3         10 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   12324 my ($self, $n) = @_;
282 998 50 33     2501 if ($n < 1 || is_infinite($n)) { return undef; }
  0         0  
283 998         2215 while ($n) {
284 1138 100       2107 if (my $digit = _divrem_mutate($n,8)) { # lowest non-zero digit
285 998         2031 return $_UNDOCUMENTED__n_to_turn_LSR[$digit];
286             }
287             }
288 0           return undef;
289             }
290             }
291              
292              
293             #------------------------------------------------------------------------------
294             1;
295             __END__