File Coverage

blib/lib/Math/PlanePath/ComplexMinus.pm
Criterion Covered Total %
statement 74 138 53.6
branch 8 38 21.0
condition 5 6 83.3
subroutine 13 22 59.0
pod 11 11 100.0
total 111 215 51.6


line stmt bran cond sub pod time code
1             # Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 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             # math-image --path=ComplexMinus --lines --scale=10
20             # math-image --path=ComplexMinus --all --output=numbers_dash --size=80x50
21              
22             # Penney numerals in tcl
23             # http://wiki.tcl.tk/10761
24              
25             # cf A003476 = boundary length of i-1 ComplexMinus
26             # is same as DragonCurve single points N=0 to N=2^k inclusive
27              
28             # Mandelbrot "Fractals: Form, Chance and Dimension"
29             # distance along the boundary between any two points is infinite
30              
31             # Fractal Tilings Derived from Complex Bases
32             # Sara Hagey and Judith Palagallo
33             # The Mathematical Gazette
34             # Vol. 85, No. 503 (Jul., 2001), pp. 194-201
35             # Published by: The Mathematical Association
36             # Article Stable URL: http://www.jstor.org/stable/3622004
37              
38             # cf http://szdg.lpds.sztaki.hu/szdg/desc_numsys_es.php
39             # in more than 2 dimensions, by vectors and matrix multiply
40              
41              
42             package Math::PlanePath::ComplexMinus;
43 1     1   9946 use 5.004;
  1         13  
44 1     1   5 use strict;
  1         3  
  1         42  
45 1     1   7 use List::Util 'min';
  1         2  
  1         196  
46             #use List::Util 'max';
47             *max = \&Math::PlanePath::_max;
48              
49 1     1   9 use vars '$VERSION', '@ISA';
  1         1  
  1         73  
50             $VERSION = 129;
51 1     1   747 use Math::PlanePath;
  1         2  
  1         43  
52             @ISA = ('Math::PlanePath');
53              
54             use Math::PlanePath::Base::Generic
55 1         48 'is_infinite',
56 1     1   7 'round_nearest';
  1         2  
57             use Math::PlanePath::Base::Digits
58 1         78 'round_up_pow',
59             'digit_split_lowtohigh',
60 1     1   518 'digit_join_lowtohigh';
  1         2  
61              
62             # uncomment this to run the ### lines
63             # use Smart::Comments;
64              
65              
66 1     1   7 use constant n_start => 0;
  1         2  
  1         69  
67              
68 1         1354 use constant parameter_info_array =>
69             [ { name => 'realpart',
70             display => 'Real Part',
71             type => 'integer',
72             default => 1,
73             minimum => 1,
74             width => 2,
75             description => 'Real part r in the i-r complex base.',
76 1     1   6 } ];
  1         2  
77              
78              
79             sub x_negative_at_n {
80 0     0 1 0 my ($self) = @_;
81 0         0 return $self->{'norm'};
82             }
83             sub y_negative_at_n {
84 0     0 1 0 my ($self) = @_;
85 0         0 return $self->{'norm'} ** 2;
86             }
87              
88             sub absdx_minimum {
89 0     0 1 0 my ($self) = @_;
90 0 0       0 return ($self->{'realpart'} == 1
91             ? 0 # i-1 N=3 dX=0,dY=-3
92             : 1); # i-r otherwise always diff
93             }
94              
95             # realpart=1
96             # dx=1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0 = (6*16^k-2)/15
97             # dy=1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,1 = ((9*16^5-1)/15-1)/2+1
98             # approaches dx=6/15=12/30, dy=9/15/2=9/30
99              
100             # FIXME: are others smaller than East ?
101             sub dir_maximum_dxdy {
102 0     0 1 0 my ($self) = @_;
103 0 0       0 if ($self->{'realpart'} == 1) { return (12,-9); }
  0         0  
104 0         0 else { return (0,0); }
105             }
106              
107             sub turn_any_straight {
108 0     0 1 0 my ($self) = @_;
109 0         0 return ($self->{'realpart'} != 1); # realpart=1 never straight
110             }
111              
112              
113             #------------------------------------------------------------------------------
114             sub new {
115 9     9 1 2019 my $self = shift->SUPER::new(@_);
116              
117 9         24 my $realpart = $self->{'realpart'};
118 9 100 66     48 if (! defined $realpart || $realpart < 1) {
119 3         7 $self->{'realpart'} = $realpart = 1;
120             }
121 9         21 $self->{'norm'} = $realpart*$realpart + 1;
122 9         20 return $self;
123             }
124              
125             sub n_to_xy {
126 140     140 1 14894 my ($self, $n) = @_;
127             ### ComplexMinus n_to_xy(): $n
128              
129 140 50       317 if ($n < 0) { return; }
  0         0  
130 140 50       367 if (is_infinite($n)) { return ($n,$n); }
  0         0  
131              
132             # is this sort of midpoint worthwhile? not documented yet
133             {
134 140         245 my $int = int($n);
  140         195  
135             ### $int
136             ### $n
137 140 50       243 if ($n != $int) {
138 0         0 my ($x1,$y1) = $self->n_to_xy($int);
139 0         0 my ($x2,$y2) = $self->n_to_xy($int+1);
140 0         0 my $frac = $n - $int; # inherit possible BigFloat
141 0         0 my $dx = $x2-$x1;
142 0         0 my $dy = $y2-$y1;
143 0         0 return ($frac*$dx + $x1, $frac*$dy + $y1);
144             }
145 140         197 $n = $int; # BigFloat int() gives BigInt, use that
146             }
147              
148 140         206 my $x = 0;
149 140         188 my $y = 0;
150 140         187 my $dy = ($n * 0); # 0, inherit bignum from $n
151 140         187 my $dx = $dy + 1; # 1, inherit bignum from $n
152 140         253 my $realpart = $self->{'realpart'};
153 140         192 my $norm = $self->{'norm'};
154              
155 140         363 foreach my $digit (digit_split_lowtohigh($n,$norm)) {
156             ### at: "$x,$y digit=$digit"
157              
158 753         1065 $x += $digit * $dx;
159 753         992 $y += $digit * $dy;
160              
161             # multiply i-r, ie. (dx,dy) = (dx + i*dy)*(i-$realpart)
162 753         1260 ($dx,$dy) = (-$dy - $realpart*$dx,
163             $dx - $realpart*$dy);
164             }
165             # GP-Test (dx+I*dy)*(I-'r) == -dy - 'r*dx + I*(dx - 'r*dy)
166              
167             ### final: "$x,$y"
168 140         366 return ($x,$y);
169             }
170              
171             sub xy_to_n {
172 140     140 1 2646 my ($self, $x, $y) = @_;
173             ### ComplexMinus xy_to_n(): "$x, $y"
174              
175 140         308 $x = round_nearest ($x);
176 140         266 $y = round_nearest ($y);
177              
178 140         239 my $realpart = $self->{'realpart'};
179             {
180 140         205 my $rx = $realpart*$x;
  140         193  
181 140         185 my $ry = $realpart*$y;
182 140         288 foreach my $overflow ($rx+$ry, $rx-$ry) {
183 280 50       536 if (is_infinite($overflow)) { return $overflow; }
  0         0  
184             }
185             }
186              
187 140         239 my $norm = $self->{'norm'};
188 140         212 my $zero = ($x * 0 * $y); # inherit bignum 0
189 140         229 my @n; # digits low to high
190              
191 140   100     316 while ($x || $y) {
192 753         1144 my $new_y = $y*$realpart + $x;
193              
194 753         1060 my $digit = $new_y % $norm;
195 753         1110 push @n, $digit;
196              
197 753         960 $x -= $digit;
198 753         925 $new_y = $digit - $new_y;
199              
200             # div i-realpart,
201             # is (i*y + x) * -(i+realpart)/norm
202             # x = [ x*realpart - y ] / -norm
203             # = [ y - x*realpart ] / norm
204             # y = - [ y*realpart + x ] / norm
205             #
206              
207             ### assert: (($y - $x*$realpart) % $norm) == 0
208             ### assert: ($new_y % $norm) == 0
209              
210 753         1992 ($x,$y) = (($y - $x*$realpart) / $norm,
211             $new_y / $norm);
212             }
213 140         385 return digit_join_lowtohigh (\@n, $norm, $zero);
214             }
215              
216             # for i-1 need level=6 to cover 8 points surrounding 0,0
217             # for i-2 and higher level=3 is enough
218              
219             # not exact
220             sub rect_to_n_range {
221 140     140 1 10997 my ($self, $x1,$y1, $x2,$y2) = @_;
222             ### ComplexMinus rect_to_n_range(): "$x1,$y1 $x2,$y2"
223              
224 140         403 my $xm = max(abs($x1),abs($x2));
225 140         313 my $ym = max(abs($y1),abs($y2));
226              
227             return (0,
228             int (($xm*$xm + $ym*$ym)
229 140 100       573 * $self->{'norm'} ** ($self->{'realpart'} > 1
230             ? 4
231             : 8)));
232             }
233              
234             #------------------------------------------------------------------------------
235              
236             sub _UNDOCUMENTED_level_to_figure_boundary {
237 0     0     my ($self, $level) = @_;
238             ### _UNDOCUMENTED_level_to_figure_boundary(): "level=$level realpart=$self->{'realpart'}"
239              
240 0 0         if ($level < 0) { return undef; }
  0            
241 0 0         if (is_infinite($level)) { return $level; }
  0            
242              
243 0           my $b0 = 4;
244 0 0         if ($level == 0) { return $b0; }
  0            
245              
246 0           my $norm = $self->{'norm'};
247 0           my $b1 = 2*$norm + 2;
248 0 0         if ($level == 1) { return $b1; }
  0            
249              
250             # 2*(norm-1)*(realpart + 2) + 4;
251             # = 2*(n*r + 2*n -r - 2) + 4
252             # = 2*n*r + 4n -2r - 4 + 4
253             # = 2*n*r + 4n -2r
254 0           my $realpart = $self->{'realpart'};
255 0           my $b2 = 2*($norm-1)*($realpart + 2) + 4;
256              
257 0           my $f1 = $norm - 2*$realpart;
258 0           my $f2 = 2*$realpart - 1;
259 0           foreach (3 .. $level) {
260 0           ($b2,$b1,$b0) = ($f2*$b2 + $f1*$b1 + $norm*$b0, $b2, $b1);
261             }
262 0           return $b2;
263             }
264              
265             #------------------------------------------------------------------------------
266              
267             {
268             my @table = ('','');
269             # 6-bit blocks per Penney
270             foreach my $i (064,067,060,063, 4,7,0,3) { vec($table[0],$i,1) = 1; }
271             foreach my $i (020,021,034,035, 0,1,014,015) { vec($table[1],$i,1) = 1; }
272              
273             sub _UNDOCUMENTED__n_is_y_axis {
274 0     0     my ($self, $n) = @_;
275 0 0         if (is_infinite($n)) { return 0; }
  0            
276 0 0         if ($n < 0) { return 0; }
  0            
277              
278 0 0         if ($self->{'realpart'} == 1) {
279 0           my $pos = 0;
280 0           foreach my $digit (digit_split_lowtohigh($n,64)) {
281 0 0         unless (vec($table[$pos&1],$digit,1)) {
282             ### bad digit: "pos=$pos digit=$digit"
283 0           return 0;
284             }
285 0           $pos++;
286             }
287             ### good ...
288 0           return 1;
289              
290             } else {
291 0 0         my ($x,$y) = $self->n_to_xy($n)
292             or return 0;
293 0           return $x == 0;
294             }
295             }
296             }
297              
298             #------------------------------------------------------------------------------
299             # levels
300              
301             sub level_to_n_range {
302 0     0 1   my ($self, $level) = @_;
303 0           return (0, $self->{'norm'}**$level - 1);
304             }
305             sub n_to_level {
306 0     0 1   my ($self, $n) = @_;
307 0 0         if ($n < 0) { return undef; }
  0            
308 0 0         if (is_infinite($n)) { return $n; }
  0            
309 0           $n = round_nearest($n);
310 0           my ($pow, $exp) = round_up_pow ($n+1, $self->{'norm'});
311 0           return $exp;
312             }
313              
314              
315             #------------------------------------------------------------------------------
316             1;
317             __END__