File Coverage

blib/lib/Math/PlanePath/QuintetCurve.pm
Criterion Covered Total %
statement 104 126 82.5
branch 37 44 84.0
condition 12 15 80.0
subroutine 12 17 70.5
pod 6 6 100.0
total 171 208 82.2


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             # Boundary of unit squares:
20             # 2*(4*3^n+1) cf A199108 = 4*3^n+1
21             #
22             # QuintetCurve unit squares boundary
23             # 12,28,76,220,652
24             # match 12,28,76,220,652
25             # [HALF]
26             # A079003 a(n) = 4*3^(n-2)+2
27              
28              
29              
30             package Math::PlanePath::QuintetCurve;
31 1     1   10572 use 5.004;
  1         9  
32 1     1   5 use strict;
  1         2  
  1         35  
33              
34 1     1   7 use vars '$VERSION', '@ISA';
  1         2  
  1         67  
35             $VERSION = 128;
36              
37             # inherit: new(), rect_to_n_range(), arms_count(), n_start(),
38             # parameter_info_array(), xy_is_visited()
39 1     1   629 use Math::PlanePath::QuintetCentres;
  1         3  
  1         38  
40 1     1   765 use Math::PlanePath::Base::NSEW;
  1         3  
  1         44  
41             @ISA = ('Math::PlanePath::Base::NSEW',
42             'Math::PlanePath::QuintetCentres');
43              
44 1     1   6 use Math::PlanePath;
  1         2  
  1         34  
45             *_divrem_mutate = \&Math::PlanePath::_divrem_mutate;
46              
47             use Math::PlanePath::Base::Generic
48 1         44 'is_infinite',
49 1     1   5 'round_nearest';
  1         2  
50             use Math::PlanePath::Base::Digits
51 1         1377 'digit_split_lowtohigh',
52 1     1   5 'round_up_pow';
  1         2  
53              
54             # uncomment this to run the ### lines
55             # use Smart::Comments;
56              
57              
58             {
59             my @x_negative_at_n = (undef, 513, 9, 2, 2);
60             sub x_negative_at_n {
61 0     0 1 0 my ($self) = @_;
62 0         0 return $x_negative_at_n[$self->{'arms'}];
63             }
64             }
65             {
66             my @y_negative_at_n = (undef, 2, 4, 6, 3);
67             sub y_negative_at_n {
68 0     0 1 0 my ($self) = @_;
69 0         0 return $y_negative_at_n[$self->{'arms'}];
70             }
71             }
72             {
73             my @_UNDOCUMENTED__dxdy_list_at_n = (undef, 8, 5, 5, 4);
74             sub _UNDOCUMENTED__dxdy_list_at_n {
75 0     0   0 my ($self) = @_;
76 0         0 return $_UNDOCUMENTED__dxdy_list_at_n[$self->{'arms'}];
77             }
78             }
79              
80             # N=4 first straight, then for other arms 18,27,36
81             # must override base Math::PlanePath::QuintetCentres
82             sub _UNDOCUMENTED__turn_any_straight_at_n {
83 0     0   0 my ($self) = @_;
84             # arms=1 4 only first arm has origin 0
85             # arms=2 7
86             # arms=3 10
87             # arms=4 13
88 0         0 return 3*$self->arms_count + 1;
89             }
90              
91              
92             #------------------------------------------------------------------------------
93             my @dir4_to_dx = (1,0,-1,0);
94             my @dir4_to_dy = (0,1,0,-1);
95             my @digit_reverse = (0,1,0,0,1,0);
96              
97             sub n_to_xy {
98 23203     23203 1 73415 my ($self, $n) = @_;
99             ### QuintetCurve n_to_xy(): $n
100              
101 23203 50       43407 if ($n < 0) {
102 0         0 return;
103             }
104 23203 50       44549 if (is_infinite($n)) {
105 0         0 return ($n,$n);
106             }
107              
108 23203         42425 my $arms = $self->{'arms'};
109 23203         31031 my $int = int($n);
110 23203         30408 $n -= $int; # fraction part
111              
112 23203         42414 my $rot = _divrem_mutate ($int,$arms);
113 23203 100       42409 if ($rot) { $int += 1; }
  4086         5703  
114              
115 23203         46911 my @digits = digit_split_lowtohigh($int,5);
116 23203         34254 my @sx;
117             my @sy;
118             {
119 23203         30776 my $sy = 0 * $int; # inherit bignum 0
  23203         33094  
120 23203         33428 my $sx = 1 + $sy; # inherit bignum 1
121 23203         37597 foreach (@digits) {
122 103310         135735 push @sx, $sx;
123 103310         143068 push @sy, $sy;
124              
125             # 2*(sx,sy) + rot+90(sx,sy)
126 103310         173535 ($sx,$sy) = (2*$sx - $sy,
127             2*$sy + $sx);
128             }
129             # ### @digits
130             # my $rev = 0;
131             # for (my $i = $#digits; $i >= 0; $i--) { # high to low
132             # ### digit: $digits[$i]
133             # if ($rev) {
134             # ### reverse: "$digits[$i] to ".(5 - $digits[$i])
135             # $digits[$i] = (5 - $digits[$i]) % 5;
136             # }
137             # # $rev ^= $digit_reverse[$digits[$i]];
138             # ### now rev: $rev
139             }
140             # ### reversed n: @digits
141              
142              
143 23203         32455 my $x = 0;
144 23203         31293 my $y = 0;
145 23203         30823 my $rev = 0;
146              
147 23203         45430 while (defined (my $digit = pop @digits)) { # high to low
148 103310         138697 my $sx = pop @sx;
149 103310         139218 my $sy = pop @sy;
150             ### at: "$x,$y digit $digit side $sx,$sy"
151              
152 103310 100       176598 if ($rot & 2) {
153 37585         60850 ($sx,$sy) = (-$sx,-$sy);
154             }
155 103310 100       165324 if ($rot & 1) {
156 47337         73259 ($sx,$sy) = (-$sy,$sx);
157             }
158              
159 103310 100       154331 if ($rev) {
160 44210 100       87856 if ($digit == 0) {
    100          
    100          
    100          
161 9983         12694 $rev = 0;
162 9983         13197 $rot++;
163              
164             } elsif ($digit == 1) {
165 5668         7423 $x -= $sy;
166 5668         7394 $y += $sx;
167 5668         7409 $rot++;
168              
169             } elsif ($digit == 2) {
170 6249         9092 $x += -2*$sy;
171 6249         8215 $y += 2*$sx;
172              
173             } elsif ($digit == 3) {
174 6225         8965 $x += $sx - 2*$sy; # add 2*rot-90(side) + side
175 6225         8208 $y += $sy + 2*$sx;
176 6225         8244 $rot--;
177 6225         8390 $rev = 0;
178              
179             } else { # $digit == 4
180 16085         21057 $x += $sx - $sy; # add rot-90(side) + side
181 16085         22214 $y += $sy + $sx;
182             }
183              
184             } else {
185             # normal
186              
187 59100 100       115825 if ($digit == 0) {
    100          
    100          
    100          
188              
189             } elsif ($digit == 1) {
190 15466         20360 $x += $sx;
191 15466         19669 $y += $sy;
192 15466         19484 $rot--;
193 15466         20775 $rev = 1;
194              
195             } elsif ($digit == 2) {
196 8924         12409 $x += $sx + $sy; # add side + rot-90(side)
197 8924         12227 $y += $sy - $sx;
198              
199             } elsif ($digit == 3) {
200 8943         12285 $x += 2*$sx + $sy;
201 8943         12232 $y += 2*$sy - $sx;
202 8943         12240 $rot++;
203              
204             } else { # $digit == 4
205 12074         17053 $x += 2*$sx;
206 12074         15513 $y += 2*$sy;
207 12074         15445 $rot++;
208 12074         16789 $rev = 1;
209             }
210             }
211              
212             # lowest non-zero digit determines the direction
213 103310 100       237198 if ($digit != 0) {
214             ### frac_dir at non-zero: $rot
215             }
216             }
217              
218             ### final: "$x,$y"
219             ### $rot
220 23203         31878 $rot &= 3;
221 23203         88020 return ($n * $dir4_to_dx[$rot] + $x,
222             $n * $dir4_to_dy[$rot] + $y);
223             }
224              
225             # up upl left
226             my @attempt_x = (0, 0, -1, -1);
227             my @attempt_y = (0, 1, 1, 0);
228             sub xy_to_n {
229 2562     2562 1 9320 my ($self, $x, $y) = @_;
230             ### QuintetCurve xy_to_n(): "$x, $y"
231              
232 2562         5552 $x = round_nearest($x);
233 2562         5063 $y = round_nearest($y);
234              
235 2562         4232 my ($n, $cx, $cy);
236 2562         4270 foreach my $i (0, 1, 2, 3) {
237 6093 100 66     17320 if (defined ($n = $self->SUPER::xy_to_n($x + $attempt_x[$i],
      100        
      100        
238             $y + $attempt_y[$i]))
239             && (($cx,$cy) = $self->n_to_xy($n))
240             && $x == $cx
241             && $y == $cy) {
242 2562         6535 return $n;
243             }
244             }
245 0         0 return undef;
246             }
247              
248             #------------------------------------------------------------------------------
249             # levels
250              
251             # arms=1 arms=2 arms=3 arms=4
252             # level 0 0..1 = 2 0..2 = 2+1=3 0..3 = 2+1+1=4 0..4 = 2+1+1+1=5
253             # level 1 0..5 = 6 0..10 = 6+5=11 0..15 = 6+5+5=16 0..20 = 6+5+5+5=21
254             # level 2 0..25 = 26 0..50 = 26+25=51 0..75 = 26+25+25=76 0..100 = 26+25+25+25=101
255             # 5^k 2*5^k 3*5^k 4*5^k
256             #
257             sub level_to_n_range {
258 7     7 1 505 my ($self, $level) = @_;
259 7         21 return (0, 5**$level * $self->{'arms'});
260             }
261             sub n_to_level {
262 0     0 1 0 my ($self, $n) = @_;
263 0 0       0 if ($n < 0) { return undef; }
  0         0  
264 0 0       0 if (is_infinite($n)) { return $n; }
  0         0  
265 0         0 $n = round_nearest($n);
266 0         0 $n += $self->{'arms'}-1; # division rounding up
267 0         0 _divrem_mutate ($n, $self->{'arms'});
268 0         0 my ($pow, $exp) = round_up_pow ($n, 5);
269 0         0 return $exp;
270             }
271              
272              
273             #------------------------------------------------------------------------------
274              
275             # R,L,L,S
276             #
277             # forward reverse
278             # 0 forward 0 forward
279             # 1 reverse 1 reverse
280             # 2 forward 2 reverse
281             # 3 forward 3 forward
282             # 4 reverse 4 reverse
283             {
284             # 1 2 3 4
285             my @_UNDOCUMENTED__n_to_turn_LSR = (-1, 1, 1, 0, # forward no low zeros
286             -1, 1, 0, 0, # forward low zeros
287             0,-1,-1, 1, # reverse
288             0, 0,-1, 1);
289             sub _UNDOCUMENTED__n_to_turn_LSR {
290 998     998   13398 my ($self, $n) = @_;
291             ### _UNDOCUMENTED__n_to_turn_LSR(): $n
292              
293 998         1483 $n += $self->{'arms'}-1; # division rounding up
294 998         2786 _divrem_mutate ($n, $self->{'arms'});
295 998 50 33     2481 if ($n < 1 || is_infinite($n)) { return undef; }
  0         0  
296              
297 998         1771 my $any_low_zeros;
298             my $low;
299 998         1727 while ($n) {
300 1244 100       2189 last if ($low = _divrem_mutate($n,5));
301 246         449 $any_low_zeros = 1;
302             }
303             ### $low
304             ### $any_low_zeros
305              
306 998         1438 my $non_two = 0;
307 998         1806 while (($non_two = _divrem_mutate($n,5)) == 2) {}
308             ### $non_two
309              
310 998 100 100     2977 $low = $low - 1
    100          
311             + ($any_low_zeros ? 4 : 0) # low zeros
312             + ($non_two == 1 || $non_two == 4 ? 8 : 0); # reverse
313             ### lookup: $low
314 998         1932 return $_UNDOCUMENTED__n_to_turn_LSR[$low];
315             }
316             }
317              
318              
319             #------------------------------------------------------------------------------
320             1;
321             __END__