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 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 101     101   11007 use 5.004;
  101         345  
20 101     101   510 use strict;
  101         205  
  101         2522  
21              
22 101     101   568 use vars '$VERSION';
  101         191  
  101         5945  
23             $VERSION = 128;
24              
25             # uncomment this to run the ### lines
26             # use Smart::Comments;
27              
28              
29             # defaults
30 101     101   686 use constant figure => 'square';
  101         208  
  101         10009  
31 101     101   651 use constant default_n_start => 1;
  101         218  
  101         15647  
32             sub n_start {
33 376     376 1 2585 my ($self) = @_;
34 376 100 66     1996 if (ref $self && defined $self->{'n_start'}) {
35 352         1219 return $self->{'n_start'};
36             } else {
37 24         348 return $self->default_n_start;
38             }
39             }
40             sub arms_count {
41 7522     7522 1 11826 my ($self) = @_;
42 7522   100     23239 return $self->{'arms'} || 1;
43             }
44              
45 101     101   742 use constant class_x_negative => 1;
  101         206  
  101         7272  
46 101     101   781 use constant class_y_negative => 1;
  101         1785  
  101         9766  
47 91     91 1 8472 sub x_negative { $_[0]->class_x_negative }
48 346     346 1 2690 sub y_negative { $_[0]->class_y_negative }
49 101     101   822 use constant x_negative_at_n => undef;
  101         259  
  101         8121  
50 101     101   910 use constant y_negative_at_n => undef;
  101         205  
  101         4810  
51 101     101   1890 use constant n_frac_discontinuity => undef;
  101         236  
  101         11798  
52              
53 101     101   2299 use constant parameter_info_array => [];
  101         243  
  101         18501  
54             sub parameter_info_list {
55 64     64 1 3532 return @{$_[0]->parameter_info_array};
  64         534  
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 6 my ($self) = @_;
62 2 100       7 return ($self->x_negative ? undef : 0);
63             }
64             sub y_minimum {
65 6     6 1 16 my ($self) = @_;
66 6 100       12 return ($self->y_negative ? undef : 0);
67             }
68 101     101   700 use constant x_maximum => undef;
  101         203  
  101         7109  
69 101     101   610 use constant y_maximum => undef;
  101         207  
  101         14169  
70              
71             sub sumxy_minimum {
72 2     2 1 4 my ($self) = @_;
73             ### PlanePath sumxy_minimum() ...
74 2 50 33     5 if (defined (my $x_minimum = $self->x_minimum)
75             && defined (my $y_minimum = $self->y_minimum)) {
76             ### $x_minimum
77             ### $y_minimum
78 2         7 return $x_minimum + $y_minimum;
79             }
80 0         0 return undef;
81             }
82 101     101   690 use constant sumxy_maximum => undef;
  101         200  
  101         16641  
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 101     101   709 use constant sumabsxy_maximum => undef;
  101         202  
  101         7243  
96              
97 101     101   676 use constant diffxy_minimum => undef;
  101         236  
  101         40141  
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 101     101   763 use constant rsquared_maximum => undef;
  101         222  
  101         14250  
174              
175             sub gcdxy_minimum {
176 1     1 0 3 my ($self) = @_;
177             ### gcdxy_minimum(): "visited=".($self->xy_is_visited(0,0)||0)
178 1 50       11 return ($self->xy_is_visited(0,0)
179             ? 0 # gcd(0,0)=0
180             : 1); # any other has gcd>=1
181             }
182 101     101   673 use constant gcdxy_maximum => undef;
  101         400  
  101         6679  
183              
184 101     101   642 use constant turn_any_left => 1;
  101         657  
  101         6227  
185 101     101   624 use constant turn_any_right => 1;
  101         227  
  101         5992  
186 101     101   587 use constant turn_any_straight => 1;
  101         188  
  101         4903  
187              
188              
189             #------------------------------------------------------------------------------
190              
191 101     101   602 use constant dir_minimum_dxdy => (1,0); # East
  101         193  
  101         5990  
192 101     101   601 use constant dir_maximum_dxdy => (0,0); # supremum all angles
  101         244  
  101         6829  
193              
194 101     101   2369 use constant dx_minimum => undef;
  101         251  
  101         8233  
195 101     101   587 use constant dy_minimum => undef;
  101         190  
  101         6450  
196 101     101   621 use constant dx_maximum => undef;
  101         220  
  101         4538  
197 101     101   2411 use constant dy_maximum => undef;
  101         208  
  101         8638  
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 101     101   2332 use constant 1.02;
  101         5899  
  101         2879  
205 101     101   516 use constant _UNDOCUMENTED__dxdy_list => (); # default empty for not a finite list
  101         199  
  101         4926  
206 101     101   2251 use constant _UNDOCUMENTED__dxdy_list_at_n => undef; # maybe dxdy_at_n()
  101         197  
  101         7811  
207 101         7221 use constant _UNDOCUMENTED__dxdy_list_three => (2,0, # E
208             -1,1, # NW
209 101     101   2395 -1,-1); # SW
  101         180  
210 101         9052 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 101     101   637 1,-1); # SE
  101         201  
216 101         30687 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 101     101   614 1,-1); # SE
  101         216  
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 101     101   721 use constant dsumxy_minimum => undef;
  101         221  
  101         5112  
263 101     101   650 use constant dsumxy_maximum => undef;
  101         186  
  101         6206  
264 101     101   559 use constant ddiffxy_minimum => undef;
  101         191  
  101         4699  
265 101     101   573 use constant ddiffxy_maximum => undef;
  101         206  
  101         93236  
266              
267             #------------------------------------------------------------------------------
268              
269             sub new {
270 1929     1929 1 35472 my $class = shift;
271 1929         7368 return bless { @_ }, $class;
272             }
273              
274             {
275             my %parameter_info_hash;
276             sub parameter_info_hash {
277 3     3 1 121 my ($class_or_self) = @_;
278 3   33     19 my $class = (ref $class_or_self || $class_or_self);
279             return ($parameter_info_hash{$class}
280 3   50     16 ||= { map { $_->{'name'} => $_ }
  4         25  
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 22654 if (defined (my $n = shift->xy_to_n(@_))) {
288             ### $n
289 5028         12132 return $n;
290             }
291             ### empty ...
292 4376         11916 return;
293             }
294             sub xy_is_visited {
295 7     7 1 672 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 5008     5008 1 103109 my ($self, $n) = @_;
308             ### n_to_dxdy(): $n
309 5008 100       9272 my ($x,$y) = $self->n_to_xy ($n)
310             or return;
311 5004 50       10719 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 5004         11785 return ($next_x - $x,
315             $next_y - $y);
316             }
317             sub n_to_rsquared {
318 31     31 1 56 my ($self, $n) = @_;
319 31 50       64 my ($x,$y) = $self->n_to_xy($n) or return undef;
320 31         82 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 738 my ($self, $x1,$y1, $x2,$y2) = @_;
330 8 50       25 my @n1 = $self->xy_to_n_list($x1,$y1) or return;
331 8 50       34 my @n2 = $self->xy_to_n_list($x2,$y2) or return;
332 8         19 my $arms = $self->arms_count;
333 8         15 return grep { my $want_n2 = $_ + $arms;
  11         18  
334 11         13 grep {$_ == $want_n2} @n2 # seek $n2 which is this $n1+$arms
  15         42  
335             } @n1;
336             }
337             sub xyxy_to_n {
338 6     6 1 1132 my $self = shift;
339 6         18 my @n_list = $self->xyxy_to_n_list(@_);
340 6         18 return $n_list[0];
341             }
342              
343             sub xyxy_to_n_list_either {
344 6258     6258 1 12075 my ($self, $x1,$y1, $x2,$y2) = @_;
345 6258 100       10804 my @n1 = $self->xy_to_n_list($x1,$y1) or return;
346 3133 100       6207 my @n2 = $self->xy_to_n_list($x2,$y2) or return;
347 1882         4198 my $arms = $self->arms_count;
348 1882         2739 my @n_list;
349 1882         3246 foreach my $n1 (@n1) {
350 1885         2773 foreach my $n2 (@n2) {
351 1888 50       3849 if (abs($n1 - $n2) == $arms) {
352 1888         3493 push @n_list, _min($n1,$n2);
353             }
354             }
355             }
356 1882         4144 @n_list = sort {$a<=>$b} @n_list;
  6         13  
357 1882         3784 return @n_list;
358             }
359             sub xyxy_to_n_either {
360 6256     6256 1 34123 my $self = shift;
361 6256         11602 my @n_list = $self->xyxy_to_n_list_either(@_);
362 6256         12659 return $n_list[0];
363             }
364              
365             #------------------------------------------------------------------------------
366             # turns
367              
368             sub _UNDOCUMENTED__n_to_turn_LSR {
369 16     16   247 my ($self, $n) = @_;
370             ### _UNDOCUMENTED__n_to_turn_LSR(): $n
371              
372 16 100       46 my ($dx,$dy) = $self->n_to_dxdy($n - $self->arms_count)
373             or return undef;
374 8 50       36 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     69 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 101     101   2256 use constant tree_n_parent => undef; # default always no parent
  101         198  
  101         6963  
391              
392 101     101   614 use constant tree_n_children => (); # default no children
  101         224  
  101         10573  
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 101     101   723 use constant tree_num_children_list => (0);
  101         207  
  101         15609  
406             sub tree_num_children_minimum {
407 2     2 1 13 my ($self) = @_;
408 2         6 return ($self->tree_num_children_list)[0];
409             }
410             sub tree_num_children_maximum {
411 2     2 1 5 my ($self) = @_;
412 2         5 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 101     101   686 use constant tree_n_to_subheight => 0; # default all leaf node
  101         213  
  101         5532  
420              
421 101     101   824 use constant tree_n_to_depth => undef;
  101         216  
  101         4733  
422 101     101   588 use constant tree_depth_to_n => undef;
  101         213  
  101         45071  
423             sub tree_depth_to_n_end {
424 117     117 1 296 my ($self, $depth) = @_;
425 117 50 33     332 if ($depth >= 0
426             && defined (my $n = $self->tree_depth_to_n($depth+1))) {
427             ### tree_depth_to_n_end(): $depth, $n
428 117         276 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 101     101   738 use constant level_to_n_range => ();
  101         263  
  101         5382  
529 101     101   653 use constant n_to_level => undef;
  101         242  
  101         26136  
530              
531              
532             #------------------------------------------------------------------------------
533             # shared undocumented internals
534              
535             sub _max {
536 78322     78322   125783 my $max = 0;
537 78322         136300 foreach my $i (1 .. $#_) {
538 180224 100       350071 if ($_[$i] > $_[$max]) {
539 51724         74688 $max = $i;
540             }
541             }
542 78322         170013 return $_[$max];
543             }
544             sub _min {
545 61696     61696   284223 my $min = 0;
546 61696         98406 foreach my $i (1 .. $#_) {
547 161057 100       268022 if ($_[$i] < $_[$min]) {
548 25364         33536 $min = $i;
549             }
550             }
551 61696         98432 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 167470     167470   223208 my ($x) = @_;
558 167470 100       241834 if (ref $x) {
559 16 100 66     150 if ($x->isa('Math::BigFloat') || $x->isa('Math::BigRat')) {
560 5         49 $x = $x->copy->as_int;
561             }
562             }
563 167470         290751 return int(sqrt($x));
564             }
565              
566 101     101   49014 use Math::PlanePath::Base::Generic 'round_nearest';
  101         244  
  101         34403  
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 85780     85780   120796 my ($n, $d) = @_;
584 85780 100 66     142027 if (ref $n && $n->isa('Math::BigInt')) {
585 1008         3188 my ($quot,$rem) = $n->copy->bdiv($d);
586 1008 50 66     216101 if (! ref $d || $d < 1_000_000) {
587 1008         58383 $rem = $rem->numify; # plain remainder if fits
588             }
589 1008         25759 return ($quot, $rem);
590             }
591 84772         101811 my $rem = $n % $d;
592 84772         164623 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 147216     147216   194663 my $d = $_[1];
601 147216         180518 my $rem;
602 147216 100 66     290179 if (ref $_[0] && $_[0]->isa('Math::BigInt')) {
603 1         13 ($_[0], $rem) = $_[0]->copy->bdiv($d); # quot,rem in array context
604 1 50 33     248 if (! ref $d || $d < 1_000_000) {
605 1         4 return $rem->numify; # plain remainder if fits
606             }
607             } else {
608 147215         192471 $rem = $_[0] % $d;
609 147215         242309 $_[0] = int(($_[0]-$rem)/$d); # exact division stays in UV
610             }
611 147215         258107 return $rem;
612             }
613              
614             1;
615             __END__