# Copyright 2011, 2012, 2013, 2014, 2015 Kevin Ryde
# This file is part of Math-PlanePath.
#
# Math-PlanePath is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 3, or (at your option) any later
# version.
#
# Math-PlanePath is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with Math-PlanePath. If not, see <http://www.gnu.org/licenses/>.
# http://www.cisl.ucar.edu/css/papers/sfc3.pdf
# Hilbert + Peano-meander
#
# http://oceans11.lanl.gov/svn/POP/trunk/pop/source/distribution.F90
#
package Math::PlanePath::CincoCurve;
use 5.004;
use strict;
#use List::Util 'min', 'max';
*min = \&Math::PlanePath::_min;
*max = \&Math::PlanePath::_max;
use vars '$VERSION', '@ISA';
$VERSION = 119;
use Math::PlanePath;
use Math::PlanePath::Base::NSEW;
@ISA = ('Math::PlanePath::Base::NSEW',
'Math::PlanePath');
use Math::PlanePath::Base::Generic
'is_infinite',
'round_nearest';
use Math::PlanePath::Base::Digits
'round_down_pow',
'digit_split_lowtohigh',
'digit_join_lowtohigh';
use constant n_start => 0;
use constant class_x_negative => 0;
use constant class_y_negative => 0;
*xy_is_visited = \&Math::PlanePath::Base::Generic::xy_is_visited_quad1;
#------------------------------------------------------------------------------
# tables generated by tools/dekking-curve-table.pl
#
my @next_state = ( 0, 0,50,50,50, # 0
75,25,25,50,50,
0, 0,75,50, 0,
0, 0,25,75,75,
0,25,75,75, 0,
25,25,75,75,75, # 25
50, 0, 0,75,75,
25,25,50,75,25,
25,25, 0,50,50,
25, 0,50,50,25,
50,50, 0, 0, 0, # 50
25,75,75, 0, 0,
50,50,25, 0,50,
50,50,75,25,25,
50,75,25,25,50,
75,75,25,25,25, # 75
0,50,50,25,25,
75,75, 0,25,75,
75,75,50, 0, 0,
75,50, 0, 0,75);
my @digit_to_x = (0,1,2,2,2, 1,1,0,0,0, 0,1,1,2,2, 3,4,4,3,3, 4,4,3,3,4,
4,3,2,2,2, 3,3,4,4,4, 4,3,3,2,2, 1,0,0,1,1, 0,0,1,1,0,
0,0,0,1,2, 2,1,1,2,3, 4,4,3,3,4, 4,4,3,3,2, 2,1,1,0,0,
4,4,4,3,2, 2,3,3,2,1, 0,0,1,1,0, 0,0,1,1,2, 2,3,3,4,4);
my @digit_to_y = (0,0,0,1,2, 2,1,1,2,3, 4,4,3,3,4, 4,4,3,3,2, 2,1,1,0,0,
4,4,4,3,2, 2,3,3,2,1, 0,0,1,1,0, 0,0,1,1,2, 2,3,3,4,4,
0,1,2,2,2, 1,1,0,0,0, 0,1,1,2,2, 3,4,4,3,3, 4,4,3,3,4,
4,3,2,2,2, 3,3,4,4,4, 4,3,3,2,2, 1,0,0,1,1, 0,0,1,1,0);
my @yx_to_digit = ( 0, 1, 2,23,24, # 0
7, 6, 3,22,21,
8, 5, 4,19,20,
9,12,13,18,17,
10,11,14,15,16,
16,15,14,11,10, # 25
17,18,13,12, 9,
20,19, 4, 5, 8,
21,22, 3, 6, 7,
24,23, 2, 1, 0,
0, 7, 8, 9,10, # 50
1, 6, 5,12,11,
2, 3, 4,13,14,
23,22,19,18,15,
24,21,20,17,16,
16,17,20,21,24, # 75
15,18,19,22,23,
14,13, 4, 3, 2,
11,12, 5, 6, 1,
10, 9, 8, 7, 0);
my @min_digit = ( 0, 0, 0, 0, 0, # 0
7, 7, 7, 7, 8,
8, 8, 9, 9,10,
0, 0, 0, 0, 0,
6, 5, 5, 5, 5,
5, 5, 9, 9,10, # 25
0, 0, 0, 0, 0,
3, 3, 3, 3, 4,
4, 4, 9, 9,10,
0, 0, 0, 0, 0,
3, 3, 3, 3, 4, # 50
4, 4, 9, 9,10,
0, 0, 0, 0, 0,
3, 3, 3, 3, 4,
4, 4, 9, 9,10,
1, 1, 1, 1, 1, # 75
6, 5, 5, 5, 5,
5, 5,12,11,11,
1, 1, 1, 1, 1,
3, 3, 3, 3, 4,
4, 4,12,11,11, # 100
1, 1, 1, 1, 1,
3, 3, 3, 3, 4,
4, 4,12,11,11,
1, 1, 1, 1, 1,
3, 3, 3, 3, 4, # 125
4, 4,12,11,11,
2, 2, 2, 2, 2,
3, 3, 3, 3, 4,
4, 4,13,13,14,
2, 2, 2, 2, 2, # 150
3, 3, 3, 3, 4,
4, 4,13,13,14,
2, 2, 2, 2, 2,
3, 3, 3, 3, 4,
4, 4,13,13,14, # 175
23,22,19,18,15,
22,19,18,15,19,
18,15,18,15,15,
23,21,19,17,15,
21,19,17,15,19, # 200
17,15,17,15,15,
24,21,20,17,16,
21,20,17,16,20,
17,16,17,16,16,
16,16,16,16,16, # 225
17,17,17,17,20,
20,20,21,21,24,
15,15,15,15,15,
17,17,17,17,19,
19,19,21,21,23, # 250
14,13, 4, 3, 2,
13, 4, 3, 2, 4,
3, 2, 3, 2, 2,
11,11, 4, 3, 1,
12, 4, 3, 1, 4, # 275
3, 1, 3, 1, 1,
10, 9, 4, 3, 0,
9, 4, 3, 0, 4,
3, 0, 3, 0, 0,
15,15,15,15,15, # 300
18,18,18,18,19,
19,19,22,22,23,
14,13, 4, 3, 2,
13, 4, 3, 2, 4,
3, 2, 3, 2, 2, # 325
11,11, 4, 3, 1,
12, 4, 3, 1, 4,
3, 1, 3, 1, 1,
10, 9, 4, 3, 0,
9, 4, 3, 0, 4, # 350
3, 0, 3, 0, 0,
14,13, 4, 3, 2,
13, 4, 3, 2, 4,
3, 2, 3, 2, 2,
11,11, 4, 3, 1, # 375
12, 4, 3, 1, 4,
3, 1, 3, 1, 1,
10, 9, 4, 3, 0,
9, 4, 3, 0, 4,
3, 0, 3, 0, 0, # 400
11,11, 5, 5, 1,
12, 5, 5, 1, 5,
5, 1, 6, 1, 1,
10, 9, 5, 5, 0,
9, 5, 5, 0, 5, # 425
5, 0, 6, 0, 0,
10, 9, 8, 7, 0,
9, 8, 7, 0, 8,
7, 0, 7, 0, 0,
0, 0, 0, 0, 0, # 450
1, 1, 1, 1, 2,
2, 2,23,23,24,
0, 0, 0, 0, 0,
1, 1, 1, 1, 2,
2, 2,22,21,21, # 475
0, 0, 0, 0, 0,
1, 1, 1, 1, 2,
2, 2,19,19,20,
0, 0, 0, 0, 0,
1, 1, 1, 1, 2, # 500
2, 2,18,17,17,
0, 0, 0, 0, 0,
1, 1, 1, 1, 2,
2, 2,15,15,16,
7, 6, 3, 3, 3, # 525
6, 3, 3, 3, 3,
3, 3,22,21,21,
7, 5, 3, 3, 3,
5, 3, 3, 3, 3,
3, 3,19,19,20, # 550
7, 5, 3, 3, 3,
5, 3, 3, 3, 3,
3, 3,18,17,17,
7, 5, 3, 3, 3,
5, 3, 3, 3, 3, # 575
3, 3,15,15,16,
8, 5, 4, 4, 4,
5, 4, 4, 4, 4,
4, 4,19,19,20,
8, 5, 4, 4, 4, # 600
5, 4, 4, 4, 4,
4, 4,18,17,17,
8, 5, 4, 4, 4,
5, 4, 4, 4, 4,
4, 4,15,15,16, # 625
9, 9, 9, 9, 9,
12,12,12,12,13,
13,13,18,17,17,
9, 9, 9, 9, 9,
11,11,11,11,13, # 650
13,13,15,15,16,
10,10,10,10,10,
11,11,11,11,14,
14,14,15,15,16,
16,15,14,11,10, # 675
15,14,11,10,14,
11,10,11,10,10,
16,15,13,11, 9,
15,13,11, 9,13,
11, 9,11, 9, 9, # 700
16,15, 4, 4, 4,
15, 4, 4, 4, 4,
4, 4, 5, 5, 8,
16,15, 3, 3, 3,
15, 3, 3, 3, 3, # 725
3, 3, 5, 5, 7,
16,15, 2, 1, 0,
15, 2, 1, 0, 2,
1, 0, 1, 0, 0,
17,17,13,12, 9, # 750
18,13,12, 9,13,
12, 9,12, 9, 9,
17,17, 4, 4, 4,
18, 4, 4, 4, 4,
4, 4, 5, 5, 8, # 775
17,17, 3, 3, 3,
18, 3, 3, 3, 3,
3, 3, 5, 5, 7,
17,17, 2, 1, 0,
18, 2, 1, 0, 2, # 800
1, 0, 1, 0, 0,
20,19, 4, 4, 4,
19, 4, 4, 4, 4,
4, 4, 5, 5, 8,
20,19, 3, 3, 3, # 825
19, 3, 3, 3, 3,
3, 3, 5, 5, 7,
20,19, 2, 1, 0,
19, 2, 1, 0, 2,
1, 0, 1, 0, 0, # 850
21,21, 3, 3, 3,
22, 3, 3, 3, 3,
3, 3, 6, 6, 7,
21,21, 2, 1, 0,
22, 2, 1, 0, 2, # 875
1, 0, 1, 0, 0,
24,23, 2, 1, 0,
23, 2, 1, 0, 2,
1, 0, 1, 0, 0);
my @max_digit = ( 0, 7, 8, 9,10, # 0
7, 8, 9,10, 8,
9,10, 9,10,10,
1, 7, 8,12,12,
7, 8,12,12, 8,
12,12,12,12,11, # 25
2, 7, 8,13,14,
7, 8,13,14, 8,
13,14,13,14,14,
23,23,23,23,23,
22,22,22,22,19, # 50
19,19,18,18,15,
24,24,24,24,24,
22,22,22,22,20,
20,20,18,18,16,
1, 6, 6,12,12, # 75
6, 6,12,12, 5,
12,12,12,12,11,
2, 6, 6,13,14,
6, 6,13,14, 5,
13,14,13,14,14, # 100
23,23,23,23,23,
22,22,22,22,19,
19,19,18,18,15,
24,24,24,24,24,
22,22,22,22,20, # 125
20,20,18,18,16,
2, 3, 4,13,14,
3, 4,13,14, 4,
13,14,13,14,14,
23,23,23,23,23, # 150
22,22,22,22,19,
19,19,18,18,15,
24,24,24,24,24,
22,22,22,22,20,
20,20,18,18,16, # 175
23,23,23,23,23,
22,22,22,22,19,
19,19,18,18,15,
24,24,24,24,24,
22,22,22,22,20, # 200
20,20,18,18,16,
24,24,24,24,24,
21,21,21,21,20,
20,20,17,17,16,
16,17,20,21,24, # 225
17,20,21,24,20,
21,24,21,24,24,
16,18,20,22,24,
18,20,22,24,20,
22,24,22,24,24, # 250
16,18,20,22,24,
18,20,22,24,20,
22,24,22,24,24,
16,18,20,22,24,
18,20,22,24,20, # 275
22,24,22,24,24,
16,18,20,22,24,
18,20,22,24,20,
22,24,22,24,24,
15,18,19,22,23, # 300
18,19,22,23,19,
22,23,22,23,23,
15,18,19,22,23,
18,19,22,23,19,
22,23,22,23,23, # 325
15,18,19,22,23,
18,19,22,23,19,
22,23,22,23,23,
15,18,19,22,23,
18,19,22,23,19, # 350
22,23,22,23,23,
14,14,14,14,14,
13,13,13,13, 4,
4, 4, 3, 3, 2,
14,14,14,14,14, # 375
13,13,13,13, 5,
6, 6, 6, 6, 2,
14,14,14,14,14,
13,13,13,13, 8,
8, 8, 7, 7, 2, # 400
11,12,12,12,12,
12,12,12,12, 5,
6, 6, 6, 6, 1,
11,12,12,12,12,
12,12,12,12, 8, # 425
8, 8, 7, 7, 1,
10,10,10,10,10,
9, 9, 9, 9, 8,
8, 8, 7, 7, 0,
0, 1, 2,23,24, # 450
1, 2,23,24, 2,
23,24,23,24,24,
7, 7, 7,23,24,
6, 6,23,24, 3,
23,24,23,24,24, # 475
8, 8, 8,23,24,
6, 6,23,24, 4,
23,24,23,24,24,
9,12,13,23,24,
12,13,23,24,13, # 500
23,24,23,24,24,
10,12,14,23,24,
12,14,23,24,14,
23,24,23,24,24,
7, 7, 7,22,22, # 525
6, 6,22,22, 3,
22,22,22,22,21,
8, 8, 8,22,22,
6, 6,22,22, 4,
22,22,22,22,21, # 550
9,12,13,22,22,
12,13,22,22,13,
22,22,22,22,21,
10,12,14,22,22,
12,14,22,22,14, # 575
22,22,22,22,21,
8, 8, 8,19,20,
5, 5,19,20, 4,
19,20,19,20,20,
9,12,13,19,20, # 600
12,13,19,20,13,
19,20,19,20,20,
10,12,14,19,20,
12,14,19,20,14,
19,20,19,20,20, # 625
9,12,13,18,18,
12,13,18,18,13,
18,18,18,18,17,
10,12,14,18,18,
12,14,18,18,14, # 650
18,18,18,18,17,
10,11,14,15,16,
11,14,15,16,14,
15,16,15,16,16,
16,16,16,16,16, # 675
15,15,15,15,14,
14,14,11,11,10,
17,18,18,18,18,
18,18,18,18,14,
14,14,12,12,10, # 700
20,20,20,20,20,
19,19,19,19,14,
14,14,12,12,10,
21,22,22,22,22,
22,22,22,22,14, # 725
14,14,12,12,10,
24,24,24,24,24,
23,23,23,23,14,
14,14,12,12,10,
17,18,18,18,18, # 750
18,18,18,18,13,
13,13,12,12, 9,
20,20,20,20,20,
19,19,19,19,13,
13,13,12,12, 9, # 775
21,22,22,22,22,
22,22,22,22,13,
13,13,12,12, 9,
24,24,24,24,24,
23,23,23,23,13, # 800
13,13,12,12, 9,
20,20,20,20,20,
19,19,19,19, 4,
5, 8, 5, 8, 8,
21,22,22,22,22, # 825
22,22,22,22, 4,
6, 8, 6, 8, 8,
24,24,24,24,24,
23,23,23,23, 4,
6, 8, 6, 8, 8, # 850
21,22,22,22,22,
22,22,22,22, 3,
6, 7, 6, 7, 7,
24,24,24,24,24,
23,23,23,23, 3, # 875
6, 7, 6, 7, 7,
24,24,24,24,24,
23,23,23,23, 2,
2, 2, 1, 1, 0);
# state length 100 in each of 4 tables = 400
# min/max 2 of 900 each = 1800
sub n_to_xy {
my ($self, $n) = @_;
### CincoCurve n_to_xy(): $n
if ($n < 0) { return; }
if (is_infinite($n)) { return ($n,$n); }
my $int = int($n);
$n -= $int; # fraction part
my @digits = digit_split_lowtohigh($int,25);
my $len = ($int*0 + 5) ** scalar(@digits); # inherit bignum
### digits: join(', ',@digits)." count ".scalar(@digits)
### $len
my $state = my $dir = 0;
my $x = 0;
my $y = 0;
while (defined (my $digit = pop @digits)) {
$len /= 5;
$state += $digit;
if ($digit != 24) {
$dir = $state;
}
### $len
### $state
### digit_to_x: $digit_to_x[$state]
### digit_to_y: $digit_to_y[$state]
### next_state: $next_state[$state]
$x += $len * $digit_to_x[$state];
$y += $len * $digit_to_y[$state];
$state = $next_state[$state];
}
### final integer: "$x,$y"
### assert: ($dir % 25) != 24
# with $n fractional part
return ($n * ($digit_to_x[$dir+1] - $digit_to_x[$dir]) + $x,
$n * ($digit_to_y[$dir+1] - $digit_to_y[$dir]) + $y);
}
sub xy_to_n {
my ($self, $x, $y) = @_;
### CincoCurve xy_to_n(): "$x, $y"
$x = round_nearest ($x);
$y = round_nearest ($y);
if ($x < 0 || $y < 0) {
return undef;
}
if (is_infinite($x)) {
return $x;
}
if (is_infinite($y)) {
return $y;
}
my @xdigits = digit_split_lowtohigh ($x, 5);
my @ydigits = digit_split_lowtohigh ($y, 5);
my $state = 0;
my @ndigits;
foreach my $i (reverse 0 .. max($#xdigits,$#ydigits)) { # high to low
my $ndigit = $yx_to_digit[$state
+ 5*($ydigits[$i]||0)
+ ($xdigits[$i]||0)];
$ndigits[$i] = $ndigit;
$state = $next_state[$state+$ndigit];
}
return digit_join_lowtohigh (\@ndigits, 25,
$x * 0 * $y); # bignum zero
}
# exact
sub rect_to_n_range {
my ($self, $x1,$y1, $x2,$y2) = @_;
### BetaOmega rect_to_n_range(): "$x1,$y1, $x2,$y2"
$x1 = round_nearest ($x1);
$x2 = round_nearest ($x2);
$y1 = round_nearest ($y1);
$y2 = round_nearest ($y2);
($x1,$x2) = ($x2,$x1) if $x1 > $x2;
($y1,$y2) = ($y2,$y1) if $y1 > $y2;
if ($x2 < 0 || $y2 < 0) {
return (1, 0);
}
if ($x1 < 0) { $x1 *= 0; } # "*=" to preserve bigint x1 or y1
if ($y1 < 0) { $y1 *= 0; }
my ($len, $level) = round_down_pow (($x2 > $y2 ? $x2 : $y2),
5);
if (is_infinite($len)) {
return (0, $len);
}
# At this point an over-estimate would be: return (0, 25*$len*$len-1);
my $n_min = my $n_max
= my $y_min = my $y_max
= my $x_min = my $x_max
= my $min_state = my $max_state
= 0;
### $x_min
### $y_min
while ($level >= 0) {
### $level
### $len
{
my $digit = $min_digit[9*$min_state
+ _rect_key($x1, $x2, $x_min, $len) * 15
+ _rect_key($y1, $y2, $y_min, $len)];
### $min_state
### $x_min
### $y_min
### $digit
$n_min = 25*$n_min + $digit;
$min_state += $digit;
$x_min += $len * $digit_to_x[$min_state];
$y_min += $len * $digit_to_y[$min_state];
$min_state = $next_state[$min_state];
}
{
my $digit = $max_digit[9*$max_state
+ _rect_key($x1, $x2, $x_max, $len) * 15
+ _rect_key($y1, $y2, $y_max, $len)];
$n_max = 25*$n_max + $digit;
$max_state += $digit;
$x_max += $len * $digit_to_x[$max_state];
$y_max += $len * $digit_to_y[$max_state];
$max_state = $next_state[$max_state];
}
$len = int($len/5);
$level--;
}
return ($n_min, $n_max);
}
sub _rect_key {
my ($z1, $z2, $zbase, $len) = @_;
$z1 = max (0, min (4, int (($z1 - $zbase)/$len)));
$z2 = max (0, min (4, int (($z2 - $zbase)/$len)));
### assert: $z1 <= $z2
return (9-$z1)*$z1/2 + $z2;
}
#------------------------------------------------------------------------------
# levels
use Math::PlanePath::DekkingCentres;
*level_to_n_range = \&Math::PlanePath::DekkingCentres::level_to_n_range;
*n_to_level = \&Math::PlanePath::DekkingCentres::n_to_level;
#------------------------------------------------------------------------------
1;
__END__
=for stopwords eg Ryde ie CincoCurve Math-PlanePath Cinco COSIM
=head1 NAME
Math::PlanePath::CincoCurve -- 5x5 self-similar curve
=head1 SYNOPSIS
use Math::PlanePath::CincoCurve;
my $path = Math::PlanePath::CincoCurve->new;
my ($x, $y) = $path->n_to_xy (123);
=head1 DESCRIPTION
X<Dennis, John>This is the 5x5 self-similar Cinco curve
=over
John Dennis, "Inverse Space-Filling Curve Partitioning of a Global Ocean
Model", and source code from COSIM
L<http://www.cecs.uci.edu/~papers/ipdps07/pdfs/IPDPS-1569010963-paper-2.pdf>
L<http://oceans11.lanl.gov/trac/POP/browser/trunk/pop/source/spacecurve_mod.F90>
L<http://oceans11.lanl.gov/svn/POP/trunk/pop/source/spacecurve_mod.F90>
=back
It makes a 5x5 self-similar traversal of the first quadrant XE<gt>0,YE<gt>0.
|
4 | 10--11 14--15--16 35--36 39--40--41 74 71--70 67--66
| | | | | | | | | | | | | |
3 | 9 12--13 18--17 34 37--38 43--42 73--72 69--68 65
| | | | | |
2 | 8 5-- 4 19--20 33 30--29 44--45 52--53--54 63--64
| | | | | | | | | | | |
1 | 7-- 6 3 22--21 32--31 28 47--46 51 56--55 62--61
| | | | | | | |
Y=0 | 0-- 1-- 2 23--24--25--26--27 48--49--50 57--58--59--60
|
+--------------------------------------------------------------
X=0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
The base pattern is the N=0 to N=24 part. It repeats transposed and rotated
to make the ends join. N=25 to N=49 is a repeat of the base, then N=50 to
N=74 is a transpose to go upwards. The sub-part arrangements are as
follows.
+------+------+------+------+------+
| 10 | 11 | 14 | 15 | 16 |
| | | | | |
|----->|----->|----->|----->|----->|
+------+------+------+------+------+
|^ 9 | 12 ||^ 13 | 18 ||<-----|
|| T | T ||| T | T || 17 |
|| | v|| | v| |
+------+------+------+------+------+
|^ 8 | 5 ||^ 4 | 19 || 20 |
|| T | T ||| T | T || |
|| | v|| | v|----->|
+------+------+------+------+------+
|<-----|<---- |^ 3 | 22 ||<-----|
| 7 | 6 || T | T || 21 |
| | || | v| |
+------+------+------+------+------+
| 0 | 1 |^ 2 | 23 || 24 |
| | || T | T || |
|----->|----->|| | v|----->|
+------+------+------+------+------+
Parts such as 6 going left are the base rotated 180 degrees. The verticals
like 2 are a transpose of the base, ie. swap X,Y, and downward vertical like
23 is transpose plus rotate 180 (which is equivalent to a mirror across the
anti-diagonal). Notice the base shape fills its sub-part to the left side
and the transpose instead fills on the right.
The N values along the X axis are increasing, as are the values along the Y
axis. This occurs because the values along the sub-parts of the base are
increasing along the X and Y axes, and the other two sides are increasing
too when rotated or transposed for sub-parts such as 2 and 23, or 7, 8
and 9.
Dennis conceives this for use in combination with 2x2 Hilbert and 3x3
meander shapes so that sizes which are products of 2, 3 and 5 can be used
for partitioning. Such mixed patterns can't be done with the code here,
mainly since a mixture depends on having a top-level target size rather than
the unlimited first quadrant here.
=head1 FUNCTIONS
See L<Math::PlanePath/FUNCTIONS> for behaviour common to all path classes.
=over 4
=item C<$path = Math::PlanePath::CincoCurve-E<gt>new ()>
Create and return a new path object.
=item C<($x,$y) = $path-E<gt>n_to_xy ($n)>
Return the X,Y coordinates of point number C<$n> on the path. Points begin
at 0 and if C<$n E<lt> 0> then the return is an empty list.
=back
=head2 Level Methods
=over
=item C<($n_lo, $n_hi) = $path-E<gt>level_to_n_range($level)>
Return C<(0, 25**$level - 1)>.
=back
=head1 SEE ALSO
L<Math::PlanePath>,
L<Math::PlanePath::PeanoCurve>,
L<Math::PlanePath::DekkingCentres>
=head1 HOME PAGE
L<http://user42.tuxfamily.org/math-planepath/index.html>
=head1 LICENSE
Copyright 2011, 2012, 2013, 2014, 2015 Kevin Ryde
Math-PlanePath is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
Math-PlanePath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along with
Math-PlanePath. If not, see <http://www.gnu.org/licenses/>.
=cut