# Copyright 2012, 2013 Kevin Ryde

# This file is part of Math-PlanePath-Toothpick.
#
# Math-PlanePath-Toothpick 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-Toothpick 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-Toothpick.  If not, see <http://www.gnu.org/licenses/>.


# side without log2 from diagonal, and sides of 3side
# side with log2 from X=3*2^k,Y=2^k down, and middle of 3side


package Math::PlanePath::OneOfEight;
use 5.004;
use strict;
use Carp;
#use List::Util 'max';
*max = \&Math::PlanePath::_max;

use vars '$VERSION', '@ISA';
$VERSION = 3;
use Math::PlanePath;
@ISA = ('Math::PlanePath');
*_divrem = \&Math::PlanePath::_divrem;

use Math::PlanePath::Base::Generic
  'is_infinite',
  'round_nearest';
use Math::PlanePath::Base::Digits
  'round_down_pow';

# uncomment this to run the ### lines
# use Smart::Comments;


use constant n_start => 0;
use constant parameter_info_array =>
  [ { name            => 'parts',
      share_key       => 'parts_surroundoneeight',
      display         => 'Parts',
      type            => 'enum',
      default         => '4',
      choices         => ['4','1','octant','3mid','3side',
                          # 'side'
                         ],
      choices_display => ['4','1','Octant','3 Mid','3 Side',
                          # 'Side'
                         ],
      description     => 'Which parts of the plane to fill.',
    },
  ];
use constant class_x_negative => 1;
use constant class_y_negative => 1;

{
  my %x_negative = (4       => 1,
                    1       => 0,
                    octant  => 0,
                    '3mid'  => 1,
                    '3side' => 1,
                    side    => 0,
                   );
  sub x_negative {
    my ($self) = @_;
    return $x_negative{$self->{'parts'}};
  }
}
{
  my %x_minimum = (4       => undef,
                   1       => 0,
                   octant  => undef,
                   '3side' => undef,
                   '3mid'  => undef,
                   side    => 0,
                  );
  sub x_minimum {
    my ($self) = @_;
    return $x_minimum{$self->{'parts'}};
  }
}
{
  my %y_negative = (4       => 1,
                    1       => 0,
                    octant  => 0,
                    '3mid'  => 1,
                    '3side' => 1,
                    side    => 0,
                   );
  sub y_negative {
    my ($self) = @_;
    return $y_negative{$self->{'parts'}};
  }
}
{
  my %y_minimum = (4       => undef,
                   1       => 0,
                    octant => 0,
                   '3mid'  => undef,
                   '3side' => undef,
                   side    => 1,
                  );
  sub y_minimum {
    my ($self) = @_;
    return $y_minimum{$self->{'parts'}};
  }
}
{
  my %tree_num_children_maximum = (4       => 8,
                                   1       => 5,
                                   octant  => 3,
                                   '3mid'  => 5,
                                   '3side' => 3,
                                   side    => 3,
                                  );
  sub tree_num_children_maximum {
    my ($self) = @_;
    return $tree_num_children_maximum{$self->{'parts'}};
  }
}
# sub rsquared_minimum {
#   my ($self) = @_;
#   return ($self->{'parts'} <= 2   ? 1   # X=0,Y=1
#           : 0);                         # origin X=0,Y=0
# }

#------------------------------------------------------------------------------

sub new {
  my $self = shift->SUPER::new(@_);
  $self->{'parts'} ||= '4';
  return $self;
}


#------------------------------------------------------------------------------
# n_to_xy()

# use Smart::Comments;

my %initial_n_to_xy = (4       => [ [0,0], [1,0], [1,1], [0,1],
                                   [-1,1], [-1,0], [-1,-1], [0,-1], [1,-1] ],
                       1       => [ [0,0], [1,0], [1,1], [0,1] ],
                       octant  => [ [0,0], [1,0], [1,1] ],
                       '3mid'  => [ [0,0], [1,-1], [1,0], [1,1],
                                    [0,1], [-1,1] ],

                       # for 3side table up to N=8 because cell X=1,Y=2 at N=7
                       # is overlapped by two upper octants
                       '3side' => [ [0,0], [1,-1], [1,0], [1,1],
                                    [1,-2], [2,-2], [2,2], [1,2], [0,2] ],

                       side    => [ [0,0], [1,0], [1,1], [2,2], [1,2] ],
                      );

#                     depth=0    1      2    3
my @octant_small_n_to_v = ([0], [0,1], [2], [1,2,3]);
my @octant_mid_n_to_v   = ([0], [-1,0,1]);

sub n_to_xy {
  my ($self, $n) = @_;
  ### OneOfEight n_to_xy(): $n

  if ($n < 0) { return; }
  if (is_infinite($n)) { return ($n,$n); }

  {
    my $int = int($n);
    ### $int
    ### $n
    if ($n != $int) {
      my ($x1,$y1) = $self->n_to_xy($int);
      my ($x2,$y2) = $self->n_to_xy($int+1);
      my $frac = $n - $int;  # inherit possible BigFloat
      my $dx = $x2-$x1;
      my $dy = $y2-$y1;
      return ($frac*$dx + $x1, $frac*$dy + $y1);
    }
    $n = $int;       # BigFloat int() gives BigInt, use that
  }
  my $zero = $n*0;

  my $parts = $self->{'parts'};
  {
    my $initial = $initial_n_to_xy{$parts};
    if ($n <= $#$initial) {
      ### initial_n_to_xy{}: $initial->[$n]
      return @{$initial->[$n]};
    }
  }

  (my $depth, $n) = _n0_to_depth_and_rem($self, $n);
  ### $depth
  ### remainder n: $n
  ### cf this depth n: $self->tree_depth_to_n($depth)
  ### cf next depth n: $self->tree_depth_to_n($depth+1)

  # $hdx,$hdy is the dx,dy offsets which is "horizontal".  Initially this is
  # hdx=1,hdy=0 so horizontal along the X axis, but subsequent blocks rotate
  # around or mirror to point other directions.
  #
  # $vdx,$vdy is similar dx,dy which is "vertical".  Initially vdx=0,vdy=1
  # so vertical along the Y axis.
  #
  # $mirror is true if in a "mirror image" such as the 0<=X<=Y part of the
  # pattern.  The difference is that when plain points are numbered
  # anti-clockwise "upwards" towards the diagonal, but when mirrored instead
  # clockwise "down" from the diagonal.
  #
  # When $mirror is true the octant generated is still reckoned as 0<=Y<=X,
  # but the $hdx,$hdy and $vdx,$vdy are suitably mangled so that this
  # logical first octant ends up in whatever target is desired.  For example
  # the 0<=X<=Y second octant of the pattern starts with hdx=0,hdy=1 and
  # vdx=1,vdy=0, so the "horizontal" is upwards and the "vertical" is to the
  # right.
  #
  # $log2_extras is true if the extra cell at the log2 positions
  # X=3,7,15,31,etc and Y=1 should be included in the pattern.  Initially
  # true, but later in the "lower" block there are no such extra cells.
  #
  # $top_no_extra_pow is a 2^k power if the top of the diagonal at
  # X=pow-1,Y=pow-1 should not be included in the pattern.  Or 0 if this
  # diagonal cell should be included.  Initially true, but later going
  # "lower" followed by "upper" it's the end of the diagonal is not wanted.
  # The first such is at X=8,Y=2 which should not be in the "upper"
  # (mirrored) diagonal coming from X=11,Y=5.  In general if $log2_extras is
  # false then $top_no_extra_pow excludes that log2 cell when going to the
  # "upper" block.
  #
  my $x = 0;
  my $y = 0;
  my $hdx = 1;
  my $hdy = 0;
  my $vdx = 0;
  my $vdy = 1;
  my $mirror = 0;        # plain
  my $log2_extras = 1;   # include cells X=3,7,15,31;Y=1 etc
  my $top_no_extra_pow = 0;

  if ($parts eq 'octant') {
    ### parts=octant ...

  } elsif ($parts eq '1' || $parts eq '2' || $parts eq '4') {
    my $add = _depth_to_octant_added([$depth],[1],$zero);
    ### octant add: $add

    if ($parts eq '4') {
      # Half-plane is 4 octants, less 2 for duplicate diagonal.
      my $hadd = 4*$add-2;
      if ($n >= $hadd) {
        ### initial rotate 180 ...
        $n -= $hadd;
        $hdx = -1;
        $vdy = -1;
      }
    }
    if ($parts eq '2' || $parts eq '4') {
      # Each quadrant is 2 octants, less 1 for duplicate diagonal.
      my $qadd = 2*$add-1;
      if ($n >= $qadd) {
        ### initial rotate +90 ...
        $n -= $qadd;
        ($hdx,$hdy) = (-$hdy,$hdx);
        ($vdx,$vdy) = (-$vdy,$vdx);
      }
    }
    if ($n >= $add) {
      ### initial mirror ...
      $mirror = 1;
      ($hdx,$hdy, $vdx,$vdy)     # mirror by transpose
        = ($vdx,$vdy, $hdx,$hdy);
      $n -= $add;
      $n += 1; # excluding diagonal
    }

  } elsif ($parts eq '3mid') {
    my $add = _depth_to_octant_added([$depth+1],[1],$zero)
      - (_is_pow2($depth+2) ? 2 : 1);
    ### lower of side 1, excluding diagonal: "depth=".($depth+1)." add=".$add
    if ($n < $add) {
      ### lower of side 1 ...
      $hdx = 0; $hdy = -1; $vdx = 1; $vdy = 0;
      $log2_extras = 0;
      $depth += 1;
      $x = -1; $y = 1;
    } else {
      $n -= $add;
      ### past side 1 lower, not past diagonal: "n=$n"

      $add = _depth_to_octant_added([$depth],[1],$zero);
      if ($n < $add) {
        ### upper of side 1 ...
        $vdy = -1;
        $mirror = 1;
      } else {
        $n -= $add;

        if ($n < $add) {
          ### lower of centre ...
        } else {
          $n -= $add;
          $n += 1;  # past diagonal

          if ($n < $add) {
            ### upper of centre ...
            $hdx = 0;
            $hdy = 1;
            $vdx = 1;
            $vdy = 0;
            $mirror = 1;
          } else {
            $n -= $add;

            if ($n < $add) {
              ### upper of side 3 ...
              $hdx = 0;
              $hdy = 1;
              $vdx = -1;
              $vdy = 0;
            } else {
              $n -= $add;
              $n += 1;  # past diagonal

              ### lower of side 3 ...
              $hdx = -1;
              $depth += 1;
              $x = 1; $y = -1;
              $log2_extras = 0;
              $mirror =1;
            }
          }
        }
      }
    }

  } elsif ($parts eq '3side') {
    my $add = (_depth_to_octant_added([$depth+1],[1],$zero)
               - (_is_pow2($depth+2) ? 2 : 1));
    ### lower of side 1, excluding diagonal: "depth=".($depth+1)." add=".$add
    if ($n < $add) {
      ### lower of side 1 ...
      $hdx = 0;
      $hdy = -1;
      $vdx = 1;
      $vdy = 0;
      $log2_extras = 0;
      $depth += 1;
      $x = -1; $y = 1;
    } else {
      $n -= $add;

      $add = _depth_to_octant_added([$depth],[1],$zero);
      ### plain add, including diagonal: "add=$add  cf n=$n"
      if ($n < $add) {
        ### upper of side 1 ...
        $vdy = -1;
        $mirror = 1;
      } else {
        $n -= $add;
        ### not upper of side 1, leaving n: $n

        if ($n < $add) {
          ### lower of centre, including diagonal ...
        } else {
          $n -= $add;
          $n += 1;  # past diagonal
          ### not lower of centre, and past diagonal to n: $n

          $add = _depth_to_octant_added([$depth-1],[1],$zero);
          ### upper of centre, excluding diagonal: "depth=".($depth-1)." add-1=".$add
          if ($n < $add) {
            ### upper of centre ...
            $hdx = 0; $hdy = 1; $vdx = 1; $vdy = 0;
            $x = 1; $y = 1;
            $mirror = 1;
            $depth -= 1;
          } else {
            $n -= $add;
            ### not upper of centre, to n: $n

            if ($n < $add) {
              ### upper of side 3 ...
              $hdx = 0; $hdy = 1; $vdx = -1; $vdy = 0; # rotate -90
              $x = 1; $y = 1;
              $depth -= 1;
            } else {
              $n -= $add;
              $n += 1;  # past diagonal
              ### not upper of side 3, and past diagonal to n: $n

              ### lower of side 3 ...
              $hdx = -1;
              $x = 2;
              $log2_extras = 0;
              $mirror =1;
            }
          }
        }
      }
    }

  } elsif ($parts eq 'side') {
    my $add = _depth_to_octant_added([$depth],[1],$zero);
    ### first octant add: $add
    if ($n < $add) {
      ### first octant ...
    } else {
      ### second octant ...
      $n -= $add;
      $n += 1; # past diagonal
      $hdx = 0; $hdy = 1; $vdx = 1; $vdy = 0;
      $depth += 1;
      $log2_extras = 0;
      $mirror = 1;
      $x = -1; $y = -1;
    }
  }

  ### adjusted to octant style: "depth=$depth remainder n=$n"

  my ($pow,$exp) = round_down_pow ($depth+1, 2);
  ### initial exp: $exp
  ### initial pow: $pow

  for ( ; $exp >= 0; $pow/=2, $exp--) {
    ### at: "pow=$pow exp=$exp depth=$depth n=$n mirror=$mirror log2extras=$log2_extras topnopow=$top_no_extra_pow  xy=$x,$y  h=$hdx,$hdy v=$vdx,$vdy"
    ### assert: $depth >= 1
    ### assert: $mirror == 0 || $mirror == 1

    if ($depth < $pow) {
      ### block 0 ...
      $top_no_extra_pow = 0;
      next;
    }

    if ($depth <= 3) {
      if ($mirror) {
        ### mirror small depth ...
        if ($depth == $top_no_extra_pow-1) {
          $n += 1;
          ### inc n for top_no_extra_pow: "to n=$n"
        }
        ### assert: $n <= $#{$octant_small_n_to_v[$depth]}
        $n = -1-$n;  # perl negative index to read array in reverse
      } else {
        ### small depth ...
        if (! $log2_extras && $depth == 3) {
          $n += 1;
          ### inc n for no log2_extras: "to n=$n"
        }
        ### assert: $n <= $#{$octant_small_n_to_v[$depth]}
      }
      my $v = $octant_small_n_to_v[$depth][$n];
      ### hv: "h=$depth, v=$v"
      $x += $depth*$hdx + $v*$vdx;     # $depth is "$h" horizontal position
      $y += $depth*$hdy + $v*$vdy;
      last;
    }

    $x += $pow * ($hdx + $vdx);   # $pow along diagonal
    $y += $pow * ($hdy + $vdy);
    $depth -= $pow;
    ### diagonal to: "depth=$depth  xy=$x,$y"

    if ($depth <= 1) {
      ### mid two levels ...
      if ($mirror) {
        ### negative perl array index to reverse for mirror state ...
        $n = -1-$n;
      }
      my $v = $octant_mid_n_to_v[$depth][$n];
      ### hv: "h=$depth v=$v"
      $x += $depth*$hdx + $v*$vdx;   # $depth is "$h" horizontal position
      $y += $depth*$hdy + $v*$vdy;
      last;
    }

    if ($mirror == 0) { # plain

      # See if $n within lower.
      # Not at depth+1==pow since lower has already finished then.
      #
      if ($depth+1 < $pow) {
        my $add = _depth_to_octant_added([$depth+1],[1],$zero);
        if (_is_pow2($depth+2)) {
          ### add lower decreased for remaining depth+2 a power-of-2 ...
          $add -= 1;
        }
        $add -= 1;
        ### add in lower, excluding diagonal: $add
        if ($n < $add) {
          ### lower, rotate +90 ...
          $top_no_extra_pow = 0;
          $log2_extras = 0;
          $depth += 1;
          ### assert: $depth < $pow
          ($hdx,$hdy, $vdx,$vdy)    # rotate 90 in direction v toward h
            = (-$vdx,-$vdy, $hdx,$hdy);
          $x -= $hdx + $vdx;
          $y -= $hdy + $vdy;
          next;
        }
        $n -= $add;
      } else {
        ### skip lower at depth==pow-1 ...
      }

      # See if $n within upper.
      #
      my $add = _depth_to_octant_added([$depth],[1],$zero);
      if (! $log2_extras && $depth+1 == $pow) {
        ### add upper decreased for no log2_extras at depth=pow-1 ...
        $add -= 1;
      }
      ### add in upper, including diagonal: $add
      if ($n < $add) {
        ### upper, mirror ...
        $mirror = 1;
        $vdx = -$vdx;  # flip vertically
        $vdy = -$vdy;
        $top_no_extra_pow = ($log2_extras ? 0 : $pow);
        $log2_extras = 1;
        next;
      }
      $n -= $add;
      ### assert: $n < $add

      # Otherwise $n is within extend.
      #
      ### extend ...
      $top_no_extra_pow /= 2;
      $log2_extras = 1;

    } else {
      # $mirror == 1, mirrored

      # See if $n within extend.
      #
      my $eadd = my $add = _depth_to_octant_added([$depth],[1],$zero);
      $top_no_extra_pow /= 2;  # since after $depth+=$pow
      if ($depth == $top_no_extra_pow - 1) {
        ### add extend decreased for no top extra ...
        $eadd -= 1;
      }
      ### add in extend: $eadd
      if ($n < $eadd) {
        ### extend ...
        $log2_extras = 1;
        next;
      }
      $n -= $eadd;

      # See if $n within upper.
      #
      ### add in upper, including diagonal: "$add cf n=$n"
      if ($n < $add) {
        ### upper, unmirror ...
        $top_no_extra_pow = ($log2_extras ? 0 : $pow);
        $log2_extras = 1;
        $mirror = 0;
        $vdx = -$vdx;  # flip vertically
        $vdy = -$vdy;
        next;
      }
      $n -= $add;

      # Otherwise $n is within lower.
      #
      $n += 1; # past diagonal
      ### lower, rotate: "n=$n"
      ### assert: $n < _depth_to_octant_added([$depth+1],[1],$zero)
      $top_no_extra_pow = 0;
      $log2_extras = 0;
      $depth += 1;
      ### assert: $depth < $pow
      ($hdx,$hdy, $vdx,$vdy)    # rotate 90 in direction v toward h
        = (-$vdx,-$vdy, $hdx,$hdy);
      $x -= $hdx + $vdx;
      $y -= $vdx + $vdy;
    }
  }

  ### n_to_xy() return: "$x,$y  (depth=$depth n=$n)"
  return ($x,$y);
}

# no Smart::Comments;

# use Smart::Comments;

# ($depth, $nrem) = _n0_to_depth_and_rem($self,$n)
#
# _n0_to_depth_and_rem() finds the tree $depth level containing $n and
# returns that $depth and the offset of $n into that level, being
# $n - $self->tree_depth_to_n($depth).
#
# The current approach is a binary search for the bits of depth which have
# tree_depth_to_n($depth) <= $n.
#
# Ndepth grows as roughly depth*depth, so this is about log4(N) many bsearch
# compares.  Maybe for modest N a table of depth->N could be used for the
# search (and for tree_depth_to_n()).  It would cover up to about sqrt(N),
# so for large N would still need some searching code.
#
# quadrant(2^k) = (4*4^k + 6*k + 14) / 9
# N*9/4 = 4^k + 6/4*k + 14/4
# parts=1      N*9 to round up to next power
# parts=octant N*18
# parts=4      N*9/4 = N*3 as estimate
# parts=3      N*9/4 = N*3 too
#
my %parts_to_depth_multiplier = (4       => 3,
                                 1       => 9,
                                 octant  => 18,
                                 '3mid' => 3,
                                 '3side' => 3,
                                 side    => 9,
                                );
sub _n0_to_depth_and_rem {
  my ($self, $n) = @_;
  ### _n0_to_depth_and_rem(): "n=$n   parts=$self->{'parts'}"

  my ($pow,$exp) = round_down_pow
    ($n * $parts_to_depth_multiplier{$self->{'parts'}},
     4);
  if (is_infinite($exp)) {
    return ($exp,0);
  }
  ### $pow
  ### $exp

  my $depth = 0;
  my $n_depth = 0;
  $pow = 2 ** $exp;  # pow=2^exp down to 1, inclusive

  while ($exp-- >= 0) {
    my $try_depth = $depth + $pow;
    my $try_n_depth = $self->tree_depth_to_n($try_depth);

    ### $depth
    ### $pow
    ### $try_depth
    ### $try_n_depth

    if ($try_n_depth <= $n) {
      ### use this tried depth ...
      $depth = $try_depth;
      $n_depth = $try_n_depth;
    }
    $pow /= 2;
  }

  ### _n0_to_depth_and_rem() final ...
  ### $depth
  ### remainder: $n - $n_depth

  return ($depth, $n - $n_depth);
}

# no Smart::Comments;

#------------------------------------------------------------------------------
# xy_to_n()

my @yxoct_to_n = ([     0, 1 ],   # Y=0
                  [ undef, 2 ]);  # Y=1
my @yx1_to_n = ([ 0, 1 ],   # Y=0
                [ 3, 2 ]);  # Y=1
my @yx3_to_n = ([     0, 2, undef ],   # Y=0   X=0,1,-1
                [     4, 3,     5 ],   # Y=1
                [ undef, 1, undef ]);  # Y=-1
my @yx4_to_n = ([ 0, 1, 5 ],   # Y=0   X=0,1,-1
                [ 3, 2, 4 ],   # Y=1
                [ 7, 8, 6 ]);  # Y=-1
my @yx3mid_to_n = ([     0, 2, undef ],   # Y=0   X=0,1,-1
                   [     4, 3, 5     ],   # Y=1
                   [ undef, 1, undef ]);  # Y=-1
my @yx3side_to_n = ([     0, 2, undef ],   # Y=0   X=0,1,-1
                    [ undef, 3, undef ],   # Y=1
                    [     8, 7, 16    ],   # Y=2
                    [ undef, 4, undef ],   # Y=-2
                    [ undef, 1, undef ]);  # Y=-1
my @yxside_to_n = ([     0, 1 ],   # Y=0   X=0,1,-1
                   [ undef, 2 ]);   # Y=1

# N values relative to tree_depth_to_n() start of the depth level
my @yx_to_n = ([ [     0,     0,          ],  # plain
                 [ undef,     1, undef, 0 ],
                 [ undef, undef,     0, 1 ],
                 [ undef, undef, undef, 2 ] ],
               [ [     0,     1,          ],  # mirror
                 [ undef,     0, undef, 2 ],
                 [ undef, undef,     0, 1 ],
                 [ undef, undef, undef, 0 ] ]);

#use Smart::Comments;

sub xy_to_n {
  my ($self, $x, $y) = @_;
  ### OneOfEight xy_to_n(): "$x, $y"

  # {
  #   require Math::PlanePath::OneOfEightByCells;
  #   my $cells = ($self->{'cells'} ||= Math::PlanePath::OneOfEightByCells->new (parts => $self->{'parts'}));
  #   return $cells->xy_to_n($x,$y);
  # }

  $x = round_nearest ($x);
  $y = round_nearest ($y);
  if (is_infinite($x)) {
    return $x;
  }
  if (is_infinite($y)) {
    return $y;
  }

  my ($pow,$exp) = round_down_pow (max(abs($x),abs($y))+2, 2);
  ### initial pow: "exp=$exp  pow=$pow"
  ### from abs(x): abs($x)
  ### from abs(y): abs($y)
  ### from max: max(abs($x),abs($y))

  if (is_infinite($exp)) {
    return $exp;
  }

  my $zero = $x * 0 * $y;
  my @add_offset;
  my @add_mult;
  my @add_log2_extras;
  my @add_top_no_extra_pow;
  my $mirror = 0;
  my $log2_extras = 1;
  my $top_extra = 1;
  my $top_no_extra_pow = 0;
  my $depth = 0;
  my $n = $zero;

  my $parts = $self->{'parts'};
  if ($parts eq 'octant') {
    ### parts==octant ...
    if ($y < 0 || $y > $x) {
      return undef;
    }
    if ($x <= 1 && $y <= 1) {
      return $yxoct_to_n[$y][$x];
    }

  } elsif ($parts eq '1' || $parts eq '4') {
    my $mult = 0;
    if ($parts eq '1') {
      ### parts==1 ...
      if ($x < 0 || $y < 0) {
        return undef;
      }
      if ($x <= 1 && $y <= 1) {
        return $yx1_to_n[$y][$x];
      }
    } else {
      ### parts==4 ...
      if (abs($x) <= 1 && abs($y) <= 1) {
        return $yx4_to_n[$y][$x];
      }
      if ($y < 0) {
        ### quad 3 or 4, rotate 180 ...
        $mult = 4;  # past first,second quads
        $n -= 2;    # unduplicate diagonals
        $x = -$x;  # rotate 180
        $y = -$y;
      }
      if ($x < 0) {
        ### quad 2 (or 4), rotate 90 ...
        $mult += 2;
        $n -= 1;  # unduplicate diagonal
        ($x,$y) = ($y,-$x);  # rotate -90
      }
    }

    ### now in first quadrant: "x=$x y=$y"
    if ($y > $x) {
      ### second octant, transpose and mirror ...
      ($x,$y) = ($y,$x);
      $mult++;
      $n -= 1;  # unduplicate diagonal
      $mirror = 1;
    }
    if ($mult) {
      push @add_offset,           0;
      push @add_mult,             $mult;
      push @add_top_no_extra_pow, 0;
      push @add_log2_extras,      1;
    }

  } elsif ($parts eq '3mid') {
    ### parts==3mid ...
    if (abs($x) <= 1 && abs($y) <= 1) {
      ### 3mid small: $yx3mid_to_n[$y][$x]
      return $yx3mid_to_n[$y][$x];
    }
    if ($y < 0) {
      if ($x < 0) {
        ### third quadrant, no such point ...
        return undef;
      }
      $y = -$y;
      if ($y >= $x) {
        ### block 0 lower ...
        $log2_extras = 0;
        ($x,$y) = ($y+1,$x+1);
        $depth = -1;
      } else {
        ### block 1 upper ...
        $mirror = 1;

        ### past block 0 lower, excluding diagonal ...
        push @add_offset,          -1;
        push @add_mult,             1;
        push @add_top_no_extra_pow, 0;
        push @add_log2_extras,      0;
        $n -= 1;  # excluding diagonal
      }
    } else {
      if ($x >= 0) {
        if ($y <= $x) {
          ### block 2 first octant ...

          ### past block 0 lower, excluding diagonal ...
          push @add_offset,          -1;
          push @add_mult,             1;
          push @add_top_no_extra_pow, 0;
          push @add_log2_extras,      0;
          $n -= 1;  # excluding diagonal

          ### past block 1 ...
          push @add_offset,           0;
          push @add_mult,             1;
          push @add_top_no_extra_pow, 0;
          push @add_log2_extras,      1;

        } else {
          ### block 3 second octant ...
          ($x,$y) = ($y,$x);
          $mirror = 1;

          ### past block 0 lower, excluding diagonal ...
          push @add_offset,          -1;
          push @add_mult,             1;
          push @add_top_no_extra_pow, 0;
          push @add_log2_extras,      0;
          $n -= 1;  # excluding diagonal

          ### past blocks 1,2, excluding leading diagonal ...
          push @add_offset,           0;
          push @add_mult,             2;
          push @add_top_no_extra_pow, 0;
          push @add_log2_extras,      1;
          $n -= 1;  # excluding leading diagonal
        }
      } else {
        ### second quadrant ...
        $x = -$x;
        if ($y >= $x) {
          ### block 4 third octant ...
          ($x,$y) = ($y,$x);

          ### past block 0 lower, excluding diagonal ...
          push @add_offset,          -1;
          push @add_mult,             1;
          push @add_top_no_extra_pow, 0;
          push @add_log2_extras,      0;
          $n -= 1;  # excluding diagonal

          ### past blocks 1,2,3 excluding leading diagonal ...
          push @add_offset,           0;
          push @add_mult,             3;
          push @add_top_no_extra_pow, 0;
          push @add_log2_extras,      1;
          $n -= 1;  # excluding leading diagonal

        } else {
          ### block 5 fourth octant ...
          $x += 1; $y += 1;
          $mirror = 1;
          $depth = -1;
          $log2_extras = 0;

          ### past block 0 lower, excluding diagonal ...
          push @add_offset,          -1;
          push @add_mult,             1;
          push @add_top_no_extra_pow, 0;
          push @add_log2_extras,      0;
          $n -= 1;  # excluding diagonal

          push @add_offset,           0;
          push @add_mult,             4;
          push @add_top_no_extra_pow, 0;
          push @add_log2_extras,      1;
          $n -= 2;  # unduplicate two diagonals
        }
      }
    }

  } elsif ($parts eq '3side') {
    ### parts==3side ...
    if (abs($x) <= 1 && abs($y) <= 2) {
      ### 3side small: $yx3side_to_n[$y][$x]
      return $yx3side_to_n[$y][$x];
    }
    if ($y < 0) {
      if ($x < 0) {
        ### third quadrant, no such point ...
        return undef;
      }
      $y = -$y;
      if ($y >= $x) {
        ### block 0 lower ...
        $log2_extras = 0;
        ($x,$y) = ($y+1,$x+1);
        $depth = -1;
      } else {
        ### block 1 upper ...
        $mirror = 1;

        ### past block 0 lower, excluding diagonal ...
        push @add_offset,          -1;
        push @add_mult,             1;
        push @add_top_no_extra_pow, 0;
        push @add_log2_extras,      0;
        $n -= 1;  # excluding diagonal
      }
    } else {
      if ($x > 0) {
        if ($y <= $x) {
          ### block 2 first octant ...

          ### past block 0 lower, excluding diagonal ...
          push @add_offset,          -1;
          push @add_mult,             1;
          push @add_top_no_extra_pow, 0;
          push @add_log2_extras,      0;
          $n -= 1;  # excluding diagonal

          ### past block 1 ...
          push @add_offset,           0;
          push @add_mult,             1;
          push @add_top_no_extra_pow, 0;
          push @add_log2_extras,      1;

        } else {
          ### block 3 second octant ...
          ($x,$y) = ($y-1,$x-1);
          $depth = 1;
          $mirror = 1;

          ### past block 0 lower, excluding diagonal ...
          push @add_offset,          -1;
          push @add_mult,             1;
          push @add_top_no_extra_pow, 0;
          push @add_log2_extras,      0;
          $n -= 1;  # excluding diagonal

          ### past block 1,2, excluding leading diagonal ...
          push @add_offset,           0;
          push @add_mult,             2;
          push @add_top_no_extra_pow, 0;
          push @add_log2_extras,      1;
          $n -= 1;  # excluding leading diagonal
        }
      } else {
        ### second quadrant ...
        $x = 2-$x;
        ### X mirror to: "x=$x y=$y"

        if ($y >= $x) {
          ### block 4 third octant ...
          ($x,$y) = ($y-1,$x-1);
          ### transpose to: "x=$x y=$y"
          $depth = 1;

          ### past block 0 lower, excluding diagonal ...
          push @add_offset,          -1;
          push @add_mult,             1;
          push @add_top_no_extra_pow, 0;
          push @add_log2_extras,      0;
          $n -= 1;  # excluding diagonal

          ### past block 1,2, excluding leading diagonal ...
          push @add_offset,           0;
          push @add_mult,             2;
          push @add_top_no_extra_pow, 0;
          push @add_log2_extras,      1;
          $n -= 1;  # excluding leading diagonal

          ### past block 3 ...
          push @add_offset,           1;
          push @add_mult,             1;
          push @add_top_no_extra_pow, 0;
          push @add_log2_extras,      1;

        } else {
          ### block 5 fourth octant ...
          $mirror = 1;
          $log2_extras = 0;

          ### past block 0 lower, excluding diagonal ...
          push @add_offset,          -1;
          push @add_mult,             1;
          push @add_top_no_extra_pow, 0;
          push @add_log2_extras,      0;
          $n -= 1;  # excluding diagonal

          ### past block 1,2, excluding leading diagonal ...
          push @add_offset,           0;
          push @add_mult,             2;
          push @add_top_no_extra_pow, 0;
          push @add_log2_extras,      1;
          $n -= 1;  # unduplicate leading diagonal

          ### past block 3,4 ...
          push @add_offset,           1;
          push @add_mult,             2;
          push @add_top_no_extra_pow, 0;
          push @add_log2_extras,      1;
          $n -= 1;  # excluding block4 diagonal
        }
      }
    }

  } elsif ($parts eq 'side') {
    ### parts==side ...
    if ($x < 0 || $y < 0) {
      return undef;
    }
    if ($x <= 1 && $y <= 1) {
      return $yxside_to_n[$y][$x];
    }

    if ($y > $x) {
      ### second octant ...
      ($x,$y) = ($y+1,$x+1);
      $depth = -1;
      $mirror = 1;
      $log2_extras = 0;
      $n -= 1;  # excluding diagonal

      ### past block 1 ...
      push @add_offset,           0;
      push @add_mult,             1;
      push @add_top_no_extra_pow, 0;
      push @add_log2_extras,      1;
    }


  } elsif ($parts eq '2') {
    ### parts==2 ...
    # if ($x == 0) {
    #   if ($y == 1) { return 0; }
    # }
    # if ($y == 1) {
    #   if ($x == 1) { return 1; }
    #   if ($x == -1) { return 2; }
    # }
    # if ($x < 0) {
    #   ### initial mirror second quadrant ...
    #   $x = -$x;
    #   $mirror = 1;
    #   push @add_offset, -1;
    #   push @add_mult, 1;
    # }
  }

  if ($x == 0 || $y == 0) {
    ### nothing on axes after origin ...
    return undef;
  }

  for (;;) {
    ### at: "x=$x,y=$y  n=$n  pow=$pow depth=$depth mirror=$mirror log2_extras=$log2_extras top_extra=$top_extra top_no_extra_pow=$top_no_extra_pow"
    ### assert: $x >= 0
    ### assert: $x < 2 * $pow
    ### assert: $y >= 0
    ### assert: $y <= $x

    if ($x <= 3) {
      ### loop small XY ...
      ### $top_no_extra_pow

      if ($x == 3) {
        if (! $log2_extras) {
          if ($y == 1) {
            ### no log2_extras ...
            return undef;
          }
          if (! $mirror) {
            ### no log2_extras, N decrement, (not mirrored) ...
            $n -= 1;
          }
        }
        if ($top_no_extra_pow == 4) {
          if ($y == 3) {
            ### no top extra, so no such point ...
            return undef;
          }
          ### top_no_extra_pow, N decrement by mirror: $mirror
          $n -= $mirror;
        }
      }

      my $nyx = $yx_to_n[$mirror][$y][$x];
      ### $nyx
      if (! defined $nyx) {
        ### no such point ...
        return undef;
      }
      $n += $nyx;
      $depth += $x;
      last;
    }

    if ($x == $pow) {
      if ($y == $pow) {
        ### mid X=pow,Y=pow, stop ...
        $depth += $pow;
        last;
      }
      ### X=pow no such point ...
      return undef;
    } elsif ($x == $pow+1) {
      if ($y == $pow-1) {
        ### mid X=pow+1,Y=pow-1, stop ...
        $depth += $pow+1;
        $n += ($mirror ? 2 : 0);
        last;
      }
      if ($y == $pow) {
        ### mid X=pow+1,Y=pow, stop ...
        $depth += $pow+1;
        $n += 1;
        last;
      }
      if ($y == $pow+1) {
        ### mid X=pow+1,Y=pow+1, stop ...
        $depth += $pow+1;
        $n += ($mirror ? 0 : 2);
        last;
      }
    }

    if ($x < $pow) {
      ### base block ...
      $top_no_extra_pow = 0;

    } else {
      $x -= $pow;
      $depth += $pow;
      if ($y < $pow) {
        $y = $pow-$y;
        ### Y flip to: $y

        if ($y > $x) {
          ### block lower, excluding diagonal ...
          ($x,$y) = ($y+1,$x+1);
          ### rotate to: "x=$x y=$y"
          ### assert: $y >= 0
          unless ($y && $x < $pow) {
            ### Y=0 or X>=pow, no such point ...
            return undef;
          }
          $top_no_extra_pow = 0;
          $log2_extras = 0;
          $depth -= 1;
          if ($mirror) {
            ### offset past extend,upper, undup diagonal, (mirrored) ...
            push @add_offset,           $depth+1;
            push @add_mult,             2;
            push @add_top_no_extra_pow, $top_no_extra_pow/2;
            push @add_log2_extras,      1;
            $n -= 1;  # duplicated diagonal upper,lower
          }

        } else {
          ### block upper ...
          if ($mirror) {
            ### offset past extend (mirrored) ...
            push @add_offset,           $depth;
            push @add_mult,             1;
            push @add_top_no_extra_pow, $top_no_extra_pow/2;
            push @add_log2_extras,      1;
          } else {
            if ($x < $pow-1) {
              ### offset past lower, unduplicate diagonal, (not mirrored) ...
              push @add_offset, $depth-1;
              push @add_mult, 1;
              push @add_top_no_extra_pow, 0;
              push @add_log2_extras, 0;
              $n -= 1;  # duplicated diagonal upper,lower
            }
          }
          $top_no_extra_pow = ($log2_extras ? 0 : $pow);
          $log2_extras = 1;
          $mirror ^= 1;
        }
      } else {
        ### extend, same ...
        unless ($x) {
          ### on X=0, past block3, no such point ...
          return undef;
        }
        if ($mirror) {
          ### no offset past lower at X=pow-1 ...
        } else {
          if ($x < $pow-1) {
            ### offset past lower (not mirrored) ...
            push @add_offset,           $depth-1;
            push @add_mult,             1;
            push @add_top_no_extra_pow, 0;
            push @add_log2_extras,      0;
            $n -= 1;  # duplicated diagonal
          }
          ### offset past upper (not mirrored) ...
          push @add_offset,           $depth;
          push @add_mult,             1;
          push @add_top_no_extra_pow, ($log2_extras ? 0 : $pow);
          push @add_log2_extras,      1;
          # if (! $log2_extras) {
          #   ### no log2_extras so N decrement ...
          #   $n -= 1;
          # }
        }
        $y -= $pow;
        $log2_extras = 1;
        $top_extra = 1;
        $top_no_extra_pow /= 2;
      }
    }

    if (--$exp < 0) {
      ### final xy: "$x,$y"
      if ($x == 1 && $y == 1) {
      } elsif ($x == 1 && $y == 2) {
        $depth += 1;
      } else {
        ### not in final position ...
        return undef;
      }
      last;
    }
    $pow /= 2;
  }


  ### final depth: $depth
  ### $n
  ### depth_to_n: $self->tree_depth_to_n($depth)
  ### add_offset: join(',',@add_offset)
  ### add_mult:   join(',',@add_mult)
  ### assert: scalar(@add_offset) == scalar(@add_mult)
  ### assert: scalar(@add_offset) == scalar(@add_log2_extras)
  ### assert: scalar(@add_offset) == scalar(@add_top_no_extra_pow)

  $n += $self->tree_depth_to_n($depth);

  if (@add_offset) {
    foreach my $i (0 .. $#add_offset) {
      my $d = $add_offset[$i] = $depth - $add_offset[$i];

      if ($d+1 == $add_top_no_extra_pow[$i]) {
        ### no top_extra, decrement applied: "d=$d"
        $n -= 1;
      }
      if (! $add_log2_extras[$i] && $d >= 3 &&  _is_pow2($d+1)) {
        ### no log2_extras, decrement applied: "depth d=$d"
        $n -= 1;
      }

      ### add: "depth=$add_offset[$i] is "._depth_to_octant_added([$add_offset[$i]],[1],$zero)." x $add_mult[$i]   log2_extras=$add_log2_extras[$i] top_no_extra_pow=$add_top_no_extra_pow[$i]"
    }

    ### total add: _depth_to_octant_added ([@add_offset], [@add_mult], $zero)
    $n += _depth_to_octant_added (\@add_offset, \@add_mult, $zero);
  }

  ### xy_to_n() return n: $n
  return $n;
}

# no Smart::Comments;


#------------------------------------------------------------------------------
# rect_to_n_range()

# not exact
sub rect_to_n_range {
  my ($self, $x1,$y1, $x2,$y2) = @_;
  ### OneOfEight rect_to_n_range(): "$x1,$y1  $x2,$y2"

  $x1 = round_nearest ($x1);
  $y1 = round_nearest ($y1);
  $x2 = round_nearest ($x2);
  $y2 = round_nearest ($y2);
  ($x1,$x2) = ($x2,$x1) if $x1 > $x2;
  ($y1,$y2) = ($y2,$y1) if $y1 > $y2;
  my $parts = $self->{'parts'};

  my $extra = ($parts eq '3side' ? 1 : 0);
  my ($pow,$exp) = round_down_pow (max(1,
                                       abs($x1),
                                       abs($x2)+$extra,
                                       abs($y1),
                                       abs($y2)+$extra),
                                   2);

  if ($parts eq '1') {
    # (total(2^k)+3)/4 = ((16*4^k + 24*k - 7)/9 + 3)/4
    #                  = (16*4^k + 24*k - 7 + 27)/9/4
    #                  = (16*4^k + 24*k + 20)/9/4
    #                  = (4*4^k + 6*k + 5)/9
    # applied to k=exp+1 2*pow=2^k
    #                  = (4* 2*pow * 2*pow + 6*(exp+1) + 5)/9
    #                  = (16*pow*pow + 6*exp + 11)/9
    return (0, (16*$pow*$pow + 6*$exp + 11) / 9);
  }

  # $parts eq '4'
  # total(2^k) = (16*4^k + 24*k - 7)/9
  # applied to k=exp+1 2*pow=2^k
  #            = (16 * 2*pow * 2*pow + 24*(exp+1) - 7) / 9
  #            = (64*pow*pow + 24*exp + 24-7) / 9
  #            = (64*pow*pow + 24*exp + 17) / 9
  return (0, (64*$pow*$pow + 24*$exp + 17) / 9);
}

#------------------------------------------------------------------------------
# tree

sub tree_n_to_depth {
  my ($self, $n) = @_;
  ### tree_n_to_depth(): "$n"

  if ($n < 0) {
    return undef;
  }
  my ($depth) = _n0_to_depth_and_rem($self, int($n));
  ### n0 depth: $depth
  return $depth;
}

my @surround8_dx = (1, 1, 0, -1, -1, -1,  0,  1);
my @surround8_dy = (0, 1, 1,  1,  0, -1, -1, -1);

sub tree_n_children {
  my ($self, $n) = @_;
  ### tree_n_children(): $n

  my ($x,$y) = $self->n_to_xy($n)
    or return;
  ### $x
  ### $y

  my $depth = $self->tree_n_to_depth($n) + 1;
  return
    sort {$a<=>$b}
      grep { $self->tree_n_to_depth($_) == $depth }
        map { $self->xy_to_n_list($x + $surround8_dx[$_],
                                  $y + $surround8_dy[$_]) }
          0 .. $#surround8_dx;
}
sub tree_n_parent {
  my ($self, $n) = @_;

  if ($n < 0) {
    return undef;
  }
  my ($x,$y) = $self->n_to_xy($n)
    or return undef;
  my $parent_depth = $self->tree_n_to_depth($n) - 1;

  foreach my $i (0 .. $#surround8_dx) {
    my $pn = $self->xy_to_n($x + $surround8_dx[$i],
                            $y + $surround8_dy[$i]);
    if (defined $pn && $self->tree_n_to_depth($pn) == $parent_depth) {
      return $pn;
    }
  }
  return undef;
}

#
#    1        1  1
#    2        9  1001
#    4       33  100001
#    8      121  1111001
#   16      465  111010001
#   32     1833  11100101001
#   64     7297  1110010000001
#  128    29145  111000111011001
#  256   116529  11100011100110001
#  512   466057  1110001110010001001
# 1024  1864161  111000111000111100001
#
# before 1  2 3  4 5  6  7  8  9 10 11 12 13  14  15   16
# side = 0, 1,3, 6,9,14,21, 27,30,35,43,52,63,80,100, 112
#                              3,5,8,9,11,17,20,12
#
# side(5)  = side(4) + side(2) + 2*side(1) + 2
#          = 6 + 1 + 2*0 + 2 = 9
# side(9)  = side(8) + side(1) + 2
# side(10) = side(8) + side(3) + 2*side(2) + 3 = 27 + 3 + 2*1 + 3 = 35
# side(11) = side(8) + side(4) + 2*side(3) + log2(4/4) + 3 = 27+6+2*3+1+3 = 42
#
# side(2^k) = 4*side(2^(k-1)) -1   block 1 missing one in corner
#                             + k-2  block 2 extra lower
#                             + 3    centre A,B,C
#           = 4*side(2^(k-1)) + k
#   = k + (k-1)*4^1 + (k-2)*4^2 + ... + 2*4^(k-1) + 4^k
# eg. k=3  3+2*4+1*16 = 27
#          = 1 + 1+4 + 1+4+16 = 1 + 5 + 21
#    sum 1+4+...+4^(k-1) = (4^k-1)/3
# side(2^k) = (4^k-1)/3 + (4^(k-1)-1)/3 + ... + (4^1-1)/3
#           = (4^k - 1 + 4^(k-1) - 1 + ... + 4^1 - 1)/3    # k terms 4^k to 4^1
#           = (4^k + 4^(k-1) + ... + 4^1 - k)/3
#           = (4^k + 4^(k-1) + ... + 4^1 + 4^0 - 1 - k)/3
#           = ((4^(k+1)-1)/3 - 1 - k)/3
#           = (4^(k+1)-1 - 3*k - 3)/9
#           = (4*4^k - 3*k - 4)/9
#
# side(2^1=2) = 1
# side(2^2=4) = 1 + 1-1 + 1+0 + 1 + 3 = 6 = 4*1 + 2 = 4^1 + 2
# side(2^3=8) = 6 + 6-1 + 6+1 + 6 + 3 = 27 = 4*6 + 3 = 4^2 + 4*2+3
# side(2^4=16) = 27+27-1 +27+2 +27 + 3 = 112 = 4*27 + 4 = 4^3 + 16*2+4*3+4
#
#
#
#  centre(2^k) = 2*side(2^(k-1)) + 2*centre(2^(k-1))
#  centre(1) = 1
#  centre(2) = 4
#  centre(4) = 2*side(2) + 2*centre(2)
#            = 2*side(2) + 2*4
#            = 2*1 + 2*4 = 10
#  centre(8) = 2*side(4) + 2*centre(4)  = 2*6+2*10 = 32
#            = 2*side(4) + 2*(2*side(2) + 2*4)
#            = 2*side(4) + 4*side(2) + 4*4
#            = 2*6 + 4*1 + 4*4 = 32
#  centre(16) = 2*side(4) + 2*centre(4) = 2*6+2*10 = 32
#            = 2*side(8) + 4**side(4) + 8*side(2) + 8
#            = 2*27 + 4*6 + 8*1 + 8 = 94
#
# 4parts = 4*centre - 7
# 4parts(4) = 4*10-7 = 33
# 4parts(8) = 4*32-7 = 121
#
# 3side total 0,1, 4, 9,17
#              +1 +3 +5 +8
#
# centre(2^k)
#   = 2*side(2^(k-1)) + 2*centre(2^(k-1))
#   = 2*side(2^(k-1) + 2^2*side(2^(k-1) + ... + 2^(k-1)*side(2^1) + 2^(k-1)*4
#   k-1 many terms, and constant at end
# side(2^k) = (4*4^k - 3*k - 4)/9
#
# constant part
# 2 + 4 + ... + 2^(k-1)
#   = 2^k - 2
# eg. k=2 2
# eg. k=3 2 + 4 = 6
# eg. k=4 2 + 4 + 8 = 14
#
# linear part
# 2*(k-1) + 4*(k-2) + ... + 2^(k-1)*(1) + 2^k*(0)
#   = 2^(k-1)-1 + 2^(k-2)-1 + ... + 2-1
#   = 2*2^k - 2*k - 2
# eg. k=2 2*1 = 2
# eg. k=3 2*2 + 4*1 = 8
# eg. k=4 2*3 + 4*2 + 8*1 =  22
# eg. k=5 2*4 + 4*3 + 8*2 + 16*1 = 52
#
# exponential part
# 2*4^(k-1) + 4*4^(k-2) + 8*4^(k-3) + ... + 2^(k-1)*4^1
#   = 2^(2k-2+1) + 2^(2k-4+2) + 2^(2k-6+3) + ... + 2^(k+1)
#   = 2^(2k-1) + 2^(2k-2) + 2^(2k-3) + ... + 2^(k+1)
#   = 2^(k+1) * [ 2^(k-2) + 2^(k-3) + 2^(k-4) + ... + 2^(0) ]
#   = 2^(k+1) * (2^(k-1) - 1)
#   = 2^k * (2^k - 2)
# eg. k=2 2*4^1 = 8
# eg. k=3 2*4^2 + 4*4^1 = 48
# eg. k=4 2*4^3 + 4*4^2 + 8*4^1 = 224
# eg. k=5 2*4^4 + 4*4^3 + 8*4^2 + 16*4^1 = 960
#
# centre(2^k) = (4*(2^k * (2^k - 2)) - 3*(2*2^k-2*k-2) - 4*(2^k-2)) / 9 + 2*2^k
# eg. k=2  sidepart = 2*1 = 1  plus
# eg. k=3  sidepart = 2*6 + 4*1 = 16
# eg. k=4  sidepart = 2*27 + 4*6 + 8*1 = 86
#   = (4*(2^k * (2^k - 2)) - 3*(2*2^k-2*k-2) - 4*(2^k-2)) / 9 + 2*2^k
#   = (4*2^k*(2^k - 2) - 6*2^k + 3*2*k + 6 - 4*2^k + 8 + 18*2^k) / 9
#   = (4*2^k*2^k - 8*2^k - 6*2^k + 3*2*k - 4*2^k + 18*2^k + 14) / 9
#   = (4*2^k*2^k + 6*k + 14) / 9
#   = (4*depth^2 + 6*k + 14) / 9
#
# centre(2^k) = (4*4^k + 6*k + 14) / 9
# side(2^k)   = (4*4^k - 3*k - 4) / 9
# diff = (9k+18)/9 = k+2
# double centre(2^(k+1)) - 4*centre(2^k)
#   = (4*4^(k+1) + 6*(k+1) + 14 - 4*(4*4^k + 6*k + 14)) / 9
#   = (4*4*4^k + 6*k + 6 + 14 - 4*4*4^k - 4*6*k - 4*14) / 9
#   = (6*k - 4*6*k + 6 + 14 - 4*14) / 9
#   = (-18*k - 36) / 9
#   = -2*k - 4
# smaller than 4* on each doubling
# 6k+14 term only adds extra 6, doesn't go 4*(6k+14)
#
# side(pow+rem) = side(pow) + side(rem+1)   -1 if rem+1=pow
#                           + side(rem)
#                           + side(rem) + log2(rem+1) + 2
# except rem==1 is side(pow)+3
# eg side(5) = side(4) + 3
#            = 6       + 3 = 9
# eg side(6) = side(4) + side(3) + 2*side(2) + log2(3)+2
#            = 6       + 3       + 2*1         +1   + 2 = 14
#
# centre(pow+rem) = centre(pow) + centre(rem) + 2*side(rem)
#                 = 2*side(pow/2) + 4*side(pow/4) + ...
#                   + centre(rem) + 2*side(rem)

# d = p1+p2+p3+p4
# C(d) = C(p1) + 2*S(p2+p3+p4) + C(p2+p3+p4)
#      = C(p1) + 2*S(p2+p3+p4) + C(p2) + 2*S(p3+p4) + C(p3+p4)
#      = C(p1) + C(p2) + 2*S(p2+p3+p4) + 2*S(p3+p4) + C(p3) + C(p4) + 2*S(p4)
#      = C(p1) + C(p2) + C(p3) + C(p4) + 2*S(p2+p3+p4) + 2*S(p3+p4) + 2*S(p4)
# eg. C(4+1) = C(4) + C(1) + 2*S(1)
#            =  10  +  1   + 2*0 = 11
# eg. C(4+1) = C(4) + C(2) + 2*S(2)
#            =  10  +   4  + 2*1  = 18
# eg. C(8+1) = C(8) + C(1) + 2*S(1)
#            =  32  +   1  + 2*0 = 35
# eg. C(8+2) = C(8) + C(2) + 2*S(2)
#            =  32  +   4  + 2*1 = 38
# eg. C(8+4) = C(8) + C(4) + 2*S(4)
#            =  32  +  10  + 2*6 = 54
# eg. C(8+4+1) = C(8) + C(4) + C(1) + 2*S(4+1) + 2*S(1)
#              =  32  +  10  +   1  + 2*9 + 2*0 = 61
# eg. C(8+4+2) = C(8) + C(4) + C(2) + 2*S(4+2) + 2*S(2)
#              =  32  +  10  +   4  + 2*14     + 2*1 = 76
#
# A151735
# before     1 2 3   4  5  6  7   8  9 10 11 12 13  14  15   16
# centre = 0,1,4,5, 10,11,16,21, 32,33,38,43,54,61  76  95  118
#
# before 1  2 3  4 5  6  7  8  9 10 11 12 13  14  15   16
# side = 0, 1,3, 6,9,14,21, 27,30,35,43,52,63,80,100, 112
#
# A151725 total cells 0,1,9,13, 33,37,57,77, 121,125,145,165,209,237,297,373,
#
#
# 15 |    15 15 15    15 15 15    15 15 15    15 15 15
# 14 |       14          14          14          14 15
# 13 |       14 13 13 13 14          14 13 13 13    15
# 12 |       14    12                      12 13
# 11 |             12 11 11 11    11 11 11    13    15
# 10 |       14    12    10          10 11    14 14 15
#  9 |       14 13 13    10  9  9  9    11          15
#  8 |                          8  9
#  7 |     7  7  7     7  7  7     9    11          15
#  6 |        6           6  7    10 10 11    14 14 15           19          18
#  5 |        6  5  5  5     7          11    13    15           20 15 14 13
#  4 |              4  5          13 12 12 12 13                       10 12
#  3 |     3  3  3     5     7    13          13    15         9  8  7    11
#  2 |        2  3     6  6  7    14 14    14 14 14 15            4  6    16 17
#  1 |  1  1     3           7                      15      3  2     5
#  0 |  0  1                                                0  1
#    +----------------------------------------------
#       0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
#
#       same mirror 1->9          same 1->9
#                                extra log(d) in Y=8 row
#
# 16 |                                                 16
# 15 |    15 15 15    15 15 15    15 15 15    15 15 15 16     k=4 depth=16
# 14 |       14          14          14          14    16
# 13 |       14 13 13 13 14          14 13 13 13 14
# 12 |       14    12                      12    14
# 11 |             12 11 11 11    11 11 11 12
# 10 |       14    12    10          10    12    14
#  9 |       14 13 13    10  9  9e 9d10    13 13 14
#  8 |                          8c   10          14
#  7 |     7  7  7     7  7  7  8b
#  6 |        6           6     8a   10          14      rotate -90  1->8
#  5 |        6  5  5  5  6     9  9 10    13 13 14      miss one in corner
#  4 |              4     6          10    12    14
#  3 |     3  3  3  4          12 11 11 11 12
#  2 |        2     4     6    12          12    14
#  1 |  1  1  2     5  5  6    13 13    13 13 13 14
#  0 |  0  .            ****                    ****
#    +---------------------------------------------------
#       0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16
#
# Octant
#
# 16 |
# 15 |                                              15
# 14 |                                           14 15
# 13 |                                        13    15
# 12 |                                     12 13
# 11 |                                  11    13    15
# 10 |                               10 11    14 14 15
#  9 |                             9    11          15
#  8 |                          8  9
#  7 |                       7     9    11          15
#  6 |                    6  7    10 10 11    14 14 15
#  5 |                 5     7          11    13    15
#  4 |              4  5          13 12 12 12 13
#  3 |           3     5     7    13          13    15
#  2 |        2  3     6  6  7    14 14    14 14 14 15
#  1 |     1     3           7                      15
#  0 |  0  1
#    +---------------------------------------------------
#       0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16
#
# oct(pow+rem) = oct(pow)
#                + oct(rem)    # extend
#                + oct(rem)    # upper
#                + oct(rem+1)  # lower
#                - rem         # undouble spine
#                + 2*floor(log2(rem+1))    # upper+extend log2_extras
#
# side(rem) = oct(rem) + oct(rem+1)
#             - rem                   # no double spine
#             + floor(log2(rem+1))    # upper log2_extras
#
# pow=2^k
# oct(2*pow) = 4*oct(pow) + 2*(k-2) - (pow-2)
# oct(2^0=1) = 0
# oct(2^1=2) = 1
# oct(2^2=4) = 4  = 4*1 - 0
# oct(2^3=8) = 16 = 4*4 - 0
# oct(2^4=16) = 16+7+4+7+3+4+5+4+3+3+3+2+1 = 62 = 4*16 - 2

# 3side
#
#  **** *** *** *** *** *** *** ***
#  * *   *   *   *   *   *   *   *
# ** *****   *****   *****   *****
#    * *       * *   * *       * *
# **   **** ****       **** ****
#  * * * *   * * *   * * *   * * *
# ** *** ***** ***   *** ***** ***
#    *   * *               * *   *      side                        side
# **       *888 888 888 888*                                        depth+1
#  * *   * * 7   7   7   7 * *   *            upper    |  upper
#    *** *** 76667   76667 *** ***            depth-1  |  depth-1
#  * * * *   7 5       5 7   * * *              \      |
# **   *****   5444 4445   *****                 \     |       /
#  * * *   * 7 5 3   3 5 7 *   * *         lower  \    |      /   lower
# ** **** ** 766 32223 667 ** ****         depth   \   |     /    depth
#                  1 3   7       *        ---------------------------
#                 01                                   |     \    upper
#                  1 3   7       *                     |      \   depth
#                  223 667 ** ****                     |       \
#                    3 5 7 *   * *                     | lower  \
#                  54445   *****                       | depth+1     side
#                  5   5 7   * * *
#                  66 6667 *** ***
#                        7 * *   *
#                  dcc 9888*
#                  d b 9   * *   *
#                    baaa **** ***
#                  e b       * * *
#                  dcccd   *****
#                  d   d   *   * *
#                  ee eee *** ****
#                                *

my @oct_to_n = (0, 1);

my %tree_depth_to_n = (4       => [ 0, 1 ],
                       1       => [ 0, 1 ],
                       octant  => [ 0, 1 ],
                       '3mid'  => [ 0, 1 ],
                       '3side' => [ 0, 1, 4 ],
                       side    => [ 0, 1 ]);
my %tree_depth_to_n_extra_depth_pow = (4       => 0,
                                       1       => 0,
                                       octant  => 0,
                                       '3mid'  => 1,
                                       '3side' => 1,
                                       side    => 1);

# use Smart::Comments;

sub tree_depth_to_n {
  my ($self, $depth) = @_;
  ### tree_depth_to_n(): "$depth  parts=$self->{'parts'}"

  $depth = int($depth);
  if ($depth < 0) {
    return undef;
  }

  my $parts = $self->{'parts'};
  {
    my $initial = $tree_depth_to_n{$parts};
    if ($depth <= $#$initial) {
      ### table %tree_depth_to_n{}: $initial->[$depth]
      return $initial->[$depth];
    }
  }

  my ($pow,$exp) = round_down_pow
    ($depth + $tree_depth_to_n_extra_depth_pow{$parts},
     2);
  if (is_infinite($exp)) {
    return $exp;
  }
  ### $pow
  ### $exp

  my $zero = $depth * 0;  # inherit bignum
  my $n = $zero;

  # @side is a list of depth values.
  # @mult is the multiple of T[depth] desired for that @side entry.
  #
  # @side is mostly high to low and growing by one more value at each
  # $exp level, but sometimes it's a bit more and some values not high to
  # low and possibly duplicated.
  #
  my @pending;
  my @mult;

  if ($parts eq '4') {
    push @pending, $depth;
    push @mult, 8;
    $n -= 4*$depth + 7;

  } elsif ($parts eq '1') {
    push @pending, $depth;
    push @mult, 2;
    $n -= $depth;

  } elsif ($parts eq 'octant') {
    push @pending, $depth;
    push @mult, 1;

  } elsif ($parts eq '3mid') {
    push @pending, $depth+1, $depth;
    push @mult, 2, 4;
    # Duplicated diagonals, and no log2_extras on two outermost octants.
    # Each log2 at depth=2^k-2, so another log2 decrease when depth=2^k-1.
    # $exp == _log2_floor($depth+1) so at $depth==2*$pow-1 one less.
    $n -= 3*$depth + 2*$exp + 6;

  } elsif ($parts eq '3side') {
    push @pending, $depth+1, $depth, $depth-1;
    push @mult, 1, 3, 2;
    # Duplicated diagonals, and no log2_extras on two outermost octants.
    # For plain depth each log2 at depth=2^k-2, so another log2 decrease
    # when depth=2^k-1.
    # For depth+1 block each log2 at depth=2^k-2, so another log2 decrease
    # when depth=2^k-2.
    # $exp == _log2_floor($depth+1) so at $depth==2*$pow-1 one less.
    $n -= 3*$depth + 2*$exp + ($depth == $pow-1 ? 3 : 4);

  } elsif ($parts eq 'side') {
    push @pending, $depth+1, $depth;
    push @mult, 1, 1;
    # $exp == _log2_floor($depth+1)
    $n -= $depth + 1 + $exp;
  }

  while ($exp >= 0 && @pending) {
    ### at: "pow=$pow exp=$exp  n=$n"
    ### assert: $pow == 2 ** $exp
    ### pending: join(',',@pending)
    ### mult: join(',',@mult)

    my @new_pending;
    my @new_mult;
    my $oct_pow;
    foreach my $depth (@pending) {
      my $mult = shift @mult;
      ### assert: $depth >= 0

      if ($depth <= 1) {
        ### small depth: "depth=$depth mult=$mult * $oct_to_n[$depth]"
        $n += $mult * $depth;  # oct=0 at depth=0, oct=1 at depth=1
        next;
      }
      my $rem = $depth - $pow;
      if ($rem < 0) {
        push @new_pending, $depth;
        push @new_mult, $mult;
        next;
      }

      ### $depth
      ### $mult
      ### $rem
      ### assert: $rem >= 0 && $rem < $pow

      my $powmult = $mult;
      if ($rem <= 1) {
        if ($rem == 0) {
          ### rem=0, oct(pow) only ...
        } else { # $rem == 1
          ### rem=1, oct(pow)+1 ...
          $n += $mult;
        }
      } else {
        ### formula ...
        # oct(pow+rem) = oct(pow)
        #                + oct(rem+1)
        #                + 2*oct(rem)
        #                - floor(log2(rem+1))
        #                - rem - 3

        my $rem1 = $rem + 1;
        {
          my ($lpow,$lexp) = round_down_pow ($rem1, 2);
          $n -= ($lexp + $rem + 3)*$mult;
          ### sub also: ($lexp + $rem + 3). " *mult=$mult"
        }
        if ($rem1 == $pow) {
          ### rem+1 == pow, increase powmult ...
          $powmult *= 2;    # oct(pow)+oct(rem+1) is 2*oct(pow)
        } elsif (@new_pending && $new_pending[-1] == $rem1) {
          ### merge into previously pushed new_pending[] ...
          # print "rem+1=$rem1 ",join(',',@new_pending),"\n";
          $new_mult[-1] += $mult;
        } else {
          ### push: "depth=$rem1 mult=$mult"
          push @new_pending, $rem1;
          push @new_mult, $mult;
        }

        ### push: "depth=$rem mult=".2*$mult
        push @new_pending, $rem;
        push @new_mult, 2*$mult;
      }

      # oct(pow) = (2*pow*pow + 3*exp + 7)/9 + pow/2
      #          = ((4*pow+9)*pow + 6*exp + 14)/18
      #
      $oct_pow ||= ((4*$pow+9)*$pow + 6*$exp + 14)/18;
      $n += $oct_pow * $powmult;
      ### oct(pow): "pow=$pow is $oct_pow * powmult=$powmult"
    }
    @pending = @new_pending;
    @mult = @new_mult;

    $exp--;
    $pow /= 2;
  }

  ### return: $n
  return $n;
}

#use Smart::Comments;

# _depth_to_octant_added() returns the number of cells added at a given
# $depth level in parts=octant.  This is the same as
#     $added = tree_depth_to_n(depth+1) - tree_depth_to_n(depth)
#
# @$depth_aref is a list of depth values.
# @$mult_aref is the multiple of oct(depth) desired for each @depth_aref.
#
# On input @$depth_aref must have $depth_aref->[0] as the highest value.
#
# Within the code the depth list is mostly high to low and growing by one
# extra depth value at each $exp level.  But sometimes it grows a bit more
# than that and sometimes the values are not high to low, and sometimes
# there's duplication.
#
my @_depth_to_octant_added = (1, 2, 1);  # depth=0to2 small values

sub _depth_to_octant_added {
  my ($depth_aref, $mult_aref, $zero) = @_;
  ### _depth_to_octant_added(): join(',',@$depth_aref)
  ### mult_aref: join(',',@$mult_aref)
  ### assert: scalar(@$depth_aref) == scalar(@$mult_aref)

  # $depth_aref->[0] must be the biggest depth, to make the $pow finding easy
  ### assert: scalar(@$depth_aref) >= 1
  ### assert: max(@$depth_aref) == $depth_aref->[0]

  my ($pow,$exp) = round_down_pow ($depth_aref->[0], 2);
  if (is_infinite($exp)) {
    return $exp;
  }
  ### $pow
  ### $exp

  my $added = $zero;

  # running $pow down to 2 (inclusive)
  while ($exp >= 0 && @$depth_aref) {
    ### at: "pow=$pow exp=$exp"
    ### assert: $pow == 2 ** $exp

    ### depth: join(',',@$depth_aref)
    ### mult: join(',',@$mult_aref)
    my @new_depth;
    my @new_mult;
    foreach my $depth (@$depth_aref) {
      my $mult = shift @$mult_aref;
      ### assert: $depth >= 0

      if ($depth <= $#_depth_to_octant_added) {
        ### small depth: "depth=$depth mult=$mult * $_depth_to_octant_added[$depth]"
        $added += $mult * $_depth_to_octant_added[$depth];
        next;
      }
      if ($depth < $pow) {
        push @new_depth, $depth;
        push @new_mult, $mult;
        next;
      }

      my $rem = $depth - $pow;

      ### $depth
      ### $mult
      ### $rem
      ### assert: $rem >= 0 && $rem < $pow

      if ($rem <= 1) {
        if ($rem == 0) {
          ### rem=0, grow 1 ...
          $added += $mult;
        } else {
          ### rem=1, grow 3 ...
          $added += 3 * $mult;
        }
      } else {
        my $rem1 = $rem + 1;
        if ($rem1 == $pow) {
          ### rem+1=pow, no lower part, 3/2 of pow ...
          $added += ($pow/2) * (3*$mult);
        } else {
          ### formula ...
          # oadd(pow+rem) = oadd(rem+1) + 2*oadd(rem)
          #                 + (is_pow2($rem+2) ? -2 : -1)

          # upper/lower diagonal overlap, and no log2_extras in lower
          $added -= (_is_pow2($rem+2) ? 2*$mult : $mult);

          if (@new_depth && $new_depth[-1] == $rem1) {
            ### merge into previously pushed new_depth ...
            # print "rem=$rem ",join(',',@new_depth),"\n";
            $new_mult[-1] += $mult;
          } else {
            ### push: "rem+1  depth=$rem1 mult=$mult"
            push @new_depth, $rem1;
            push @new_mult, $mult;
          }

          ### push: "rem    depth=$rem mult=".2*$mult
          push @new_depth, $rem;
          push @new_mult, 2*$mult;
        }
      }
    }
    $depth_aref = \@new_depth;
    $mult_aref = \@new_mult;

    $exp--;
    $pow /= 2;
  }

  ### return: $added
  return $added;
}

# no Smart::Comments;

sub _is_pow2 {
  my ($n) = @_;
  my ($pow,$exp) = round_down_pow ($n, 2);
  return ($n == $pow);
}
sub _log2_floor {
  my ($n) = @_;
  if ($n < 2) { return 0; }
  my ($pow,$exp) = round_down_pow ($n, 2);
  return $exp;
}

1;
__END__

=for stopwords eg Ryde Math-PlanePath-Toothpick Nstart Nend Applegate Automata Congressus Numerantium ie Octant octant octants Ie OEIS

=head1 NAME

Math::PlanePath::OneOfEight -- automaton growing to cells with one of eight neighbours

=head1 SYNOPSIS

 use Math::PlanePath::OneOfEight;
 my $path = Math::PlanePath::OneOfEight->new;
 my ($x, $y) = $path->n_to_xy (123);

=head1 DESCRIPTION

X<Applegate, David>X<Pol, Omar E.>X<Sloane, Neil>This a cellular automaton
growing into cells which have just 1 of 8 neighbours already "on" as per
part 14 "Square Grid with Eight Neighbours" of

=over

David Applegate, Omar E. Pol, N.J.A. Sloane, "The Toothpick Sequence and
Other Sequences from Cellular Automata", Congressus Numerantium, volume 206
(2010), pages 157-191.

http://www.research.att.com/~njas/doc/tooth.pdf

=back

Points are numbered by growth levels and anti-clockwise on each cell within
the level.

=cut

# math-image --path=OneOfEight --output=numbers --all --size=75x16

=pod

                                                                121    8
     93  92  91      90  89  88      87  86  85      84  83  82        7
     94  64              63              60              59  81        6
     95      44  43  42  62              61  41  40  39      80        5
             45  34                              33  38                4
     96      46      20  19  18      17  16  15      37      79        3
     97  65  66      21  10               9  14      57  58  78        2
     98              22       4   3   2      13              77        1
                              5   0   1                           <- Y=0
     99              23       6   7   8      32             120       -1
    100  68  67      24  11              12  31      76  75 119       -2
    101      47      25  26  27      28  29  30      56     118       -3
             48  35                              36  55               -4
    102      49  50  51  71              72  52  53  54     117       -5
    103  69              70              73              74 116       -6
    104 105 106     107 108 109     110 111 112     113 114 115       -7

                                  ^
     -7  -6  -5  -4  -3  -2  -1  X=0  1   2   3   4   5   6   7


The start is N=0 at the origin X=0,Y=0.  Then each cell around it has just
one neighbour (that first N=0 cell) and so all are turned on.  The rule is
applied in a single atomic step, so any adjacent prospective new cells don't
count.

At the next level only the diagonal cells X=+/-2,Y=+/-2 have a single
neighbour, and so on.

                                     10           9
                                       \         /
                     4  3  2             4  3  2
                      \ | /               \ | /
         0           5--0--1             5--0--1
                      / | \               / | \
                     6  7  8             6  7  8
                                       /         \
                                     11           12

The children of a given node are numbered anti-clockwise relative to the
position of the parent of that node.  So for example N=9 has it's parent
south-west and points are numbered anti-clockwise around from there to give
N=13 through N=17.

=head2 Level Ranges

The pattern always extends along the X=+/-Y diagonals, and extends to the
sides in power-of-2 blocks.  So for example in the part shown above N=33 at
X=4,Y=4 is the only cell growing out of the 4x4 block X=0to3,Y=0to3 at the
origin, and likewise N=34,35,36 in the other quadrants.  Then N=121 at
X=8,Y=8 is the only growth out of the 8x8 block, etc.

In general the first N at depth=2^k for kE<gt>=0 is

    Ndepth(2^k) = (16*4^k + 24*k - 7) / 9
                = (16*depth*depth + 24*k - 7) / 9

    eg. k=3 Ndepth=121

Because points are numbered from N=0 this is how many cells are "on" in the
pattern before this depth level.  The cells are within -2^k E<lt> X,Y E<lt>
2^k and so the fraction of the plane covered is

    density = Ndepth(2^k) / (2*2^k - 1)^2
            = (16*4^k + 24*k - 7) / 9 / (2*2^k-1)^2
            -> 4/9 = 0.444...    as k -> infinity

This density is approached from above, ie. density(k) decreases towards 4/9
as k increases.  The first k=0 is the single origin point which is 1/1, and
k=2 is 9/9 of the 3x3 at the origin.  Then for example k=2 7x7 square has
33/49=0.673, then k=3 121/225=0.5377, etc.

=head2 One Quadrant

Option C<parts =E<gt> 1> confines the pattern to the first quadrant.  This
is a single copy of the part repeated in each of the four quadrants of the
full pattern.

=cut

# math-image --path=OneOfEight,parts=1 --all --output=numbers --size=75x16

=pod

    parts => 1

     15 |    117 116 115     114 113 112     111 110 109     108 107 106
     14 |         90              89              86              85 105
     13 |         91  73  72  71  88              87  70  69  68     104
     12 |         92      58                              57  67
     11 |                 59  53  52  51      50  49  48      66     103
     10 |         93      60      41              40  47      83  84 102
      9 |         94  74  75      42  37  36  35      46             101
      8 |                                 32  34
      7 |     31  30  29      28  27  26      33      45             100
      6 |         19              18  25      38  39  44      82  81  99
      5 |         20  15  14  13      24              43      65      98
      4 |                 10  12              61  54  55  56  64
      3 |      9   8   7      11      23      62              63      97
      2 |          4   6      16  17  22      76  77      78  79  80  96
      1 |  3   2       5              21                              95
    Y=0 |  0   1
        +----------------------------------------------------------------
         X=0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15

=head2 One Octant

Option C<parts =E<gt> 'octant'> confines the pattern to the first octant
0E<lt>=YE<lt>=X.

=cut

# math-image --path=OneOfEight,parts=octant --all --output=numbers --size=75x16

=pod

    parts => "octant"

     15 |                                              66
     14 |                                           54 65
     13 |                                        44    64
     12 |                                     36 43
     11 |                                  32    42    63
     10 |                               26 31    52 53 62
      9 |                            23    30          61
      8 |                         20 22
      7 |                      19    21    29          60
      6 |                   13 18    24 25 28    51 50 59
      5 |                10    17          27    41    58
      4 |              7  9          37 33 34 35 40
      3 |           6     8    16    38          39    57
      2 |        3  5    11 12 15    45 46    47 48 49 56
      1 |     2     4          14                      55
    Y=0 |  0  1
        +-------------------------------------------------
         X=0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15

The full pattern is symmetric on each size of the four diagonals X=Y, X=-Y.
This octant is one of those eight symmetric parts.  It includes the diagonal
which is shared if two octants are combined to make a quadrant.

The octant is self similar in blocks

                              --|
                            --  |
                          --    |
                        --      |
                      -- extend |
                    --          |
                  --------------|
                --| --   upper  |
              --  |   --  flip  |
            --    |     --      |
          --      | lower --    |
        --  base  | depth+1 --  |
      --          | no pow2s  --|
    -----------------------------

"extend" is a direct copy of the "base" block and the "upper" likewise
except flipped vertically.

The "lower" block is the base pattern rotated by +90 degrees, and without
the pow2 cells at Y=1 X=3,7,15,etc.  These absent cells are easier to see in
a bigger picture of the pattern.

The "lower" block is one depth level ahead.  It ends at N=45,46,47,48
depth=14 whereas the corresponding N=62,63,64,65 in the "extend" is at
depth=15.

The diagonal between the lower and upper looks like a stair-step, but that's
not so.  It's the same as the X=Y leading diagonal of the whole octant but
because the lower block is one depth level ahead of the upper the branches
off the diagonal are offset by 1 position.  For example N=34,33,37 branching
off to the lower corresponds to N=40,41,51 branching into the upper.

This offset on the upper/lower diagonal becomes easier to see by chopping
off one level of leaf nodes.

=cut

# math-image --text --path=OneOfEight,parts=octant --values=PlanePathCoord,planepath=\"OneOfEight,parts=octant\",coordinate_type=IsNonLeaf --size=50x40

=pod

                  * 
                 *  
                *   
               *       octant with leaf nodes pruned
              * *   
             *   *  
            *       
           *        
          * *       
         *   *   *  
        *     * *      <- upper,lower parts
       *     * *          branch off lower is 1 level sooner
      * *   *   *   
     *   *       *  
    *               
   *                

It may look at first as if the square side block comprising "upper" and
"lower" is entirely different from the central symmetric square (of L</One
Quadrant> above), but that's not so, it's just the offset of the branching
from the diagonal.

=head2 Three Mid

Option C<parts =E<gt> "3mid"> is the "second corner sequence" of the
toothpick paper above.  This is the part of the full pattern starting at a
point X=2^k,Y=2^k in that pattern, with the three parts there extended
indefinitely.

=cut

# math-image --path=OneOfEight,parts=3mid --all --output=numbers --size=75x15

=pod

    parts => "3mid"

    85 84 83    82 81 80    79 78 77    76 75 74         7
       58          57          54          53 73         6
       59 41 40 39 56          55 38 37 36    72         5
       60    26                      25 35               4
             27 21 20 19    18 17 16    34    71         3
       61    28     9           8 15    51 52 70         2
       62 42 43    10  5  4  3    14          69         1
                          0  2                      <- Y=0
                             1    13          68        -1
                             6  7 12    50 49 67        -2
                                  11    33    66        -3
                            29 22 23 24 32              -4
                            30          31    65        -5
                            44 45    46 47 48 64        -6
                                              63        -7

                          ^
    -7 -5 -6 -4 -3 -2 -1 X=0 1  2  3  4  5  6  7

The first quadrant XE<gt>=0,YE<gt>=0 is the same as in the full pattern, but
the other quadrants are a "side" portion of the pattern.

This pattern can be generated from the automaton rule by starting from N=0
at X=0,Y=0 and then treating all points with XE<lt>0,YE<lt>0 as if already
occupied.

                       ..       .. .. ..
                        9           8 ..
                       10  5  4  3    ..
                              0  2
              -------------+     1
    X<0,Y<0       *  *  *  |     6  7 ..
    considered    *  *  *  |
    all "on"      *  *  *  |

=head2 Three Side

Option C<parts =E<gt> "3side"> is the "first corner sequence" of the
toothpick paper above.  This is the part of the full pattern starting at a
point X=2^k+1,Y=3*2^k-1 and mirrored horizontally, and the three parts there
extended indefinitely.

=cut

# math-image --path=OneOfEight,parts=3side --expression='i<=99?i:0' --output=numbers --size=120x25

=pod

    parts => "3side"

    .. 89 88 87    86 85 84    83 82 81    80 79 78 ..           8
          70          69          66          65                 7
          71 51 50 49 68          67 48 47 46 64                 6
          72    34                      33    63                 5
                35 25 24 23    22 21 20 32                       4
          73    36    15          14    31    62                 3
          74 52 53    16  8  7  6 13    44 45 61                 2
                             3    12          60                 1
                          0  2                              <- Y=0
                             1    11          59                -1
                             4  5 10    43 42 58                -2
                                   9    30    57                -3
                            26 17 18 19 29                      -4
                            27          28    56                -5
                            37 38    39 40 41 55                -6
                                              54                -7
                                        .. 75 76 77 ..          -8
                          ^
       -7 -6 -4 -3 -2 -1 X=0 1  2  3  4  5  6  7  8

The Y negative quadrant is rotated +90 degrees and is one depth level ahead
of the other two, so for example its run N=54,55,56 corresponds to
N=78,79,80 in the first quadrant.

This pattern can be generated from the automaton rule by starting from N=0
at X=0,Y=0 and then treating all points with XE<lt>0,YE<lt>=0 as already
occupied.  Notice 3mid above is YE<lt>0 whereas here YE<lt>=0.

                           .. 8  7  6 ..
                                 3
              -------------+  0  2
    X<0,Y<=0      *  *  *  |     1    11
    considered    *  *  *  |     4  5 10
    all "on"      *  *  *  |           9
                  *  *  *  |          ..

The 3side pattern is the same as the 3mid but with the portion above the X=Y
diagonal shifted up diagonally to X+1,Y+1 and therefore branching off the
diagonal 1 depth level later.  On that basis the two C<tree_depth_to_n()>
total cells are related by

   N3side(depth) = (N3mid(depth) + N3mid(depth-1) + 1) / 2

For example depth=4 begins at N=17 in 3side,

   N3side(4) = 17
   N3mid(4) = 22, N3mid(3) = 11 
   (22 + 11 + 1)/2 = 17

=head2 Three Growth

The interest in the 3mid and 3side "corner" sequences is that a base 3mid
can be doubled in size by adding one "3mid" and two "3side"s.

    +-------------+-------------+
    |             |             |       3mid doubled in size
    | new 3side   |  new 3mid   |       by adding two new 3sides
    |             |             |       and one new 3mid.
    |      +-------------+      |
    |      |             |      |
    |      |  start 3mid |      |
    |      |             |      |
    +------+------+      |------+
                  |      |      |
                  |      |      |
                  |      |      |
                  +------+      |
                  |             |
                  |   new 3side |
                  |             |
                  +-------------+

=head1 FUNCTIONS

See L<Math::PlanePath/FUNCTIONS> for behaviour common to all path classes.

=over 4

=item C<$path = Math::PlanePath::OneOfEight-E<gt>new ()>

=item C<$path = Math::PlanePath::OneOfEight-E<gt>new (parts =E<gt> $str)>

Create and return a new path object.  The C<parts> option (a string) can be

    "4"           full pattern (the default)
    "1"           single quadrant
    "octant"      single octant wedge
    "3mid"        three quadrants, middle symmetric style
    "3side"       three quadrants, side style

=back

=head2 Tree Methods

=over

=item C<@n_children = $path-E<gt>tree_n_children($n)>

Return the children of C<$n>, or an empty list if C<$n> has no children
(including when C<$n E<lt> 1>, ie. before the start of the path).  The way
points are numbered means the children are always consecutive N values.

The children are the new cells turned "on" adjacent to C<$n> at the next
depth level.  The possible number of children varies with the parts,

    parts        possible num children
    -----        ---------------------
      4          0, 1, 2, 3, 5, 8
      1          0, 1, 2, 3, 5
    octant       0, 1, 2, 3
    3mid         0, 1, 2, 3, 5
    3side        0,    2, 3

For parts=4 there's 8 children at the initial N=0 and after that at most 5.

1 child only occurs at X=2^k,Y=2^k of the central diagonal In parts=3side
there's no such corner and never a single child, only 2 or 3 children.

5 children occurs around the 1-child of X=2^k,Y=2^k in parts=4,1,3mid.  In
an octant there's only 3 children around that point since that pattern
doesn't go above the X=Y diagonal.

=back

=head1 FORMULAS

=head2 Depth to N

The first point is N=0 so C<tree_depth_to_n($depth)> is the total number of
points up to and not including C<$depth>.  For the full pattern this
total(depth) follows a recurrence

    total(0)         = 0
    total(pow)       = (16*pow^2 + 24*exp - 7) / 9
    total(pow + rem) = total(pow) + 2*total(rem) + total(rem+1)
                         - 8*floor(log2(rem+1)) + 1
    where depth = pow + rem
      with pow=2^k the biggest power-of-2 <= depth
      and rem the remainder

For parts=octant the equivalent total points is

    oct(0)         = 0
    oct(pow)       = (4*pow^2 + 9*pow + 6*exp + 14) / 18
    oct(pow + rem) = oct(pow) + 2*oct(rem) + oct(rem+1)
                       - floor(log2(rem+1)) - rem - 3

The way this recurrence formula works can be seen from the self-similar
pattern described in L</One Octant> above.

    oct(pow)                # "base"
    + oct(rem)              # "extend"
    + oct(rem)              # "upper"
    + oct(rem+1)            # "lower"
    - floor(log2(rem+1))    # no pow2 points in lower
    - rem                   # unduplicate diagonal upper/lower
    - 3                     # unduplicate centre points

The oct(rem)+oct(rem+1) of upper+lower parts would count their common
diagonal twice, hence "-rem" being the length of that diagonal.  The
"centre" point at X=pow,Y=pow is repeated by each of extend, upper, lower so
"-2" to count just once, and the X=pow+1,Y=pow point is repeated by extend
and upper, so "-1" to count it just once.

The 2*total(rem)+total(rem+1) in the formula is the same recurrence as the
toothpick pattern and the approach there can calculate it as a little set of
pending depths and pow subtractions.  See
L<Math::PlanePath::ToothpickTree/Depth to N>.

The other patterns can be expressed as combinations of octants,

    parts=4  total = 8*oct(n) - 4*n - 7
    parts=1  total = 2*oct(n) - n
    3mid  V2 total = 2*oct(n+1) + 4*oct(n)
                       - 3n - 2*floor(log(n+1)) - 6
    3side V1 total =   oct(n+1) + 3*oct(n) + 2*oct(n-1)
                       - 3n - floor(log(n+1)) - floor(log(n)) - 4

The set of depth offsets n,n+1,etc become initial depth values in the list
for the recurrence reduction algorithm, with respective initial multipliers.

From the V1,V2 formulas it can be seen that V2(n)+V2(n+1) gives the same
combination of 1,3,2 octants as V1, and that therefore as noted in the
Ndepth part of L</Three Side> above

    V1(n) = (V2(n) + V2(n-1) + 1) / 2

=head1 OEIS

This cellular automaton is in Sloane's Online Encyclopedia of Integer
Sequences as

    http://oeis.org/A151725    (etc)

    parts=4 (the default)
      A151725   total cells "V" (tree_depth_to_n())
      A151726   added cells "v"

    parts=1
      A151735   total cells (tree_depth_to_n())
      A151737   added cells

    parts=3mid
      A170880   total cells (tree_depth_to_n())
      A151728   added cells "v2"

    parts=3side
      A170879   total cells (tree_depth_to_n())
      A151747   added cells "v1"

=head1 SEE ALSO

L<Math::PlanePath>,
L<Math::PlanePath::ToothpickTree>,
L<Math::PlanePath::UlamWarburton>

=head1 HOME PAGE

http://user42.tuxfamily.org/math-planepath/index.html

=head1 LICENSE

Copyright 2012, 2013 Kevin Ryde

This file is part of Math-PlanePath-Toothpick.

Math-PlanePath-Toothpick 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-Toothpick 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-Toothpick.  If not, see <http://www.gnu.org/licenses/>.

=cut
