File Coverage

blib/lib/Math/PlanePath.pm
Criterion Covered Total %
statement 243 352 69.0
branch 39 108 36.1
condition 19 88 21.5
subroutine 74 94 78.7
pod 40 41 97.5
total 415 683 60.7


line stmt bran cond sub pod time code
1             # Copyright 2010, 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 it
6             # under the terms of the GNU General Public License as published by the Free
7             # 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             package Math::PlanePath;
19 102     102   11365 use 5.004;
  102         390  
20 102     102   553 use strict;
  102         214  
  102         2676  
21              
22 102     102   553 use vars '$VERSION';
  102         255  
  102         6576  
23             $VERSION = 129;
24              
25             # uncomment this to run the ### lines
26             # use Smart::Comments;
27              
28              
29             # defaults
30 102     102   729 use constant figure => 'square';
  102         259  
  102         11472  
31 102     102   697 use constant default_n_start => 1;
  102         218  
  102         17055  
32             sub n_start {
33 398     398 1 2861 my ($self) = @_;
34 398 100 66     1977 if (ref $self && defined $self->{'n_start'}) {
35 374         1264 return $self->{'n_start'};
36             } else {
37 24         332 return $self->default_n_start;
38             }
39             }
40             sub arms_count {
41 7659     7659 1 12161 my ($self) = @_;
42 7659   100     25927 return $self->{'arms'} || 1;
43             }
44              
45 102     102   778 use constant class_x_negative => 1;
  102         223  
  102         10299  
46 102     102   670 use constant class_y_negative => 1;
  102         313  
  102         10714  
47 92     92 1 7792 sub x_negative { $_[0]->class_x_negative }
48 347     347 1 2869 sub y_negative { $_[0]->class_y_negative }
49 102     102   845 use constant x_negative_at_n => undef;
  102         233  
  102         9595  
50 102     102   911 use constant y_negative_at_n => undef;
  102         194  
  102         7525  
51 102     102   3243 use constant n_frac_discontinuity => undef;
  102         2364  
  102         11418  
52              
53 102     102   690 use constant parameter_info_array => [];
  102         233  
  102         18737  
54             sub parameter_info_list {
55 65     65 1 3333 return @{$_[0]->parameter_info_array};
  65         568  
56             }
57              
58             # x_negative(),y_negative() existed before x_minimum(),y_minimum(), so
59             # default x_minimum(),y_minimum() from those.
60             sub x_minimum {
61 2     2 1 8 my ($self) = @_;
62 2 100       7 return ($self->x_negative ? undef : 0);
63             }
64             sub y_minimum {
65 6     6 1 17 my ($self) = @_;
66 6 100       15 return ($self->y_negative ? undef : 0);
67             }
68 102     102   753 use constant x_maximum => undef;
  102         2183  
  102         7706  
69 102     102   725 use constant y_maximum => undef;
  102         220  
  102         13573  
70              
71             sub sumxy_minimum {
72 2     2 1 6 my ($self) = @_;
73             ### PlanePath sumxy_minimum() ...
74 2 50 33     6 if (defined (my $x_minimum = $self->x_minimum)
75             && defined (my $y_minimum = $self->y_minimum)) {
76             ### $x_minimum
77             ### $y_minimum
78 2         10 return $x_minimum + $y_minimum;
79             }
80 0         0 return undef;
81             }
82 102     102   734 use constant sumxy_maximum => undef;
  102         223  
  102         18120  
83              
84             sub sumabsxy_minimum {
85 0     0 1 0 my ($self) = @_;
86 0         0 my $x_minimum = $self->x_minimum;
87 0         0 my $y_minimum = $self->y_minimum;
88 0 0 0     0 if (defined $x_minimum && $x_minimum >= 0
      0        
      0        
89             && defined $y_minimum && $y_minimum >= 0) {
90             # X>=0 and Y>=0 so abs(X)+abs(Y) == X+Y
91 0         0 return $self->sumxy_minimum;
92             }
93 0   0     0 return _max($x_minimum||0,0) + _max($y_minimum||0,0);
      0        
94             }
95 102     102   2820 use constant sumabsxy_maximum => undef;
  102         214  
  102         6296  
96              
97 102     102   2841 use constant diffxy_minimum => undef;
  102         279  
  102         42830  
98             #
99             # If the path is confined to the fourth quadrant, so X>=something and
100             # Y<=something then a minimum X-Y exists. But fourth-quadrant-only path is
101             # unusual, so don't bother with code checking that.
102             # sub diffxy_minimum {
103             # my ($self) = @_;
104             # if (defined (my $y_maximum = $self->y_maximum)
105             # && defined (my $x_minimum = $self->x_minimum)) {
106             # return $x_minimum - $y_maximum;
107             # } else {
108             # return undef;
109             # }
110             # }
111              
112             # If the path is confined to the second quadrant, so X<=something and
113             # Y>=something, then has a maximum X-Y. Presume that the x_maximum() and
114             # y_minimum() occur together.
115             #
116             sub diffxy_maximum {
117 0     0 1 0 my ($self) = @_;
118 0 0 0     0 if (defined (my $y_minimum = $self->y_minimum)
119             && defined (my $x_max = $self->x_maximum)) {
120 0         0 return $x_max - $y_minimum;
121             } else {
122 0         0 return undef;
123             }
124             }
125              
126             # absdiffxy = abs(X-Y)
127             sub absdiffxy_minimum {
128 0     0 1 0 my ($self) = @_;
129             # if X-Y all one sign, so X-Y>=0 or X-Y<=0, then abs(X-Y) from that
130 0         0 my $m;
131 0 0 0     0 if (defined($m = $self->diffxy_minimum) && $m >= 0) {
132 0         0 return $m;
133             }
134 0 0 0     0 if (defined($m = $self->diffxy_maximum) && $m <= 0) {
135 0         0 return - $m;
136             }
137 0         0 return 0;
138             }
139             sub absdiffxy_maximum {
140 0     0 1 0 my ($self) = @_;
141             # if X-Y constrained so min<=X-Y<=max then max abs(X-Y) one of the two ends
142 0 0 0     0 if (defined (my $min = $self->diffxy_minimum)
143             && defined (my $max = $self->diffxy_maximum)) {
144 0         0 return _max(abs($min),abs($max));
145             }
146 0         0 return undef;
147             }
148              
149              
150             # experimental default from x_minimum(),y_minimum()
151             # FIXME: should use absx_minimum, absy_minimum, for paths outside first quadrant
152             sub rsquared_minimum {
153 0     0 1 0 my ($self) = @_;
154              
155             # The X and Y each closest to the origin. This assumes that point is
156             # actually visited, but is likely to be close.
157 0         0 my $x_minimum = $self->x_minimum;
158 0         0 my $x_maximum = $self->x_maximum;
159 0         0 my $y_minimum = $self->y_minimum;
160 0         0 my $y_maximum = $self->y_maximum;
161 0 0 0     0 my $x = (( defined $x_minimum && $x_minimum) > 0 ? $x_minimum
    0 0        
162             : (defined $x_maximum && $x_maximum) < 0 ? $x_maximum
163             : 0);
164 0 0 0     0 my $y = (( defined $y_minimum && $y_minimum) > 0 ? $y_minimum
    0 0        
165             : (defined $y_maximum && $y_maximum) < 0 ? $y_maximum
166             : 0);
167 0         0 return ($x*$x + $y*$y);
168              
169             # # Maybe initial point $self->n_to_xy($self->n_start)) as the default,
170             # # but that's not the minimum on "wider" paths.
171             # return 0;
172             }
173 102     102   796 use constant rsquared_maximum => undef;
  102         265  
  102         17318  
174              
175             sub gcdxy_minimum {
176 1     1 0 5 my ($self) = @_;
177             ### gcdxy_minimum(): "visited=".($self->xy_is_visited(0,0)||0)
178 1 50       26 return ($self->xy_is_visited(0,0)
179             ? 0 # gcd(0,0)=0
180             : 1); # any other has gcd>=1
181             }
182 102     102   823 use constant gcdxy_maximum => undef;
  102         2559  
  102         5520  
183              
184 102     102   660 use constant turn_any_left => 1;
  102         727  
  102         7134  
185 102     102   692 use constant turn_any_right => 1;
  102         240  
  102         7122  
186 102     102   636 use constant turn_any_straight => 1;
  102         258  
  102         5404  
187              
188              
189             #------------------------------------------------------------------------------
190              
191 102     102   626 use constant dir_minimum_dxdy => (1,0); # East
  102         195  
  102         6692  
192 102     102   2829 use constant dir_maximum_dxdy => (0,0); # supremum all angles
  102         211  
  102         7597  
193              
194 102     102   2591 use constant dx_minimum => undef;
  102         233  
  102         7219  
195 102     102   650 use constant dy_minimum => undef;
  102         260  
  102         7271  
196 102     102   668 use constant dx_maximum => undef;
  102         212  
  102         7152  
197 102     102   2607 use constant dy_maximum => undef;
  102         238  
  102         7608  
198             #
199             # =item C<$n = $path-E_UNDOCUMENTED__dxdy_list_at_n()>
200             #
201             # Return the N at which all possible dX,dY will have been seen. If there is
202             # not a finite set of possible dX,dY steps then return C.
203             #
204 102     102   5045 use constant 1.02;
  102         2188  
  102         4941  
205 102     102   549 use constant _UNDOCUMENTED__dxdy_list => (); # default empty for not a finite list
  102         195  
  102         7158  
206 102     102   661 use constant _UNDOCUMENTED__dxdy_list_at_n => undef; # maybe dxdy_at_n()
  102         206  
  102         8692  
207 102         7866 use constant _UNDOCUMENTED__dxdy_list_three => (2,0, # E
208             -1,1, # NW
209 102     102   2323 -1,-1); # SW
  102         177  
210 102         8195 use constant _UNDOCUMENTED__dxdy_list_six => (2,0, # E
211             1,1, # NE
212             -1,1, # NW
213             -2,0, # W
214             -1,-1, # SW
215 102     102   708 1,-1); # SE
  102         2253  
216 102         33868 use constant _UNDOCUMENTED__dxdy_list_eight => (1,0, # E
217             1,1, # NE
218             0,1, # N
219             -1,1, # NW
220             -1,0, # W
221             -1,-1, # SW
222             0,-1, # S
223 102     102   660 1,-1); # SE
  102         232  
224              
225             sub absdx_minimum {
226 0     0 1 0 my ($self) = @_;
227             # If dX>=0 then abs(dX)=dX always and absdx_minimum()==dx_minimum().
228             # This happens for column style paths like CoprimeColumns.
229             # dX>0 is only for line paths so not very interesting.
230 0 0       0 if (defined (my $dx_minimum = $self->dx_minimum)) {
231 0 0       0 if ($dx_minimum >= 0) { return $dx_minimum; }
  0         0  
232             }
233 0         0 return 0;
234             }
235             sub absdx_maximum {
236 0     0 1 0 my ($self) = @_;
237 0 0 0     0 if (defined (my $dx_minimum = $self->dx_minimum)
238             && defined (my $dx_maximum = $self->dx_maximum)) {
239 0         0 return _max(abs($dx_minimum),abs($dx_maximum));
240             }
241 0         0 return undef;
242             }
243              
244             sub absdy_minimum {
245 0     0 1 0 my ($self) = @_;
246             # if dY>=0 then abs(dY)=dY always and absdy_minimum()==dy_minimum()
247 0 0       0 if (defined (my $dy_minimum = $self->dy_minimum)) {
248 0 0       0 if ($dy_minimum >= 0) { return $dy_minimum; }
  0         0  
249             }
250 0         0 return 0;
251             }
252             sub absdy_maximum {
253 0     0 1 0 my ($self) = @_;
254 0 0 0     0 if (defined (my $dy_minimum = $self->dy_minimum)
255             && defined (my $dy_maximum = $self->dy_maximum)) {
256 0         0 return _max(abs($dy_minimum),abs($dy_maximum));
257             } else {
258 0         0 return undef;
259             }
260             }
261              
262 102     102   788 use constant dsumxy_minimum => undef;
  102         228  
  102         7545  
263 102     102   640 use constant dsumxy_maximum => undef;
  102         220  
  102         4786  
264 102     102   624 use constant ddiffxy_minimum => undef;
  102         219  
  102         4956  
265 102     102   619 use constant ddiffxy_maximum => undef;
  102         235  
  102         105312  
266              
267             #------------------------------------------------------------------------------
268              
269             sub new {
270 1972     1972 1 36380 my $class = shift;
271 1972         8366 return bless { @_ }, $class;
272             }
273              
274             {
275             my %parameter_info_hash;
276             sub parameter_info_hash {
277 3     3 1 150 my ($class_or_self) = @_;
278 3   33     23 my $class = (ref $class_or_self || $class_or_self);
279             return ($parameter_info_hash{$class}
280 3   50     38 ||= { map { $_->{'name'} => $_ }
  4         30  
281             $class_or_self->parameter_info_list });
282             }
283             }
284              
285             sub xy_to_n_list {
286             ### xy_to_n_list() ...
287 9404 100   9404 1 21730 if (defined (my $n = shift->xy_to_n(@_))) {
288             ### $n
289 5028         12387 return $n;
290             }
291             ### empty ...
292 4376         11694 return;
293             }
294             sub xy_is_visited {
295 7     7 1 726 my ($self, $x, $y) = @_;
296             ### xy_is_visited(): "$x,$y is ndefined=".defined($self->xy_to_n($x,$y))
297 7         18 return defined($self->xy_to_n($x,$y));
298             }
299              
300             sub n_to_n_list {
301 0     0 1 0 my ($self, $n) = @_;
302 0 0       0 my ($x,$y) = $self->n_to_xy($n) or return;
303 0         0 return $self->xy_to_n_list($x,$y);
304             }
305              
306             sub n_to_dxdy {
307 5155     5155 1 103752 my ($self, $n) = @_;
308             ### n_to_dxdy(): $n
309 5155 100       10177 my ($x,$y) = $self->n_to_xy ($n)
310             or return;
311 5151 50       11467 my ($next_x,$next_y) = $self->n_to_xy ($n + $self->arms_count)
312             or return;
313             ### points: "$x,$y $next_x,$next_y"
314 5151         11859 return ($next_x - $x,
315             $next_y - $y);
316             }
317             sub n_to_rsquared {
318 31     31 1 68 my ($self, $n) = @_;
319 31 50       73 my ($x,$y) = $self->n_to_xy($n) or return undef;
320 31         105 return $x*$x + $y*$y;
321             }
322             sub n_to_radius {
323 0     0 1 0 my ($self, $n) = @_;
324 0         0 my $rsquared = $self->n_to_rsquared($n);
325 0 0       0 return (defined $rsquared ? sqrt($rsquared) : undef);
326             }
327              
328             sub xyxy_to_n_list {
329 8     8 1 319 my ($self, $x1,$y1, $x2,$y2) = @_;
330 8 50       30 my @n1 = $self->xy_to_n_list($x1,$y1) or return;
331 8 50       32 my @n2 = $self->xy_to_n_list($x2,$y2) or return;
332 8         19 my $arms = $self->arms_count;
333 8         18 return grep { my $want_n2 = $_ + $arms;
  11         18  
334 11         15 grep {$_ == $want_n2} @n2 # seek $n2 which is this $n1+$arms
  15         46  
335             } @n1;
336             }
337             sub xyxy_to_n {
338 6     6 1 653 my $self = shift;
339 6         18 my @n_list = $self->xyxy_to_n_list(@_);
340 6         19 return $n_list[0];
341             }
342              
343             sub xyxy_to_n_list_either {
344 6258     6258 1 11344 my ($self, $x1,$y1, $x2,$y2) = @_;
345 6258 100       10733 my @n1 = $self->xy_to_n_list($x1,$y1) or return;
346 3133 100       6102 my @n2 = $self->xy_to_n_list($x2,$y2) or return;
347 1882         4187 my $arms = $self->arms_count;
348 1882         2802 my @n_list;
349 1882         3028 foreach my $n1 (@n1) {
350 1885         2691 foreach my $n2 (@n2) {
351 1888 50       3668 if (abs($n1 - $n2) == $arms) {
352 1888         3262 push @n_list, _min($n1,$n2);
353             }
354             }
355             }
356 1882         3908 @n_list = sort {$a<=>$b} @n_list;
  6         14  
357 1882         3921 return @n_list;
358             }
359             sub xyxy_to_n_either {
360 6256     6256 1 32285 my $self = shift;
361 6256         11498 my @n_list = $self->xyxy_to_n_list_either(@_);
362 6256         13405 return $n_list[0];
363             }
364              
365             #------------------------------------------------------------------------------
366             # turns
367              
368             sub _UNDOCUMENTED__n_to_turn_LSR {
369 16     16   229 my ($self, $n) = @_;
370             ### _UNDOCUMENTED__n_to_turn_LSR(): $n
371              
372 16 100       47 my ($dx,$dy) = $self->n_to_dxdy($n - $self->arms_count)
373             or return undef;
374 8 50       26 my ($next_dx,$next_dy) = $self->n_to_dxdy($n)
375             or return undef;
376              
377             ### dxdy: "$dx,$dy and $next_dx,$next_dy arms=".$self->arms_count
378              
379 8   100     59 return (($next_dy * $dx <=> $next_dx * $dy) || 0); # 1,0,-1
380             }
381              
382             #------------------------------------------------------------------------------
383             # tree
384              
385             sub is_tree {
386 0     0 1 0 my ($self) = @_;
387 0         0 return $self->tree_n_num_children($self->n_start);
388             }
389              
390 102     102   820 use constant tree_n_parent => undef; # default always no parent
  102         253  
  102         8152  
391              
392 102     102   678 use constant tree_n_children => (); # default no children
  102         228  
  102         11678  
393             sub tree_n_num_children {
394 0     0 1 0 my ($self, $n) = @_;
395 0 0       0 if ($n >= $self->n_start) {
396 0         0 my @n_list = $self->tree_n_children($n);
397 0         0 return scalar(@n_list);
398             } else {
399 0         0 return undef;
400             }
401             }
402              
403             # For non-trees n_num_children() always returns 0 so that's the single
404             # return here.
405 102     102   773 use constant tree_num_children_list => (0);
  102         238  
  102         16805  
406             sub tree_num_children_minimum {
407 2     2 1 14 my ($self) = @_;
408 2         6 return ($self->tree_num_children_list)[0];
409             }
410             sub tree_num_children_maximum {
411 2     2 1 4 my ($self) = @_;
412 2         7 return ($self->tree_num_children_list)[-1];
413             }
414             sub tree_any_leaf {
415 0     0 1 0 my ($self) = @_;
416 0         0 return ($self->tree_num_children_minimum == 0);
417             }
418              
419 102     102   786 use constant tree_n_to_subheight => 0; # default all leaf node
  102         245  
  102         6006  
420              
421 102     102   699 use constant tree_n_to_depth => undef;
  102         194  
  102         5024  
422 102     102   633 use constant tree_depth_to_n => undef;
  102         274  
  102         49101  
423             sub tree_depth_to_n_end {
424 117     117 1 327 my ($self, $depth) = @_;
425 117 50 33     401 if ($depth >= 0
426             && defined (my $n = $self->tree_depth_to_n($depth+1))) {
427             ### tree_depth_to_n_end(): $depth, $n
428 117         300 return $n-1;
429             } else {
430 0         0 return undef;
431             }
432             }
433             sub tree_depth_to_n_range {
434 0     0 1 0 my ($self, $depth) = @_;
435 0 0 0     0 if (defined (my $n = $self->tree_depth_to_n($depth))
436             && defined (my $n_end = $self->tree_depth_to_n_end($depth))) {
437 0         0 return ($n, $n_end);
438             }
439 0         0 return;
440             }
441              
442             sub tree_depth_to_width {
443 0     0 1 0 my ($self, $depth) = @_;
444 0 0 0     0 if (defined (my $n = $self->tree_depth_to_n($depth))
445             && defined (my $n_end = $self->tree_depth_to_n_end($depth))) {
446 0         0 return $n_end - $n + 1;
447             }
448 0         0 return undef;
449             }
450              
451             sub tree_num_roots {
452 0     0 1 0 my ($self) = @_;
453 0         0 my @root_n_list = $self->tree_root_n_list;
454 0         0 return scalar(@root_n_list);
455             }
456             sub tree_root_n_list {
457 0     0 1 0 my ($self) = @_;
458 0         0 my $n_start = $self->n_start;
459 0         0 my @ret;
460 0         0 for (my $n = $n_start; ; $n++) {
461             # stop on finding a non-root (has a parent), or a non-tree path has no
462             # children at all
463 0 0 0     0 if (defined($self->tree_n_parent($n))
464             || ! $self->tree_n_num_children($n)) {
465 0         0 last;
466             }
467 0         0 push @ret, $n;
468             }
469 0         0 return @ret;
470             }
471              
472             # Generic search upwards. Not fast, but works with past Toothpick or
473             # anything slack which doesn't have own tree_n_root(). When only one root
474             # there's no search.
475             sub tree_n_root {
476 0     0 1 0 my ($self, $n) = @_;
477 0         0 my $num_roots = $self->tree_num_roots;
478 0 0       0 if ($num_roots == 0) {
479 0         0 return undef; # not a tree
480             }
481 0         0 my $n_start = $self->n_start;
482 0 0       0 unless ($n >= $n_start) { # and warn if $n==undef
483 0         0 return undef; # -inf or NaN
484             }
485 0 0       0 if ($num_roots == 1) {
486 0         0 return $n_start; # only one root, no search
487             }
488              
489 0         0 for (;;) {
490 0         0 my $n_parent = $self->tree_n_parent($n);
491 0 0       0 if (! defined $n_parent) {
492 0         0 return $n; # found root
493             }
494 0 0       0 unless ($n_parent < $n) {
495 0         0 return undef; # +inf or something bad not making progress
496             }
497 0         0 $n = $n_parent;
498             }
499             }
500              
501             # Generic search for where no more children.
502             # But must watch out for infinite lets, and might also watch out for
503             # rounding or overflow.
504             #
505             # sub path_tree_n_to_subheight {
506             # my ($path, $n) = @_;
507             # ### path_tree_n_to_subheight(): "$n"
508             #
509             # if (is_infinite($n)) {
510             # return $n;
511             # }
512             # my $max = $path->tree_n_to_depth($n) + 10;
513             # my @n = ($n);
514             # my $height = 0;
515             # do {
516             # @n = map {$path->tree_n_children($_)} @n
517             # or return $height;
518             # $height++;
519             # } while (@n && $height < $max);
520             #
521             # ### height infinite ...
522             # return undef;
523             # }
524              
525             #------------------------------------------------------------------------------
526             # levels
527              
528 102     102   901 use constant level_to_n_range => ();
  102         261  
  102         5795  
529 102     102   682 use constant n_to_level => undef;
  102         217  
  102         28575  
530              
531              
532             #------------------------------------------------------------------------------
533             # shared undocumented internals
534              
535             sub _max {
536 78698     78698   131588 my $max = 0;
537 78698         142456 foreach my $i (1 .. $#_) {
538 180600 100       365818 if ($_[$i] > $_[$max]) {
539 51866         79791 $max = $i;
540             }
541             }
542 78698         175826 return $_[$max];
543             }
544             sub _min {
545 62062     62062   293762 my $min = 0;
546 62062         115248 foreach my $i (1 .. $#_) {
547 161423 100       296584 if ($_[$i] < $_[$min]) {
548 25617         39024 $min = $i;
549             }
550             }
551 62062         113697 return $_[$min];
552             }
553             # Return square root of $x, rounded towards zero.
554             # Recent BigFloat and BigRat need explicit conversion to BigInt, they no
555             # longer do that in int().
556             sub _sqrtint {
557 167772     167772   250667 my ($x) = @_;
558 167772 100       280935 if (ref $x) {
559 16 100 66     121 if ($x->isa('Math::BigFloat') || $x->isa('Math::BigRat')) {
560 5         50 $x = $x->copy->as_int;
561             }
562             }
563 167772         341308 return int(sqrt($x));
564             }
565              
566 102     102   56869 use Math::PlanePath::Base::Generic 'round_nearest';
  102         264  
  102         37708  
567             sub _rect_for_first_quadrant {
568 0     0   0 my ($self, $x1,$y1, $x2,$y2) = @_;
569 0         0 $x1 = round_nearest($x1);
570 0         0 $y1 = round_nearest($y1);
571 0         0 $x2 = round_nearest($x2);
572 0         0 $y2 = round_nearest($y2);
573 0 0       0 ($x1,$x2) = ($x2,$x1) if $x1 > $x2;
574 0 0       0 ($y1,$y2) = ($y2,$y1) if $y1 > $y2;
575 0 0 0     0 if ($x2 < 0 || $y2 < 0) {
576 0         0 return;
577             }
578 0         0 return ($x1,$y1, $x2,$y2);
579             }
580              
581             # return ($quotient, $remainder)
582             sub _divrem {
583 85813     85813   136258 my ($n, $d) = @_;
584 85813 100 66     157935 if (ref $n && $n->isa('Math::BigInt')) {
585 1008         2703 my ($quot,$rem) = $n->copy->bdiv($d);
586 1008 50 66     212287 if (! ref $d || $d < 1_000_000) {
587 1008         59071 $rem = $rem->numify; # plain remainder if fits
588             }
589 1008         25284 return ($quot, $rem);
590             }
591 84805         123758 my $rem = $n % $d;
592 84805         187968 return (int(($n-$rem)/$d), # exact division stays in UV
593             $rem);
594             }
595              
596             # return $remainder, modify $n
597             # the scalar $_[0] is modified, but if it's a BigInt then a new BigInt is made
598             # and stored there, the bigint value is not changed
599             sub _divrem_mutate {
600 148333     148333   202506 my $d = $_[1];
601 148333         188285 my $rem;
602 148333 100 66     299752 if (ref $_[0] && $_[0]->isa('Math::BigInt')) {
603 1         16 ($_[0], $rem) = $_[0]->copy->bdiv($d); # quot,rem in array context
604 1 50 33     250 if (! ref $d || $d < 1_000_000) {
605 1         4 return $rem->numify; # plain remainder if fits
606             }
607             } else {
608 148332         206498 $rem = $_[0] % $d;
609 148332         253491 $_[0] = int(($_[0]-$rem)/$d); # exact division stays in UV
610             }
611 148332         262249 return $rem;
612             }
613              
614             1;
615             __END__