File Coverage

blib/lib/Math/PlanePath.pm
Criterion Covered Total %
statement 240 349 68.7
branch 39 108 36.1
condition 19 88 21.5
subroutine 73 93 78.4
pod 40 41 97.5
total 411 679 60.5


line stmt bran cond sub pod time code
1             # Copyright 2010, 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 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 99     99   11015 use 5.004;
  99         400  
20 99     99   496 use strict;
  99         194  
  99         2379  
21              
22 99     99   526 use vars '$VERSION';
  99         216  
  99         5707  
23             $VERSION = 127;
24              
25             # uncomment this to run the ### lines
26             # use Smart::Comments;
27              
28              
29             # defaults
30 99     99   664 use constant figure => 'square';
  99         193  
  99         10035  
31 99     99   677 use constant default_n_start => 1;
  99         196  
  99         15240  
32             sub n_start {
33 375     375 1 2182 my ($self) = @_;
34 375 100 66     1828 if (ref $self && defined $self->{'n_start'}) {
35 351         1151 return $self->{'n_start'};
36             } else {
37 24         378 return $self->default_n_start;
38             }
39             }
40             sub arms_count {
41 7512     7512 1 11415 my ($self) = @_;
42 7512   100     23568 return $self->{'arms'} || 1;
43             }
44              
45 99     99   714 use constant class_x_negative => 1;
  99         206  
  99         5455  
46 99     99   593 use constant class_y_negative => 1;
  99         255  
  99         13059  
47 89     89 1 7359 sub x_negative { $_[0]->class_x_negative }
48 344     344 1 2660 sub y_negative { $_[0]->class_y_negative }
49 99     99   751 use constant x_negative_at_n => undef;
  99         227  
  99         4893  
50 99     99   2594 use constant y_negative_at_n => undef;
  99         208  
  99         7462  
51 99     99   1012 use constant n_frac_discontinuity => undef;
  99         215  
  99         11805  
52              
53 99     99   572 use constant parameter_info_array => [];
  99         213  
  99         20045  
54             sub parameter_info_list {
55 63     63 1 3245 return @{$_[0]->parameter_info_array};
  63         488  
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 7 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       14 return ($self->y_negative ? undef : 0);
67             }
68 99     99   755 use constant x_maximum => undef;
  99         230  
  99         6534  
69 99     99   593 use constant y_maximum => undef;
  99         218  
  99         13595  
70              
71             sub sumxy_minimum {
72 2     2 1 3 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         15 return $x_minimum + $y_minimum;
79             }
80 0         0 return undef;
81             }
82 99     99   692 use constant sumxy_maximum => undef;
  99         234  
  99         14618  
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 99     99   718 use constant sumabsxy_maximum => undef;
  99         183  
  99         7519  
96              
97 99     99   644 use constant diffxy_minimum => undef;
  99         206  
  99         40824  
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 99     99   712 use constant rsquared_maximum => undef;
  99         226  
  99         14505  
174              
175             sub gcdxy_minimum {
176 1     1 0 9 my ($self) = @_;
177             ### gcdxy_minimum(): "visited=".($self->xy_is_visited(0,0)||0)
178 1 50       4 return ($self->xy_is_visited(0,0)
179             ? 0 # gcd(0,0)=0
180             : 1); # any other has gcd>=1
181             }
182 99     99   681 use constant gcdxy_maximum => undef;
  99         423  
  99         6703  
183              
184 99     99   571 use constant turn_any_left => 1;
  99         684  
  99         6164  
185 99     99   611 use constant turn_any_right => 1;
  99         221  
  99         8252  
186 99     99   590 use constant turn_any_straight => 1;
  99         204  
  99         4674  
187              
188              
189             #------------------------------------------------------------------------------
190              
191 99     99   621 use constant dir_minimum_dxdy => (1,0); # East
  99         206  
  99         5776  
192 99     99   600 use constant dir_maximum_dxdy => (0,0); # supremum all angles
  99         189  
  99         6823  
193              
194 99     99   2333 use constant dx_minimum => undef;
  99         209  
  99         8441  
195 99     99   598 use constant dy_minimum => undef;
  99         178  
  99         6311  
196 99     99   573 use constant dx_maximum => undef;
  99         201  
  99         4420  
197 99     99   593 use constant dy_maximum => undef;
  99         192  
  99         6366  
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 99     99   5824 use constant _UNDOCUMENTED__dxdy_list => (); # default empty for not a finite list
  99         2008  
  99         6365  
205 99     99   583 use constant _UNDOCUMENTED__dxdy_list_at_n => undef; # maybe dxdy_at_n()
  99         190  
  99         7726  
206 99         8584 use constant _UNDOCUMENTED__dxdy_list_three => (2,0, # E
207             -1,1, # NW
208 99     99   2449 -1,-1); # SW
  99         168  
209 99         7201 use constant _UNDOCUMENTED__dxdy_list_six => (2,0, # E
210             1,1, # NE
211             -1,1, # NW
212             -2,0, # W
213             -1,-1, # SW
214 99     99   595 1,-1); # SE
  99         215  
215 99         29889 use constant _UNDOCUMENTED__dxdy_list_eight => (1,0, # E
216             1,1, # NE
217             0,1, # N
218             -1,1, # NW
219             -1,0, # W
220             -1,-1, # SW
221             0,-1, # S
222 99     99   635 1,-1); # SE
  99         175  
223              
224             sub absdx_minimum {
225 0     0 1 0 my ($self) = @_;
226             # If dX>=0 then abs(dX)=dX always and absdx_minimum()==dx_minimum().
227             # This happens for column style paths like CoprimeColumns.
228             # dX>0 is only for line paths so not very interesting.
229 0 0       0 if (defined (my $dx_minimum = $self->dx_minimum)) {
230 0 0       0 if ($dx_minimum >= 0) { return $dx_minimum; }
  0         0  
231             }
232 0         0 return 0;
233             }
234             sub absdx_maximum {
235 0     0 1 0 my ($self) = @_;
236 0 0 0     0 if (defined (my $dx_minimum = $self->dx_minimum)
237             && defined (my $dx_maximum = $self->dx_maximum)) {
238 0         0 return _max(abs($dx_minimum),abs($dx_maximum));
239             }
240 0         0 return undef;
241             }
242              
243             sub absdy_minimum {
244 0     0 1 0 my ($self) = @_;
245             # if dY>=0 then abs(dY)=dY always and absdy_minimum()==dy_minimum()
246 0 0       0 if (defined (my $dy_minimum = $self->dy_minimum)) {
247 0 0       0 if ($dy_minimum >= 0) { return $dy_minimum; }
  0         0  
248             }
249 0         0 return 0;
250             }
251             sub absdy_maximum {
252 0     0 1 0 my ($self) = @_;
253 0 0 0     0 if (defined (my $dy_minimum = $self->dy_minimum)
254             && defined (my $dy_maximum = $self->dy_maximum)) {
255 0         0 return _max(abs($dy_minimum),abs($dy_maximum));
256             } else {
257 0         0 return undef;
258             }
259             }
260              
261 99     99   692 use constant dsumxy_minimum => undef;
  99         213  
  99         7940  
262 99     99   696 use constant dsumxy_maximum => undef;
  99         205  
  99         4682  
263 99     99   625 use constant ddiffxy_minimum => undef;
  99         181  
  99         6133  
264 99     99   561 use constant ddiffxy_maximum => undef;
  99         197  
  99         91139  
265              
266             #------------------------------------------------------------------------------
267              
268             sub new {
269 1866     1866 1 35372 my $class = shift;
270 1866         7662 return bless { @_ }, $class;
271             }
272              
273             {
274             my %parameter_info_hash;
275             sub parameter_info_hash {
276 3     3 1 134 my ($class_or_self) = @_;
277 3   33     19 my $class = (ref $class_or_self || $class_or_self);
278             return ($parameter_info_hash{$class}
279 3   50     19 ||= { map { $_->{'name'} => $_ }
  4         25  
280             $class_or_self->parameter_info_list });
281             }
282             }
283              
284             sub xy_to_n_list {
285             ### xy_to_n_list() ...
286 9404 100   9404 1 21876 if (defined (my $n = shift->xy_to_n(@_))) {
287             ### $n
288 5028         12091 return $n;
289             }
290             ### empty ...
291 4376         12058 return;
292             }
293             sub xy_is_visited {
294 7     7 1 563 my ($self, $x, $y) = @_;
295             ### xy_is_visited(): "$x,$y is ndefined=".defined($self->xy_to_n($x,$y))
296 7         17 return defined($self->xy_to_n($x,$y));
297             }
298              
299             sub n_to_n_list {
300 0     0 1 0 my ($self, $n) = @_;
301 0 0       0 my ($x,$y) = $self->n_to_xy($n) or return;
302 0         0 return $self->xy_to_n_list($x,$y);
303             }
304              
305             sub n_to_dxdy {
306 5006     5006 1 103966 my ($self, $n) = @_;
307             ### n_to_dxdy(): $n
308 5006 100       9904 my ($x,$y) = $self->n_to_xy ($n)
309             or return;
310 5003 50       10972 my ($next_x,$next_y) = $self->n_to_xy ($n + $self->arms_count)
311             or return;
312             ### points: "$x,$y $next_x,$next_y"
313 5003         11773 return ($next_x - $x,
314             $next_y - $y);
315             }
316             sub n_to_rsquared {
317 31     31 1 70 my ($self, $n) = @_;
318 31 50       74 my ($x,$y) = $self->n_to_xy($n) or return undef;
319 31         105 return $x*$x + $y*$y;
320             }
321             sub n_to_radius {
322 0     0 1 0 my ($self, $n) = @_;
323 0         0 my $rsquared = $self->n_to_rsquared($n);
324 0 0       0 return (defined $rsquared ? sqrt($rsquared) : undef);
325             }
326              
327             sub xyxy_to_n_list {
328 8     8 1 2012 my ($self, $x1,$y1, $x2,$y2) = @_;
329 8 50       24 my @n1 = $self->xy_to_n_list($x1,$y1) or return;
330 8 50       19 my @n2 = $self->xy_to_n_list($x2,$y2) or return;
331 8         35 my $arms = $self->arms_count;
332 8         81 return grep { my $want_n2 = $_ + $arms;
  11         18  
333 11         15 grep {$_ == $want_n2} @n2 # seek $n2 which is this $n1+$arms
  15         48  
334             } @n1;
335             }
336             sub xyxy_to_n {
337 6     6 1 1294 my $self = shift;
338 6         18 my @n_list = $self->xyxy_to_n_list(@_);
339 6         24 return $n_list[0];
340             }
341              
342             sub xyxy_to_n_list_either {
343 6258     6258 1 11431 my ($self, $x1,$y1, $x2,$y2) = @_;
344 6258 100       11133 my @n1 = $self->xy_to_n_list($x1,$y1) or return;
345 3133 100       6305 my @n2 = $self->xy_to_n_list($x2,$y2) or return;
346 1882         3870 my $arms = $self->arms_count;
347 1882         2552 my @n_list;
348 1882         2891 foreach my $n1 (@n1) {
349 1885         2582 foreach my $n2 (@n2) {
350 1888 50       3724 if (abs($n1 - $n2) == $arms) {
351 1888         3587 push @n_list, _min($n1,$n2);
352             }
353             }
354             }
355 1882         3765 @n_list = sort {$a<=>$b} @n_list;
  6         15  
356 1882         3675 return @n_list;
357             }
358             sub xyxy_to_n_either {
359 6256     6256 1 32335 my $self = shift;
360 6256         11632 my @n_list = $self->xyxy_to_n_list_either(@_);
361 6256         12975 return $n_list[0];
362             }
363              
364             #------------------------------------------------------------------------------
365             # turns
366              
367             sub _UNDOCUMENTED__n_to_turn_LSR {
368 13     13   153 my ($self, $n) = @_;
369             ### _UNDOCUMENTED__n_to_turn_LSR(): $n
370              
371 13 100       39 my ($dx,$dy) = $self->n_to_dxdy($n - $self->arms_count) or return undef;
372 7 50       21 my ($next_dx,$next_dy) = $self->n_to_dxdy($n) or return undef;
373              
374             ### dxdy: "$dx,$dy and $next_dx,$next_dy arms=".$self->arms_count
375              
376 7   100     42 return (($next_dy * $dx <=> $next_dx * $dy) || 0); # 1,0,-1
377             }
378              
379             #------------------------------------------------------------------------------
380             # tree
381              
382             sub is_tree {
383 0     0 1 0 my ($self) = @_;
384 0         0 return $self->tree_n_num_children($self->n_start);
385             }
386              
387 99     99   778 use constant tree_n_parent => undef; # default always no parent
  99         195  
  99         5405  
388              
389 99     99   2444 use constant tree_n_children => (); # default no children
  99         200  
  99         12234  
390             sub tree_n_num_children {
391 0     0 1 0 my ($self, $n) = @_;
392 0 0       0 if ($n >= $self->n_start) {
393 0         0 my @n_list = $self->tree_n_children($n);
394 0         0 return scalar(@n_list);
395             } else {
396 0         0 return undef;
397             }
398             }
399              
400             # For non-trees n_num_children() always returns 0 so that's the single
401             # return here.
402 99     99   711 use constant tree_num_children_list => (0);
  99         202  
  99         15006  
403             sub tree_num_children_minimum {
404 2     2 1 13 my ($self) = @_;
405 2         9 return ($self->tree_num_children_list)[0];
406             }
407             sub tree_num_children_maximum {
408 2     2 1 5 my ($self) = @_;
409 2         6 return ($self->tree_num_children_list)[-1];
410             }
411             sub tree_any_leaf {
412 0     0 1 0 my ($self) = @_;
413 0         0 return ($self->tree_num_children_minimum == 0);
414             }
415              
416 99     99   695 use constant tree_n_to_subheight => 0; # default all leaf node
  99         182  
  99         5192  
417              
418 99     99   607 use constant tree_n_to_depth => undef;
  99         188  
  99         4926  
419 99     99   631 use constant tree_depth_to_n => undef;
  99         189  
  99         43242  
420             sub tree_depth_to_n_end {
421 117     117 1 322 my ($self, $depth) = @_;
422 117 50 33     371 if ($depth >= 0
423             && defined (my $n = $self->tree_depth_to_n($depth+1))) {
424             ### tree_depth_to_n_end(): $depth, $n
425 117         306 return $n-1;
426             } else {
427 0         0 return undef;
428             }
429             }
430             sub tree_depth_to_n_range {
431 0     0 1 0 my ($self, $depth) = @_;
432 0 0 0     0 if (defined (my $n = $self->tree_depth_to_n($depth))
433             && defined (my $n_end = $self->tree_depth_to_n_end($depth))) {
434 0         0 return ($n, $n_end);
435             }
436 0         0 return;
437             }
438              
439             sub tree_depth_to_width {
440 0     0 1 0 my ($self, $depth) = @_;
441 0 0 0     0 if (defined (my $n = $self->tree_depth_to_n($depth))
442             && defined (my $n_end = $self->tree_depth_to_n_end($depth))) {
443 0         0 return $n_end - $n + 1;
444             }
445 0         0 return undef;
446             }
447              
448             sub tree_num_roots {
449 0     0 1 0 my ($self) = @_;
450 0         0 my @root_n_list = $self->tree_root_n_list;
451 0         0 return scalar(@root_n_list);
452             }
453             sub tree_root_n_list {
454 0     0 1 0 my ($self) = @_;
455 0         0 my $n_start = $self->n_start;
456 0         0 my @ret;
457 0         0 for (my $n = $n_start; ; $n++) {
458             # stop on finding a non-root (has a parent), or a non-tree path has no
459             # children at all
460 0 0 0     0 if (defined($self->tree_n_parent($n))
461             || ! $self->tree_n_num_children($n)) {
462 0         0 last;
463             }
464 0         0 push @ret, $n;
465             }
466 0         0 return @ret;
467             }
468              
469             # Generic search upwards. Not fast, but works with past Toothpick or
470             # anything slack which doesn't have own tree_n_root(). When only one root
471             # there's no search.
472             sub tree_n_root {
473 0     0 1 0 my ($self, $n) = @_;
474 0         0 my $num_roots = $self->tree_num_roots;
475 0 0       0 if ($num_roots == 0) {
476 0         0 return undef; # not a tree
477             }
478 0         0 my $n_start = $self->n_start;
479 0 0       0 unless ($n >= $n_start) { # and warn if $n==undef
480 0         0 return undef; # -inf or NaN
481             }
482 0 0       0 if ($num_roots == 1) {
483 0         0 return $n_start; # only one root, no search
484             }
485              
486 0         0 for (;;) {
487 0         0 my $n_parent = $self->tree_n_parent($n);
488 0 0       0 if (! defined $n_parent) {
489 0         0 return $n; # found root
490             }
491 0 0       0 unless ($n_parent < $n) {
492 0         0 return undef; # +inf or something bad not making progress
493             }
494 0         0 $n = $n_parent;
495             }
496             }
497              
498             # Generic search for where no more children.
499             # But must watch out for infinite lets, and might also watch out for
500             # rounding or overflow.
501             #
502             # sub path_tree_n_to_subheight {
503             # my ($path, $n) = @_;
504             # ### path_tree_n_to_subheight(): "$n"
505             #
506             # if (is_infinite($n)) {
507             # return $n;
508             # }
509             # my $max = $path->tree_n_to_depth($n) + 10;
510             # my @n = ($n);
511             # my $height = 0;
512             # do {
513             # @n = map {$path->tree_n_children($_)} @n
514             # or return $height;
515             # $height++;
516             # } while (@n && $height < $max);
517             #
518             # ### height infinite ...
519             # return undef;
520             # }
521              
522             #------------------------------------------------------------------------------
523             # levels
524              
525 99     99   726 use constant level_to_n_range => ();
  99         308  
  99         5246  
526 99     99   626 use constant n_to_level => undef;
  99         201  
  99         23583  
527              
528              
529             #------------------------------------------------------------------------------
530             # shared undocumented internals
531              
532             sub _max {
533 77684     77684   122014 my $max = 0;
534 77684         135605 foreach my $i (1 .. $#_) {
535 179586 100       352249 if ($_[$i] > $_[$max]) {
536 51900         76490 $max = $i;
537             }
538             }
539 77684         165437 return $_[$max];
540             }
541             sub _min {
542 61891     61891   285864 my $min = 0;
543 61891         101518 foreach my $i (1 .. $#_) {
544 161252 100       275938 if ($_[$i] < $_[$min]) {
545 25517         33399 $min = $i;
546             }
547             }
548 61891         97082 return $_[$min];
549             }
550             # Return square root of $x, rounded towards zero.
551             # Recent BigFloat and BigRat need explicit conversion to BigInt, they no
552             # longer do that in int().
553             sub _sqrtint {
554 167471     167471   239808 my ($x) = @_;
555 167471 100       262912 if (ref $x) {
556 16 100 66     107 if ($x->isa('Math::BigFloat') || $x->isa('Math::BigRat')) {
557 5         39 $x = $x->copy->as_int;
558             }
559             }
560 167471         329360 return int(sqrt($x));
561             }
562              
563 99     99   52634 use Math::PlanePath::Base::Generic 'round_nearest';
  99         691  
  99         35264  
564             sub _rect_for_first_quadrant {
565 0     0   0 my ($self, $x1,$y1, $x2,$y2) = @_;
566 0         0 $x1 = round_nearest($x1);
567 0         0 $y1 = round_nearest($y1);
568 0         0 $x2 = round_nearest($x2);
569 0         0 $y2 = round_nearest($y2);
570 0 0       0 ($x1,$x2) = ($x2,$x1) if $x1 > $x2;
571 0 0       0 ($y1,$y2) = ($y2,$y1) if $y1 > $y2;
572 0 0 0     0 if ($x2 < 0 || $y2 < 0) {
573 0         0 return;
574             }
575 0         0 return ($x1,$y1, $x2,$y2);
576             }
577              
578             # return ($quotient, $remainder)
579             sub _divrem {
580 85741     85741   139528 my ($n, $d) = @_;
581 85741 100 66     155099 if (ref $n && $n->isa('Math::BigInt')) {
582 1008         2774 my ($quot,$rem) = $n->copy->bdiv($d);
583 1008 50 66     215896 if (! ref $d || $d < 1_000_000) {
584 1008         58639 $rem = $rem->numify; # plain remainder if fits
585             }
586 1008         25455 return ($quot, $rem);
587             }
588 84733         113831 my $rem = $n % $d;
589 84733         190765 return (int(($n-$rem)/$d), # exact division stays in UV
590             $rem);
591             }
592              
593             # return $remainder, modify $n
594             # the scalar $_[0] is modified, but if it's a BigInt then a new BigInt is made
595             # and stored there, the bigint value is not changed
596             sub _divrem_mutate {
597 145432     145432   195546 my $d = $_[1];
598 145432         178669 my $rem;
599 145432 100 66     290823 if (ref $_[0] && $_[0]->isa('Math::BigInt')) {
600 1         4 ($_[0], $rem) = $_[0]->copy->bdiv($d); # quot,rem in array context
601 1 50 33     217 if (! ref $d || $d < 1_000_000) {
602 1         3 return $rem->numify; # plain remainder if fits
603             }
604             } else {
605 145431         190662 $rem = $_[0] % $d;
606 145431         243582 $_[0] = int(($_[0]-$rem)/$d); # exact division stays in UV
607             }
608 145431         256255 return $rem;
609             }
610              
611             1;
612             __END__