File Coverage

blib/lib/Math/PlanePath/QuadricCurve.pm
Criterion Covered Total %
statement 112 128 87.5
branch 42 54 77.7
condition 5 8 62.5
subroutine 18 19 94.7
pod 5 5 100.0
total 182 214 85.0


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             package Math::PlanePath::QuadricCurve;
20 2     2   8957 use 5.004;
  2         13  
21 2     2   12 use strict;
  2         4  
  2         58  
22              
23 2     2   9 use vars '$VERSION', '@ISA';
  2         4  
  2         132  
24             $VERSION = 127;
25 2     2   669 use Math::PlanePath;
  2         4  
  2         65  
26 2     2   850 use Math::PlanePath::Base::NSEW;
  2         4  
  2         79  
27             @ISA = ('Math::PlanePath::Base::NSEW',
28             'Math::PlanePath');
29              
30             use Math::PlanePath::Base::Generic
31 2         91 'is_infinite',
32 2     2   13 'round_nearest';
  2         3  
33             use Math::PlanePath::Base::Digits
34 2         127 'round_down_pow',
35             'round_up_pow',
36 2     2   446 'digit_split_lowtohigh';
  2         4  
37             *_divrem_mutate = \&Math::PlanePath::_divrem_mutate;
38              
39             # uncomment this to run the ### lines
40             #use Devel::Comments;
41              
42 2     2   11 use constant n_start => 0;
  2         4  
  2         142  
43 2     2   12 use constant class_x_negative => 0;
  2         4  
  2         80  
44 2     2   11 use constant y_negative_at_n => 5;
  2         3  
  2         84  
45 2     2   12 use constant sumxy_minimum => 0; # triangular X>=-Y
  2         3  
  2         84  
46 2     2   12 use constant diffxy_minimum => 0; # triangular Y<=X so X-Y>=0
  2         10  
  2         1822  
47              
48              
49             #------------------------------------------------------------------------------
50              
51             # 2---3
52             # | |
53             # 0---1 4 7---8
54             # | |
55             # 5---6
56             #
57             sub n_to_xy {
58 1014     1014 1 7133 my ($self, $n) = @_;
59             ### QuadricCurve n_to_xy(): $n
60              
61 1014 50       1764 if ($n < 0) { return; }
  0         0  
62 1014 50       1899 if (is_infinite($n)) { return ($n,$n); }
  0         0  
63              
64 1014         1663 my $x;
65             {
66 1014         1295 my $int = int($n);
  1014         1394  
67 1014         1365 $x = $n - $int; # frac
68 1014         1406 $n = $int; # BigFloat/BigRat int() gives BigInt, use that
69             }
70 1014         1347 my $y = $x * 0; # inherit bignum 0
71 1014         1417 my $len = $y + 1; # inherit bignum 1
72              
73 1014         1896 foreach my $digit (digit_split_lowtohigh($n,8)) {
74             ### at: "$x,$y digit=$digit"
75              
76 3436 100       8324 if ($digit == 0) {
    100          
    100          
    100          
    100          
    100          
    100          
    50          
77              
78             } elsif ($digit == 1) {
79 869         1467 ($x,$y) = (-$y + $len, # rotate +90 and offset
80             $x);
81              
82             } elsif ($digit == 2) {
83 381         498 $x += $len; # offset
84 381         494 $y += $len;
85              
86             } elsif ($digit == 3) {
87 381         685 ($x,$y) = ($y + 2*$len, # rotate -90 and offset
88             -$x + $len);
89              
90             } elsif ($digit == 4) {
91 381         648 ($x,$y) = ($y + 2*$len, # rotate -90 and offset
92             -$x);
93              
94             } elsif ($digit == 5) {
95 373         507 $x += 2*$len; # offset
96 373         512 $y -= $len;
97              
98             } elsif ($digit == 6) {
99 373         621 ($x,$y) = (-$y + 3*$len, # rotate +90 and offset
100             $x - $len);
101              
102             } elsif ($digit == 7) {
103             ### assert: $digit==7
104 370         541 $x += 3*$len; # offset
105             }
106 3436         5067 $len *= 4;
107             }
108              
109             ### final: "$x,$y"
110 1014         2661 return ($x,$y);
111             }
112              
113              
114             # 8
115             # |
116             # 7---6
117             # |
118             # 3---4---5
119             # |
120             # 2---1
121             # |
122             # 0
123             #
124             # |
125             # * 11--12--13
126             # / \ |
127             # 2---3 10---9
128             # / | | \ |
129             # 0---1 4 7---8
130             # \ | | /
131             # 5---6
132             # \ /
133             # *
134             #
135             sub xy_to_n {
136 5618     5618 1 44729 my ($self, $x, $y) = @_;
137             ### QuadricCurve xy_to_n(): "$x, $y"
138              
139 5618         10552 $x = round_nearest ($x);
140 5618         11053 $y = round_nearest ($y);
141 5618 100       10388 if ($x < 0) {
142             ### neg x ...
143 265         450 return undef;
144             }
145 5353   100     13747 my ($len,$level) = round_down_pow (($x+abs($y)) || 1, 4);
146             ### $level
147             ### $len
148 5353 50       11222 if (is_infinite($level)) {
149 0         0 return $level;
150             }
151              
152             my $diamond_p = sub {
153             ### diamond_p(): "$x,$y len=$len is ".(($x == 0 && $y == 0) || ($y <= $x && $y > -$x && $y < $len-$x && $y >= $x-$len))
154 51656   66 51656   223964 return (($x == 0 && $y == 0)
155             || ($y <= $x
156             && $y > -$x
157             && $y < $len-$x
158             && $y >= $x-$len));
159 5353         18484 };
160              
161 5353         9016 my $n = 0;
162 5353         9741 foreach (0 .. $level) {
163 8756         11750 $n *= 8;
164             ### at: "level=$level len=$len x=$x,y=$y n=$n"
165 8756 100       13097 if (&$diamond_p()) {
166             # digit 0 ...
167             } else {
168 8389         16253 ($x,$y) = ($y, -($x-$len)); # shift and rotate -90
169              
170 8389 100       12050 if (&$diamond_p()) {
171             # digit 1 ...
172 1295         2018 $n += 1;
173             } else {
174 7094         12447 ($x,$y) = (-$y, $x-$len); # shift and rotate +90
175              
176 7094 100       10231 if (&$diamond_p()) {
177             # digit 2 ...
178 1060         1588 $n += 2;
179             } else {
180 6034         10504 ($x,$y) = (-$y, $x-$len); # shift and rotate +90
181              
182 6034 100       8628 if (&$diamond_p()) {
183             # digit 3 ...
184 268         479 $n += 3;
185             } else {
186 5766         8398 $x -= $len;
187              
188 5766 100       8215 if (&$diamond_p()) {
189             # digit 4 ...
190 360         542 $n += 4;
191             } else {
192 5406         9385 ($x,$y) = ($y, -($x-$len)); # shift and rotate -90
193              
194 5406 100       7779 if (&$diamond_p()) {
195             # digit 5 ...
196 148         222 $n += 5;
197             } else {
198 5258         9012 ($x,$y) = ($y, -($x-$len)); # shift and rotate -90
199              
200 5258 100       7341 if (&$diamond_p()) {
201             # digit 6 ...
202 305         488 $n += 6;
203             } else {
204 4953         8294 ($x,$y) = (-$y, $x-$len); # shift and rotate +90
205              
206 4953 100       7263 if (&$diamond_p()) {
207             # digit 7 ...
208 201         316 $n += 7;
209              
210             } else {
211 4752         19165 return undef;
212             }
213             }
214             }
215             }
216             }
217             }
218             }
219             }
220 4004         7939 $len /= 4;
221             }
222             ### end at: "x=$x,y=$y n=$n"
223 601 50 33     1736 if ($x != 0 || $y != 0) {
224 0         0 return undef;
225             }
226 601         2072 return $n;
227             }
228              
229             # level extends to x= 4^level
230             # level = log4(x)
231             #
232             # not exact
233             sub rect_to_n_range {
234 1     1 1 12 my ($self, $x1,$y1, $x2,$y2) = @_;
235             ### QuadricCurve rect_to_n_range(): "$x1,$y1 $x2,$y2"
236              
237 1         4 $x1 = round_nearest ($x1);
238 1         4 $x2 = round_nearest ($x2);
239 1 50       5 if ($x2 < $x1) {
240 0         0 $x2 = $x1; # x2 bigger
241             }
242 1 50       3 if ($x2 < 0) {
243 0         0 return (1,0); # rect all x negative, no points
244             }
245 1         3 $y1 = abs (round_nearest ($y1));
246 1         4 $y2 = abs (round_nearest ($y2));
247 1 50       2 if ($y2 < $y1) {
248 0         0 $y2 = $y1; # y2 bigger abs
249             }
250              
251 1         2 my $p4 = $x2+$y2+1;
252             ### $p4
253 1         3 return (0, $p4*$p4);
254             }
255              
256             #------------------------------------------------------------------------------
257             # levels
258              
259             sub level_to_n_range {
260 3     3 1 207 my ($self, $level) = @_;
261 3         10 return (0, 8**$level);
262             }
263             sub n_to_level {
264 0     0 1 0 my ($self, $n) = @_;
265 0 0       0 if ($n < 0) { return undef; }
  0         0  
266 0 0       0 if (is_infinite($n)) { return $n; }
  0         0  
267 0         0 $n = round_nearest($n);
268 0         0 my ($pow, $exp) = round_up_pow ($n, 8);
269 0         0 return $exp;
270             }
271              
272             #------------------------------------------------------------------------------
273              
274             {
275             # 0 1 2 3 4 5 6 7
276             my @_UNDOCUMENTED__n_to_turn_LSR = (undef, 1,-1,-1, 0, 1,1,-1);
277             sub _UNDOCUMENTED__n_to_turn_LSR {
278 998     998   12271 my ($self, $n) = @_;
279 998         1844 while ($n) {
280 1138 100       2003 if (my $digit = _divrem_mutate($n,8)) { # lowest non-zero digit
281 998         2111 return $_UNDOCUMENTED__n_to_turn_LSR[$digit];
282             }
283             }
284 0           return undef;
285             }
286             }
287              
288              
289             #------------------------------------------------------------------------------
290             1;
291             __END__