File Coverage

blib/lib/Math/PlanePath/AlternatePaper.pm
Criterion Covered Total %
statement 191 286 66.7
branch 69 124 55.6
condition 59 116 50.8
subroutine 21 35 60.0
pod 12 12 100.0
total 352 573 61.4


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             # ENHANCE-ME: Explanation for this bit ...
20             # 'arms=4' =>
21             # { dSum => 'A020985', # GRS
22             # # OEIS-Other: A020985 planepath=AlternatePaper,arms=4 delta_type=dSum
23             # },
24              
25              
26             package Math::PlanePath::AlternatePaper;
27 2     2   9603 use 5.004;
  2         7  
28 2     2   11 use strict;
  2         4  
  2         59  
29 2     2   11 use List::Util 'min'; # 'max'
  2         5  
  2         209  
30             *max = \&Math::PlanePath::_max;
31              
32 2     2   21 use vars '$VERSION', '@ISA';
  2         6  
  2         140  
33             $VERSION = 127;
34 2     2   711 use Math::PlanePath;
  2         4  
  2         58  
35 2     2   452 use Math::PlanePath::Base::NSEW;
  2         3  
  2         107  
36             @ISA = ('Math::PlanePath::Base::NSEW',
37             'Math::PlanePath');
38              
39             use Math::PlanePath::Base::Generic
40 2         98 'is_infinite',
41 2     2   13 'round_nearest';
  2         4  
42             use Math::PlanePath::Base::Digits
43 2         215 'round_down_pow',
44             'digit_split_lowtohigh',
45             'digit_join_lowtohigh',
46 2     2   485 'bit_split_lowtohigh';
  2         5  
47             *_divrem = \&Math::PlanePath::_divrem;
48             *_divrem_mutate = \&Math::PlanePath::_divrem_mutate;
49              
50             # uncomment this to run the ### lines
51             # use Smart::Comments;
52              
53              
54 2         125 use constant parameter_info_array => [ { name => 'arms',
55             share_key => 'arms_8',
56             display => 'Arms',
57             type => 'integer',
58             minimum => 1,
59             maximum => 8,
60             default => 1,
61             width => 1,
62             description => 'Arms',
63 2     2   14 } ];
  2         5  
64              
65 2     2   21 use constant n_start => 0;
  2         5  
  2         580  
66             sub x_negative {
67 6     6 1 92 my ($self) = @_;
68 6         15 return ($self->{'arms'} >= 3);
69             }
70             sub y_negative {
71 6     6 1 326 my ($self) = @_;
72 6         16 return ($self->{'arms'} >= 5);
73             }
74             {
75             my @x_negative_at_n = (undef,
76             undef,undef,8,7,
77             4,4,4,4);
78             sub x_negative_at_n {
79 0     0 1 0 my ($self) = @_;
80 0         0 return $x_negative_at_n[$self->{'arms'}];
81             }
82             }
83             {
84             my @y_negative_at_n = (undef,
85             undef,undef,undef,undef,
86             44,23,13,14);
87             sub y_negative_at_n {
88 0     0 1 0 my ($self) = @_;
89 0         0 return $y_negative_at_n[$self->{'arms'}];
90             }
91             }
92              
93             sub sumxy_minimum {
94 0     0 1 0 my ($self) = @_;
95 0 0       0 return ($self->arms_count <= 3
96             ? 0 # 1,2,3 arms above X=-Y diagonal
97             : undef);
98             }
99             sub diffxy_minimum {
100 0     0 1 0 my ($self) = @_;
101 0 0       0 return ($self->arms_count == 1
102             ? 0 # 1 arms right of X=Y diagonal
103             : undef);
104             }
105              
106 2     2   16 use constant turn_any_straight => 0; # never straight
  2         10  
  2         3409  
107              
108              
109             #------------------------------------------------------------------------------
110              
111             sub new {
112 37     37 1 6044 my $self = shift->SUPER::new(@_);
113 37   100     296 $self->{'arms'} = max(1, min(8, $self->{'arms'} || 1));
114 37         96 return $self;
115             }
116              
117              
118             # state=0 /| +----+----+
119             # / | |\ 1||<--/
120             # /2 | |^\ || 0/
121             # /-->| || \v| /
122             # +----+ ||3 \|/
123             # /|\ 3|| +----+
124             # / |^\ || |<--/ state=4
125             # / 0|| \v| | 2/
126             # /-->||1 \| | /
127             # +----+----+ |/
128             #
129             # |\ state=8 +----+----+ state=12
130             # |^\ \ 1||<--/|
131             # || \ \ || 0/ |
132             # ||3 \ \v| /2 |
133             # +----+ \|/-->|
134             # |<--/|\ +----+
135             # | 2/ |^\ \ 3||
136             # | /0 || \ \ ||
137             # |/-->||1 \ \v|
138             # +----+----+ \|
139              
140             my @next_state = (0, 8, 0, 12, # forward
141             4, 12, 4, 8, # forward NW
142             0, 8, 4, 8, # reverse
143             4, 12, 0, 12, # reverse NE
144             );
145             my @digit_to_x = (0,1,1,1,
146             1,0,0,0,
147             0,1,0,0,
148             1,0,1,1,
149             );
150             my @digit_to_y = (0,0,1,0,
151             1,1,0,1,
152             0,0,0,1,
153             1,1,1,0,
154             );
155              
156             # state_to_dx[S] == state_to_x[S+3] - state_to_x[S+0]
157             my @state_to_dx = (1, undef,undef,undef,
158             -1, undef,undef,undef,
159             0, undef,undef,undef,
160             0, undef,undef,undef,
161             );
162             my @state_to_dy = (0, undef,undef,undef,
163             0, undef,undef,undef,
164             1, undef,undef,undef,
165             -1, undef,undef,undef,
166             );
167              
168             sub n_to_xy {
169 7847     7847 1 249362 my ($self, $n) = @_;
170             ### AlternatePaper n_to_xy(): $n
171              
172 7847 50       15084 if ($n < 0) { return; }
  0         0  
173 7847 50       15257 if (is_infinite($n)) { return ($n, $n); }
  0         0  
174              
175 7847         14343 my $int = int($n); # integer part
176 7847         10784 $n -= $int; # fraction part
177             ### $int
178             ### $n
179              
180 7847         11106 my $zero = ($int * 0); # inherit bignum 0
181 7847         16688 my $arm = _divrem_mutate ($int, $self->{'arms'});
182              
183             ### $arm
184             ### $int
185              
186 7847         15767 my @digits = digit_split_lowtohigh($int,4);
187 7847         11793 my $state = 0;
188 7847         10977 my (@xbits,@ybits); # bits low to high (like @digits)
189              
190 7847         14855 foreach my $i (reverse 0 .. $#digits) { # high to low
191 19058         26255 $state += $digits[$i];
192 19058         27906 $xbits[$i] = $digit_to_x[$state];
193 19058         26516 $ybits[$i] = $digit_to_y[$state];
194 19058         28275 $state = $next_state[$state];
195             }
196 7847         17523 my $x = digit_join_lowtohigh(\@xbits,2,$zero);
197 7847         15195 my $y = digit_join_lowtohigh(\@ybits,2,$zero);
198              
199             # X+1,Y+1 for final state=4 or state=12
200 7847         11891 $x += $digit_to_x[$state];
201 7847         10835 $y += $digit_to_y[$state];
202              
203             ### final: "xy=$x,$y state=$state"
204              
205             # apply possible fraction part of $n in direction of $state
206 7847         11514 $x = $n * $state_to_dx[$state] + $x;
207 7847         10779 $y = $n * $state_to_dy[$state] + $y;
208              
209             # rotate,transpose for arm number
210 7847 100       14719 if ($arm & 1) {
211 3368         5969 ($x,$y) = ($y,$x); # transpose
212             }
213 7847 100       13634 if ($arm & 2) {
214 2888         5154 ($x,$y) = (-$y,$x+1); # rotate +90 and shift origin to X=0,Y=1
215             }
216 7847 100       13305 if ($arm & 4) {
217 2026         3204 $x = -1 - $x; # rotate +180 and shift origin to X=-1,Y=1
218 2026         2753 $y = 1 - $y;
219             }
220              
221             ### rotated return: "$x,$y"
222 7847         18704 return ($x,$y);
223             }
224              
225             # 8
226             #
227             # 42 43 7
228             #
229             # 40 41/45 44 6
230             #
231             # 34 35/39 38/46 47 5
232             #
233             # 32-33/53-36/52-37/49---48 4
234             # | \
235             # 10 11/31 30/54 51/55 50/58 59 3
236             # | \
237             # 8 9/13 12/28 25/29 24/56 57/61 60 2
238             # | \
239             # 2 3/7 6/14 15/27 18/26 19/23 22/62 63 1
240             # | \
241             # 0 1 4 5 16 17 20 21 ==64 0
242             #
243             # 0 1 2 3 4 5 6 7 8
244              
245             sub xy_to_n {
246 121     121 1 8231 return scalar((shift->xy_to_n_list(@_))[0]);
247             }
248             sub xy_to_n_list {
249 159     159 1 4690 my ($self, $x, $y) = @_;
250             ### AlternatePaper xy_to_n(): "$x, $y"
251              
252 159         385 $x = round_nearest($x);
253 159         338 $y = round_nearest($y);
254 159 50       392 if (is_infinite($x)) { return $x; }
  0         0  
255 159 50       344 if (is_infinite($y)) { return $y; }
  0         0  
256              
257 159         311 my $arms = $self->{'arms'};
258 159         207 my $arm = 0;
259 159         220 my @ret;
260 159         316 foreach (1 .. 4) {
261 231         432 push @ret, map {$_*$arms+$arm} _xy_to_n_list__onearm($self,$x,$y);
  174         410  
262 231 100       477 last if ++$arm >= $arms;
263              
264 113         219 ($x,$y) = ($y,$x); # transpose
265 113         213 push @ret, map {$_*$arms+$arm} _xy_to_n_list__onearm($self,$x,$y);
  47         123  
266 113 100       255 last if ++$arm >= $arms;
267              
268             # X,Y -> Y,X
269             # -> Y,X-1 # Y-1 shift
270             # -> X-1,-Y # rot -90
271             # ie. mirror across X axis and shift
272 72         147 ($x,$y) = ($x-1,-$y);
273             }
274 159         474 return sort {$a<=>$b} @ret;
  85         298  
275             }
276              
277             sub _xy_to_n_list__onearm {
278 344     344   557 my ($self, $x, $y) = @_;
279             ### _xy_to_n_list__onearm(): "$x,$y"
280              
281 344 100 100     1096 if ($y < 0 || $y > $x || $x < 0) {
      66        
282             ### outside first octant ...
283 179         302 return;
284             }
285              
286 165         376 my ($len,$level) = round_down_pow($x, 2);
287             ### $len
288             ### $level
289 165 50       339 if (is_infinite($level)) {
290 0         0 return;
291             }
292              
293 165         312 my $n = my $big_n = $x * 0 * $y; # inherit bignum 0
294 165         217 my $rev = 0;
295              
296 165         251 my $big_x = $x;
297 165         221 my $big_y = $y;
298 165         245 my $big_rev = 0;
299              
300 165         326 while ($level-- >= 0) {
301             ### at: "$x,$y len=$len n=$n"
302              
303             # the smaller N
304             {
305 429         534 $n *= 4;
306 429 100       674 if ($rev) {
307 121 100       230 if ($x+$y < 2*$len) {
308             ### rev 0 or 1 ...
309 39 100       72 if ($x < $len) {
310             } else {
311             ### rev 1 ...
312 20         26 $rev = 0;
313 20         30 $n -= 2;
314 20         38 ($x,$y) = ($len-$y, $x-$len); # x-len,y-len then rotate +90
315             }
316              
317             } else {
318             ### rev 2 or 3 ...
319 82 100 66     280 if ($y > $len || ($x==$len && $y==$len)) {
      100        
320             ### rev 2 ...
321 28         38 $n -= 2;
322 28         42 $x -= $len;
323 28         41 $y -= $len;
324             } else {
325             ### rev 3 ...
326 54         75 $n -= 4;
327 54         85 $rev = 0;
328 54         105 ($x,$y) = ($y, 2*$len-$x); # to origin then rotate -90
329             }
330             }
331             } else {
332 308 100 100     1366 if ($x+$y <= 2*$len
      100        
      66        
      100        
333             && !($x==$len && $y==$len)
334             && !($x==2*$len && $y==0)) {
335             ### 0 or 1 ...
336 178 100       350 if ($x <= $len) {
337             } else {
338             ### 1 ...
339 61         94 $n += 2;
340 61         83 $rev = 1;
341 61         126 ($x,$y) = ($len-$y, $x-$len); # x-len,y-len then rotate +90
342             }
343              
344             } else {
345             ### 2 or 3 ...
346 130 100 100     430 if ($y >= $len && !($x==2*$len && $y==$len)) {
      100        
347 70         94 $n += 2;
348 70         100 $x -= $len;
349 70         94 $y -= $len;
350             } else {
351 60         87 $n += 4;
352 60         83 $rev = 1;
353 60         114 ($x,$y) = ($y, 2*$len-$x); # to origin then rotate -90
354             }
355             }
356             }
357             }
358              
359             # the bigger N
360             {
361 429         529 $big_n *= 4;
  429         552  
  429         582  
362 429 100       648 if ($big_rev) {
363 161 100 100     649 if ($big_x+$big_y <= 2*$len
      100        
      66        
      100        
364             && !($big_x==$len && $big_y==$len)
365             && !($big_x==2*$len && $big_y==0)) {
366             ### rev 0 or 1 ...
367 67 100       118 if ($big_x <= $len) {
368             } else {
369             ### rev 1 ...
370 23         32 $big_rev = 0;
371 23         31 $big_n -= 2;
372 23         41 ($big_x,$big_y) = ($len-$big_y, $big_x-$len); # x-len,y-len then rotate +90
373             }
374              
375             } else {
376             ### rev 2 or 3 ...
377 94 100 100     272 if ($big_y >= $len && !($big_x==2*$len && $big_y==$len)) {
      100        
378             ### rev 2 ...
379 34         53 $big_n -= 2;
380 34         48 $big_x -= $len;
381 34         48 $big_y -= $len;
382             } else {
383             ### rev 3 ...
384 60         75 $big_n -= 4;
385 60         82 $big_rev = 0;
386 60         106 ($big_x,$big_y) = ($big_y, 2*$len-$big_x); # to origin then rotate -90
387             }
388             }
389             } else {
390 268 100       454 if ($big_x+$big_y < 2*$len) {
391             ### 0 or 1 ...
392 170 100       276 if ($big_x < $len) {
393             } else {
394             ### 1 ...
395 105         135 $big_n += 2;
396 105         138 $big_rev = 1;
397 105         210 ($big_x,$big_y) = ($len-$big_y, $big_x-$len); # x-len,y-len then rotate +90
398             }
399              
400             } else {
401             ### 2 or 3 ...
402 98 100 66     317 if ($big_y > $len || ($big_x==$len && $big_y==$len)) {
      100        
403 56         98 $big_n += 2;
404 56         85 $big_x -= $len;
405 56         88 $big_y -= $len;
406             } else {
407 42         60 $big_n += 4;
408 42         59 $big_rev = 1;
409 42         74 ($big_x,$big_y) = ($big_y, 2*$len-$big_x); # to origin then rotate -90
410             }
411             }
412             }
413             }
414 429         837 $len /= 2;
415             }
416              
417 165 100       300 if ($x) {
418 61 100       120 $n += ($rev ? -1 : 1);
419             }
420 165 100       285 if ($big_x) {
421 61 100       108 $big_n += ($big_rev ? -1 : 1);
422             }
423              
424             ### final: "$x,$y n=$n rev=$rev"
425             ### final: "$x,$y big_n=$n big_rev=$rev"
426              
427 165 100       413 return ($n,
428             ($n == $big_n ? () : ($big_n)));
429             }
430              
431              
432             # not exact
433             sub rect_to_n_range {
434 40     40 1 3400 my ($self, $x1,$y1, $x2,$y2) = @_;
435             ### AlternatePaper rect_to_n_range(): "$x1,$y1 $x2,$y2"
436              
437 40         112 $x1 = round_nearest($x1);
438 40         83 $x2 = round_nearest($x2);
439 40         84 $y1 = round_nearest($y1);
440 40         78 $y2 = round_nearest($y2);
441              
442 40 50       87 ($x1,$x2) = ($x2,$x1) if $x1 > $x2;
443 40 50       81 ($y1,$y2) = ($y2,$y1) if $y1 > $y2;
444              
445             ### rounded: "$x1,$y1 $x2,$y2"
446              
447 40         80 my $arms = $self->{'arms'};
448 40 50 66     263 if (($arms == 1 && $y1 > $x2) # x2,y1 bottom right corner
      66        
      33        
      66        
      33        
449             || ($arms <= 2 && $x2 < 0)
450             || ($arms <= 4 && $y2 < 0)) {
451             ### outside ...
452 0         0 return (1,0);
453             }
454              
455             # arm start 0,1 at X=0,Y=0
456             # 2,3 at X=0,Y=1
457             # 4,5 at X=-1,Y=1
458             # 6,7 at X=-1,Y=1
459             # arms>=6 is arm=5 starting at Y=+1, so 1-$y1
460             # arms>=8 starts at X=-1 so extra +1 for x2 to the right in that case
461 40 100       178 my ($len, $level) =round_down_pow (max ($x2+($arms>=8),
    100          
    100          
462             ($arms >= 2 ? $y2 : ()),
463             ($arms >= 4 ? -$x1 : ()),
464             ($arms >= 6 ? 1-$y1 : ())),
465             2);
466 40         112 return (0, 4*$arms*$len*$len-1);
467             }
468              
469              
470             my @dir4_to_dx = (1,0,-1,0);
471             my @dir4_to_dy = (0,1,0,-1);
472              
473             sub n_to_dxdy {
474 2000     2000 1 37946 my ($self, $n) = @_;
475             ### n_to_dxdy(): $n
476              
477 2000         2730 my $int = int($n);
478 2000         2639 $n -= $int; # $n fraction part
479             ### $int
480             ### $n
481              
482 2000         3797 my $arm = _divrem_mutate ($int, $self->{'arms'});
483             ### $arm
484             ### $int
485              
486             # $dir initial direction from the arm.
487             # $inc +/-1 according to the bit position odd or even, but also odd
488             # numbered arms are transposed so flip them.
489             #
490 2000         3742 my @bits = bit_split_lowtohigh($int);
491 2000         3690 my $dir = ($arm+1) >> 1;
492 2000 100       3795 my $inc = (($#bits ^ $arm) & 1 ? -1 : 1);
493 2000         2825 my $prev = 0;
494              
495             ### @bits
496             ### initial dir: $dir
497             ### initial inc: $inc
498              
499 2000         3177 foreach my $bit (reverse @bits) {
500 15991 100       27087 if ($bit != $prev) {
501 9088         11507 $dir += $inc;
502 9088         12455 $prev = $bit;
503             }
504 15991         22233 $inc = -$inc; # opposite at each bit
505             }
506 2000         2618 $dir &= 3;
507 2000         2956 my $dx = $dir4_to_dx[$dir];
508 2000         2575 my $dy = $dir4_to_dy[$dir];
509             ### $dx
510             ### $dy
511              
512 2000 50       3364 if ($n) {
513             ### apply fraction part: $n
514              
515             # maybe:
516             # +/- $n as dx or dy
517             # +/- (1-$n) as other dy or dx
518              
519             # strip any low 1-bits, and the 0-bit above them
520             # $inc is +1 at an even bit position or -1 at an odd bit position
521 0 0       0 $inc = my $inc = ($arm & 1 ? -1 : 1);
522 0         0 while (shift @bits) {
523 0         0 $inc = -$inc;
524             }
525 0 0       0 if ($bits[0]) { # bit above lowest 0-bit, 1=right,0=left
526 0         0 $inc = -$inc;
527             }
528 0         0 $dir += $inc; # apply turn to give $dir at $n+1
529 0         0 $dir &= 3;
530 0         0 $dx += $n*($dir4_to_dx[$dir] - $dx);
531 0         0 $dy += $n*($dir4_to_dy[$dir] - $dy);
532             }
533              
534             ### result: "$dx, $dy"
535 2000         5286 return ($dx,$dy);
536             }
537              
538             # {
539             # sub print_table {
540             # my ($name, $aref) = @_;
541             # print "my \@$name = (";
542             # my $entry_width = max (map {length($_//'')} @$aref);
543             #
544             # foreach my $i (0 .. $#$aref) {
545             # printf "%*s", $entry_width, $aref->[$i]//'undef';
546             # if ($i == $#$aref) {
547             # print ");\n";
548             # } else {
549             # print ",";
550             # if (($i % 16) == 15
551             # || ($entry_width >= 3 && ($i % 4) == 3)) {
552             # print "\n ".(" " x length($name));
553             # } elsif (($i % 4) == 3) {
554             # print " ";
555             # }
556             # }
557             # }
558             # }
559             #
560             # my @next_state;
561             # my @state_to_dxdy;
562             #
563             # sub make_state {
564             # my %values = @_;
565             # # if ($oddpos) { $rot = ($rot-1)&3; }
566             # my $state = delete $values{'nextturn'};
567             # $state <<= 2; $state |= delete $values{'rot'};
568             # $state <<= 1; $state |= delete $values{'oddpos'};
569             # $state <<= 1; $state |= delete $values{'lowerbit'};
570             # $state <<= 1; $state |= delete $values{'bit'};
571             # die if %values;
572             # return $state;
573             # }
574             # sub state_string {
575             # my ($state) = @_;
576             # my $bit = $state & 1; $state >>= 1;
577             # my $lowerbit = $state & 1; $state >>= 1;
578             # my $oddpos = $state & 1; $state >>= 1;
579             # my $rot = $state & 3; $state >>= 2;
580             # my $nextturn = $state;
581             # # if ($oddpos) { $rot = ($rot+1)&3; }
582             # return "rot=$rot,oddpos=$oddpos nextturn=$nextturn lowerbit=$lowerbit (bit=$bit)";
583             # }
584             #
585             # foreach my $nextturn (0, 1, 2) {
586             # foreach my $rot (0, 1, 2, 3) {
587             # foreach my $oddpos (0, 1) {
588             # foreach my $lowerbit (0, 1) {
589             # foreach my $bit (0, 1) {
590             # my $state = make_state (bit => $bit,
591             # lowerbit => $lowerbit,
592             # rot => $rot,
593             # oddpos => $oddpos,
594             # nextturn => $nextturn);
595             # ### $state
596             #
597             # my $new_nextturn = $nextturn;
598             # my $new_lowerbit = $bit;
599             # my $new_rot = $rot;
600             # my $new_oddpos = $oddpos ^ 1;
601             #
602             # if ($bit != $lowerbit) {
603             # if ($oddpos) {
604             # $new_rot++;
605             # } else {
606             # $new_rot--;
607             # }
608             # $new_rot &= 3;
609             # }
610             # if ($lowerbit == 0 && ! $nextturn) {
611             # $new_nextturn = ($bit ^ $oddpos ? 1 : 2); # bit above lowest 0
612             # }
613             #
614             # my $dx = 1;
615             # my $dy = 0;
616             # if ($rot & 2) {
617             # $dx = -$dx;
618             # $dy = -$dy;
619             # }
620             # if ($rot & 1) {
621             # ($dx,$dy) = (-$dy,$dx); # rotate +90
622             # }
623             # ### rot to: "$dx, $dy"
624             #
625             # # if ($oddpos) {
626             # # ($dx,$dy) = (-$dy,$dx); # rotate +90
627             # # } else {
628             # # ($dx,$dy) = ($dy,-$dx); # rotate -90
629             # # }
630             #
631             # my $next_dx = $dx;
632             # my $next_dy = $dy;
633             # if ($nextturn == 2) {
634             # ($next_dx,$next_dy) = (-$next_dy,$next_dx); # left, rotate +90
635             # } else {
636             # ($next_dx,$next_dy) = ($next_dy,-$next_dx); # right, rotate -90
637             # }
638             # my $frac_dx = $next_dx - $dx;
639             # my $frac_dy = $next_dy - $dy;
640             #
641             # # mask to rot,oddpos only, ignore bit,lowerbit
642             # my $masked_state = $state & ~3;
643             # $state_to_dxdy[$masked_state] = $dx;
644             # $state_to_dxdy[$masked_state + 1] = $dy;
645             # $state_to_dxdy[$masked_state + 2] = $frac_dx;
646             # $state_to_dxdy[$masked_state + 3] = $frac_dy;
647             #
648             # my $next_state = make_state (bit => 0,
649             # lowerbit => $new_lowerbit,
650             # rot => $new_rot,
651             # oddpos => $new_oddpos,
652             # nextturn => $new_nextturn);
653             # $next_state[$state] = $next_state;
654             # }
655             # }
656             # }
657             # }
658             # }
659             #
660             # my @arm_to_state;
661             # foreach my $arm (0 .. 7) {
662             # my $rot = $arm >> 1;
663             # my $oddpos = 0;
664             # if ($arm & 1) {
665             # $rot++;
666             # $oddpos ^= 1;
667             # }
668             # $arm_to_state[$arm] = make_state (bit => 0,
669             # lowerbit => 0,
670             # rot => $rot,
671             # oddpos => $oddpos,
672             # nextturn => 0);
673             # }
674             #
675             # ### @next_state
676             # ### @state_to_dxdy
677             # ### next_state length: 4*(4*2*2 + 4*2)
678             #
679             # print "# next_state length ", scalar(@next_state), "\n";
680             # print_table ("next_state", \@next_state);
681             # print_table ("state_to_dxdy", \@state_to_dxdy);
682             # print_table ("arm_to_state", \@arm_to_state);
683             # print "\n";
684             #
685             # foreach my $arm (0 .. 7) {
686             # print "# arm=$arm ",state_string($arm_to_state[$arm]),"\n";
687             # }
688             # print "\n";
689             #
690             #
691             #
692             # use Smart::Comments;
693             #
694             # sub n_to_dxdy {
695             # my ($self, $n) = @_;
696             # ### n_to_dxdy(): $n
697             #
698             # my $int = int($n);
699             # $n -= $int; # $n fraction part
700             # ### $int
701             # ### $n
702             #
703             # my $state = _divrem_mutate ($int, $self->{'arms'}) << 2;
704             # ### arm as initial state: $state
705             #
706             # foreach my $bit (bit_split_lowtohigh($int)) {
707             # $state = $next_state[$state + $bit];
708             # }
709             # $state &= 0x1C; # mask out "prevbit"
710             #
711             # ### final state: $state
712             # ### dx: $state_to_dxdy[$state]
713             # ### dy: $state_to_dxdy[$state+1],
714             # ### frac dx: $state_to_dxdy[$state+2],
715             # ### frac dy: $state_to_dxdy[$state+3],
716             #
717             # return ($state_to_dxdy[$state] + $n * $state_to_dxdy[$state+2],
718             # $state_to_dxdy[$state+1] + $n * $state_to_dxdy[$state+3]);
719             # }
720             #
721             # }
722              
723             #------------------------------------------------------------------------------
724             # levels
725              
726 2     2   1298 use Math::PlanePath::DragonCurve;
  2         6  
  2         1673  
727             *level_to_n_range = \&Math::PlanePath::DragonCurve::level_to_n_range;
728             *n_to_level = \&Math::PlanePath::DragonCurve::n_to_level;
729              
730             #------------------------------------------------------------------------------
731              
732             sub _UNDOCUMENTED_level_to_right_line_boundary {
733 0     0     my ($self, $level) = @_;
734 0 0         if ($level == 0) {
735 0           return 1;
736             }
737 0           my ($h,$odd) = _divrem($level,2);
738 0 0         return ($odd
739             ? 6 * 2**$h - 4
740             : 2 * 2**$h);
741             }
742             sub _UNDOCUMENTED_level_to_left_line_boundary {
743 0     0     my ($self, $level) = @_;
744 0 0         if ($level == 0) {
745 0           return 1;
746             }
747 0           my ($h,$odd) = _divrem($level,2);
748 0 0         return ($odd
749             ? 2 * 2**$h
750             : 4 * 2**$h - 4);
751             }
752             sub _UNDOCUMENTED_level_to_line_boundary {
753 0     0     my ($self, $level) = @_;
754 0           my ($h,$odd) = _divrem($level,2);
755 0 0         return (($odd?8:6) * 2**$h - 4);
756             }
757              
758             sub _UNDOCUMENTED_level_to_hull_area {
759 0     0     my ($self, $level) = @_;
760 0           return (2**$level - 1)/2;
761             }
762              
763             sub _UNDOCUMENTED__n_is_x_positive {
764 0     0     my ($self, $n) = @_;
765 0 0 0       if (! ($n >= 0) || is_infinite($n)) { return 0; }
  0            
766              
767 0           $n = int($n);
768             {
769 0           my $arm = _divrem_mutate($n, $self->{'arms'});
  0            
770              
771             # arm 1 good only on N=1 which is remaining $n==0
772 0 0         if ($arm == 1) {
773 0           return ($n == 0);
774             }
775              
776             # arm 0 good
777             # arm 8 good for N>=15 which is remaining $n>=1
778 0 0 0       unless ($arm == 0
      0        
779             || ($arm == 7 && $n > 0)) {
780 0           return 0;
781             }
782             }
783              
784 0           return _is_base4_01($n);
785             }
786              
787             sub _UNDOCUMENTED__n_is_diagonal_NE {
788 0     0     my ($self, $n) = @_;
789 0 0 0       if (! ($n >= 0) || is_infinite($n)) { return 0; }
  0            
790              
791 0           $n = int($n);
792 0 0 0       if ($self->{'arms'} >= 8 && $n == 15) { return 1; }
  0            
793 0 0         if (_divrem_mutate($n, $self->{'arms'}) >= 2) { return 0; }
  0            
794 0           return _is_base4_02($n);
795             }
796              
797             # X axis N is base4 digits 0,1
798             # and -1 from even is 0,1 low 0333333
799             # and -2 from even is 0,1 low 0333332
800             # so $n+2 low digit any then 0,1s above
801             sub _UNDOCUMENTED__n_segment_is_right_boundary {
802 0     0     my ($self, $n) = @_;
803 0 0 0       if ($self->{'arms'} >= 8
      0        
804             || ! ($n >= 0)
805             || is_infinite($n)) {
806 0           return 0;
807             }
808 0           $n = int($n);
809              
810 0 0         if (_divrem_mutate($n, $self->{'arms'}) >= 1) {
811 0           return 0;
812             }
813 0           $n += 2;
814 0           _divrem_mutate($n,4);
815 0           return _is_base4_01($n);
816             }
817              
818             # diagonal N is base4 digits 0,2,
819             # and -1 from there is 0,2 low 1
820             # or 0,2 low 13333
821             # so $n+1 low digit possible 1 or 3 then 0,2s above
822             # which means $n+1 low digit any and 0,2s above
823             #use Smart::Comments;
824              
825             sub _UNDOCUMENTED__n_segment_is_left_boundary {
826 0     0     my ($self, $n) = @_;
827             ### _UNDOCUMENTED__n_segment_is_left_boundary(): $n
828              
829 0           my $arms = $self->{'arms'};
830 0 0 0       if ($arms >= 8
      0        
831             || ! ($n >= 0)
832             || is_infinite($n)) {
833 0           return 0;
834             }
835 0           $n = int($n);
836              
837 0 0 0       if (($n == 1 && $arms >= 4)
      0        
      0        
      0        
      0        
838             || ($n == 3 && $arms >= 5)
839             || ($n == 5 && $arms == 7)) {
840 0           return 1;
841             }
842 0 0         if (_divrem_mutate($n, $arms) < $arms-1) {
843             ### no, not last arm ...
844 0           return 0;
845             }
846              
847 0 0         if ($arms % 2) {
848             ### odd arms, stair-step boundary ...
849 0           $n += 1;
850 0           _divrem_mutate($n,4);
851 0           return _is_base4_02($n);
852             } else {
853             # even arms, notched like right boundary
854 0           $n += 2;
855 0           _divrem_mutate($n,4);
856 0           return _is_base4_01($n);
857             }
858             }
859              
860             sub _is_base4_01 {
861 0     0     my ($n) = @_;
862 0           while ($n) {
863 0           my $digit = _divrem_mutate($n,4);
864 0 0         if ($digit >= 2) { return 0; }
  0            
865             }
866 0           return 1;
867             }
868             sub _is_base4_02 {
869 0     0     my ($n) = @_;
870 0           while ($n) {
871 0           my $digit = _divrem_mutate($n,4);
872 0 0 0       if ($digit == 1 || $digit == 3) { return 0; }
  0            
873             }
874 0           return 1;
875             }
876              
877             1;
878             __END__