#!/usr/bin/perl -w

# Copyright 2011, 2012, 2013, 2014, 2015, 2017, 2019 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/>.

use 5.010;
use strict;
use warnings;
use FindBin;
use Math::Libm 'M_PI', 'hypot';
use Math::BaseCnv 'cnv';
use Math::PlanePath;;
*_divrem_mutate = \&Math::PlanePath::_divrem_mutate;
use Math::PlanePath::Base::Digits
  'digit_split_lowtohigh',
  'digit_join_lowtohigh';
$|=1;

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

{
  require Math::NumSeq::PlanePathDelta;
  require Math::PlanePath::FlowsnakeCentres;
  my $class = 'Math::PlanePath::FlowsnakeCentres';
  my $path = $class->new;
  my $seq = Math::NumSeq::PlanePathDelta->new (planepath_object=>$path,
                                               delta_type => 'TDir6');
  # Centres N to turn by path
  my $path_n_to_tturn6 = sub {
    my ($n) = @_;
    if ($n < 1) { return undef; }
    my $turn6 = ($seq->ith($n) - $seq->ith($n-1)) % 6;
    if ($turn6 > 3) { $turn6 -= 6; }
    return $turn6;
  };

  # Centres N to Turn by digits
  my $calc_n_to_tturn6;
  $calc_n_to_tturn6 = sub {           # not working
    my ($n) = @_;
    if ($n < 1) { return undef; }
    my $z = 0;
    while ($n % 7 == 0) {
      $n /= 7;
      $n == int($n) or die;
      $z++;
    }
    my $t = $n % 7;
    $n = ($n-$t)/7;

    while ($n % 7 == 3) {
      $n = ($n-3)/7;
      $n == int($n) or die;
    }
    my $r = $n % 7;

    if ($r == 1 || $r == 2 || $r == 6) {
      if ($t == 1) { return  1; }
      if ($t == 2) { return  1; }
      if ($t == 3) { return  2; }
      if ($t == 4) { return -1; }
      if ($t == 5) { return -2; }
      if ($t == 6) { return  ($r == 1 ? 1 : 0); }
    } else {
      if ($t == 1) { return ($z ? 0 : 2); }
      if ($t == 2) { return ($z ? 0 : 1); }
      if ($t == 3) { return ($z ? -1 : -2); }
      if ($t == 4) { return -1; }
      if ($t == 5) { return -1; }
      if ($t == 6) { return ($z ? 0 : 1); }
    }
    die "oops t=$t";
  };
  {
    for (my $n = 1; $n < 7**3; $n+=1) {
      my $n7 = cnv($n,10,7);
      my $by_path = $path_n_to_tturn6->($n);
      my $calc = $calc_n_to_tturn6->($n);
      my $diff = ($by_path != $calc ? '   ***' : '');
      print "$n [$n7]  $by_path $calc$diff\n";
    }
    exit 0;
  }
  exit 0;
}
{
  # islands convex hull

  require Math::PlanePath::GosperIslands;
  require Math::Geometry::Planar;
  my $path = Math::PlanePath::GosperIslands->new;
  my @values;
  foreach my $k (0 .. 9) {
    my $n_lo = 3**($k+1) - 2;
    my $n_hi = 3**($k+2) - 2 - 1;
    ### $n_lo
    ### $n_hi
    my $points = [ map{[$path->n_to_xy($_)]} $n_lo .. $n_hi ];

    my $planar = Math::Geometry::Planar->new;
    $planar->points($points);
    if (@$points > 4) {
      $planar = $planar->convexhull2;
      $points = $planar->points;
    }
    my $area = $planar->area / 6;

    my $whole_area = 7**$k;
    my $f = $area / $whole_area;
    my $num_points = scalar(@$points);
    print "k=$k hull points $num_points area $area cf $whole_area ratio $f\n";
    push @values,$area;

  }
  require Math::OEIS::Grep;
  Math::OEIS::Grep->search(array => \@values, verbose=>1);
  exit 0;
}

{
  # ascii by path
  my $k = 3;
  require Math::PlanePath::Flowsnake;
  my $path = Math::PlanePath::Flowsnake->new;
  my ($n_lo, $n_hi) = $path->level_to_n_range($k);
  foreach my $y (reverse -2 .. 25) {
    my $x = -20;
    if ($y % 2) { print " "; $x++; }
  X: for ( ; $x <= 28; $x+=2) {
      ### at: "$x, $y"
      {
        my $n = $path->xyxy_to_n_either($x,$y, $x+2,$y);
        if (defined $n && $n < $n_hi) {
          print "__";
          ### horiz ...
          next X;
        }
      }
      {
        my $n = $path->xyxy_to_n_either($x,$y, $x+1,$y+1);
        if (defined $n && $n < $n_hi) {
          print "/ ";
          next X;
        }
      }
      {
        my $n = $path->xyxy_to_n_either($x+2,$y, $x+1,$y+1);
        if (defined $n && $n < $n_hi) {
          print " \\";
          next X;
        }
      }
      ### none ...
      print "..";
    }
    print "\n";
  }
  exit 0;
}

{
  require Math::BaseCnv;
  push @INC, "$FindBin::Bin/../../dragon/tools";
  require MyFSM;

  my @digit_to_rot  = (0, 0, 1, 0, 0, 1, 2);
  my @digit_permute = (0, 2, 4, 6, 1, 3, 5);
  my %table;
  foreach my $digit (0 .. 6) {
    foreach my $rot (0 .. 2) {
      my $p = $digit;
      foreach (1 .. $rot) {
        $p = $digit_permute[$p];
      }
      my $new_rot = ($rot + $digit_to_rot[$p]) % 3;
      $table{$rot}->{$digit} = $new_rot;
      print "$new_rot, ";
    }
    print "\n";
  }
  my $fsm = MyFSM->new(table => \%table,
                       initial => 0,
                        accepting => { 0=>0, 1=>1, 2=>2 },
                      );
  {
    my $width = 2;
    foreach my $n (0 .. 7**$width-1) {
      my $n7 = sprintf '%0*s', $width, Math::BaseCnv::cnv($n,10,7);
      my @v = split //,$n7;
      # print $fsm->traverse(\@v);
      print @v,"   ",$fsm->traverse(\@v),"\n";
    }
    print "\n";
    # exit 0;
  }

  my $hf = $fsm;
  print "traverse ", $fsm->traverse([0,2]), "\n";
  $fsm->view;
  $fsm = $fsm->reverse;
  $fsm->simplify;
    $fsm->view;

  print "reverse\n";
  foreach my $digit (0 .. 6) {
    foreach my $state ($fsm->sorted_states) {
      my $new_state = $fsm->{'table'}->{$state}->{$digit};
      my $ns = $new_state;
      if ($ns eq 'identity') { $ns = 0; } $ns =~ s/,.*//;
      print $ns,", ";
    }
    print "\n";
  }
  print "traverse ", $fsm->traverse([2,0]), "\n";

  my $width = 2;
  foreach my $n (0 .. 7**$width-1) {
    my $n7 = sprintf '%0*s', $width, Math::BaseCnv::cnv($n,10,7);
    my @v = split //,$n7;
    my $h = $hf->traverse(\@v);
    my $l = $fsm->traverse([ reverse @v ]);
    if ($l eq 'identity') { $l = 0; } $l =~ s/,.*//;
    if ($h ne $l) {
      print join(', ',@v),"  h=$h l=$l\n";
    }
  }
  exit 0;
}

{
  # rect_to_n_range
  require Math::PlanePath::Flowsnake;
  my $path = Math::PlanePath::Flowsnake->new;
  my ($n_lo,$n_hi) = $path->rect_to_n_range(0,0, 31.5,31.5);
  ### $n_lo
  ### $n_hi

  foreach my $n (973 .. 1000000) {
    my ($x,$y) = $path->n_to_xy($n);
    if ($x >= 0 && $x <= 31.5
        && $y >= 0 && $y <= 31.5) {
      print "$n  $x,$y\n";
    }
  }
  exit 0;
}
{
  require Math::NumSeq::PlanePathDelta;
  require Math::PlanePath::Flowsnake;
  my $class = 'Math::PlanePath::Flowsnake';
  my $path = $class->new;
  my $seq = Math::NumSeq::PlanePathDelta->new (planepath_object=>$path,
                                               delta_type => 'TDir6');

  my $path_n_to_tturn6 = sub {
    my ($n) = @_;
    if ($n < 1) { return undef; }
    my $turn6 = $seq->ith($n) - $seq->ith($n-1);
    if ($turn6 > 3) { $turn6 -= 6; }
    return $turn6;
  };

  # N to Turn by recurrence
  my $calc_n_to_tturn6 = sub {           # not working
    my ($n) = @_;
    if ($n < 1) { return undef; }
    if ($n % 49 == 0) {
      return calc_n_to_tturn6($n/7);
    }
    if (int($n/7) % 7 == 3) {  # "_3_"
      return calc_n_to_tturn6(($n%7) + int($n/49));
    }
    return path_n_to_tturn6($n);

    if ($n == 1) { return  1; }
    if ($n == 2) { return  2; }
    if ($n == 3) { return -1; }
    if ($n == 4) { return -2; }
    if ($n == 5) { return  0; }
    if ($n == 6) { return -1; }
    my @digits = digit_split_lowtohigh($n,7);
    my $high = pop @digits;
    if ($digits[-1]) {
    }
    $n = digit_join_lowtohigh(\@digits,7,0);
    if ($n == 0) {
      return 0;
    }
    return calc_n_to_tturn6($n);
  };
  {
    for (my $n = 1; $n < 7**3; $n+=1) {
      my $value = $path_n_to_tturn6->($n);
      my $calc = $calc_n_to_tturn6->($n);
      my $diff = ($value != $calc ? '   ***' : '');
      print "$n  $value $calc$diff\n";
    }
    exit 0;
  }
  exit 0;
}
{
  # N to Dir6 -- working for integers

  require Math::PlanePath::Flowsnake;
  require Math::NumSeq::PlanePathDelta;

  my @next_state = (0,7,7,0,0,0,7,
                    0,7,7,7,0,0,7);
  my @tdir6 = (0,1,3,2,0,0,-1,
               -1,0,0,2,3,1,0);
  sub n_to_totalturn6 {
    my ($self, $n) = @_;
    unless ($n >= 0) {
      return undef;
    }
    my $state = 0;
    my $tdir6 = 0;
    foreach my $digit (reverse digit_split_lowtohigh($n,7)) {
      $state += $digit;
      $tdir6 += $tdir6[$state];
      $state = $next_state[$state];
    }
    return $tdir6 % 6;
  }

  {
    my $class = 'Math::PlanePath::Flowsnake';
    my $path = $class->new;
    my $seq = Math::NumSeq::PlanePathDelta->new (planepath=>'Flowsnake',
                                                 delta_type => 'TDir6');
    for (my $n = 0; $n < 7**3; $n+=1) {
      my $value = $seq->ith($n);
      my $tdir6 = n_to_totalturn6($path,$n) % 6;
      my $diff = ($value != $tdir6 ? '   ***' : '');
      print "$n  $value $tdir6$diff\n";
    }
    exit 0;
  }


  # sub _digit_lowest {
  #   my ($n, $radix) = @_;
  #   my $digit;
  #   for (;;) {
  #     last if ($digit = ($n % 7));
  #     $n /= 7;
  #     last unless $n;
  #   }
  #   # if ($digit < 1_000_000) {
  #   #   $digit = "$digit";
  #   # }
  #   return $digit;
  # }
}

{
  # N to Turn6 -- working for integers

  require Math::PlanePath::Flowsnake;
  require Math::NumSeq::PlanePathDelta;
  my $class = 'Math::PlanePath::Flowsnake';
  my $path = $class->new;
  my $seq = Math::NumSeq::PlanePathDelta->new (planepath=>'Flowsnake',
                                               delta_type => 'TDir6');
  for (my $n = 1; $n < 7**4; $n+=1) {
    my $value = ($seq->ith($n) - $seq->ith($n-1)) % 6;
    $value += 2; # range -2 to +2
    $value %= 6;
    $value -= 2;
    my $turn = $path->_WORKING_BUT_SECRET__n_to_turn6($n);
    my $diff = ($value != $turn ? '   ***' : '');
    print "$n  $value $turn$diff\n";
    die if $value != $turn;
  }
  exit 0;
}
{
  require Math::PlanePath::Flowsnake;
  require Math::PlanePath::FlowsnakeCentres;
  my $f = Math::PlanePath::Flowsnake->new (arms => 2);
  my $c = Math::PlanePath::FlowsnakeCentres->new (arms => 2);
  my $width = 5;
  my %saw;
  foreach my $n (0 .. 7**($width-1)) {
    my ($x,$y) = $f->n_to_xy($n);

    my $cn = $c->xy_to_n($x,$y) // -1;

    my $cr = $c->xy_to_n($x+2, $y) // -1;
    my $ch = $c->xy_to_n($x+1,$y+1) // -1;
    my $cw = $c->xy_to_n($x-1,$y+1) // -1;
    my $cl = $c->xy_to_n($x-2,$y) // -1;       # <------
    my $cu = $c->xy_to_n($x-1,$y-1) // -1;     # <------3
    my $cz = $c->xy_to_n($x+1,$y-1) // -1;

    if ($n == $cn) { $saw{'n'} = 0; }
    if ($n == $cr) { $saw{'r'} = 1; }
    if ($n == $ch) { $saw{'h'} = 2; }
    if ($n == $cw) { $saw{'w'} = 3; }
    if ($n == $cl) { $saw{'l'} = 4; }
    if ($n == $cu) { $saw{'u'} = 5; }
    if ($n == $cz) { $saw{'z'} = 6; }

    unless (($n == $cn)
            || ($n == $cr)
            || ($n == $ch)
            || ($n == $cw)
            || ($n == $cl)
            || ($n == $cu)
            || ($n == $cz)) {
      die "no match $n: $cn,$cr,$ch,$cw,$cl,$cu,$cz";
    }
  }
  my $saw = join(',', sort {$saw{$a}<=>$saw{$b}} keys %saw);
  print "$saw\n";
  exit 0;
}
{
  require Math::PlanePath::Flowsnake;
  require Math::PlanePath::FlowsnakeCentres;
  say Math::PlanePath::Flowsnake->isa('Math::PlanePath::FlowsnakeCentres');
  say Math::PlanePath::FlowsnakeCentres->isa('Math::PlanePath::Flowsnake');
  say Math::PlanePath::Flowsnake->can('xy_to_n');
  say Math::PlanePath::FlowsnakeCentres->can('xy_to_n');
  exit 0;
}

{
  require Math::BaseCnv;
  require Math::PlanePath::Flowsnake;
  require Math::PlanePath::FlowsnakeCentres;
  my $c = Math::PlanePath::Flowsnake->new;
  my $f = Math::PlanePath::FlowsnakeCentres->new;
  my $width = 5;
  my %saw;
  foreach my $n (0 .. 7**($width-1)) {
    my $n7 = sprintf '%*s', $width, Math::BaseCnv::cnv($n,10,7);
    my ($x,$y) = $f->n_to_xy($n);

    my $cn = $c->xy_to_n($x,$y) || -1;
    my $cn7 = sprintf '%*s', $width, Math::BaseCnv::cnv($cn,10,7);

    my $rx = $x + 1;
    my $ry = $y + 1;
    my $cr = $c->xy_to_n($rx,$ry) || -1;
    my $cr7 = sprintf '%*s', $width, Math::BaseCnv::cnv($cr,10,7);

    my $hx = $x + 1;
    my $hy = $y + 1;
    my $ch = $c->xy_to_n($hx,$hy) || -1;
    my $ch7 = sprintf '%*s', $width, Math::BaseCnv::cnv($ch,10,7);

    my $wx = $x - 1;
    my $wy = $y + 1;
    my $cw = $c->xy_to_n($wx,$wy) || -1;
    my $cw7 = sprintf '%*s', $width, Math::BaseCnv::cnv($cw,10,7);

    my $lx = $x - 2;
    my $ly = $y;
    my $cl = $c->xy_to_n($lx,$ly) || -1;
    my $cl7 = sprintf '%*s', $width, Math::BaseCnv::cnv($cl,10,7);

    my $ux = $x - 1;
    my $uy = $y - 1;
    my $cu = $c->xy_to_n($ux,$uy) || -1;
    my $cu7 = sprintf '%*s', $width, Math::BaseCnv::cnv($cu,10,7);

    my $zx = $x + 1;
    my $zy = $y - 1;
    my $cz = $c->xy_to_n($zx,$zy) || -1;
    my $cz7 = sprintf '%*s', $width, Math::BaseCnv::cnv($cz,10,7);

    if ($n == $cn) { $saw{'n'} = 0; }
    if ($n == $cr) { $saw{'r'} = 1; }
    if ($n == $ch) { $saw{'h'} = 2; }
    if ($n == $cw) { $saw{'w'} = 3; }
    if ($n == $cl) { $saw{'l'} = 4; }
    if ($n == $cu) { $saw{'u'} = 5; }
    if ($n == $cz) { $saw{'z'} = 6; }
    my $bad = ($n == $cn
               || $n == $cr
               || $n == $ch
               || $n == $cw
               || $n == $cl
               || $n == $cu
               || $n == $cz
               ? ''
               : '  ******');

    # print "$n7 $cn7 $ch7 $cw7 $cu7   $bad\n";
  }
  my $saw = join(',', sort {$saw{$a}<=>$saw{$b}} keys %saw);
  print "$saw\n";
  exit 0;
}

{
  require Math::BaseCnv;
  require Math::PlanePath::Flowsnake;
  my $path = Math::PlanePath::Flowsnake->new;

  foreach my $y (reverse -5 .. 40) {
    printf "%3d ", $y;
    foreach my $x (-20 .. 15) {
      my $n = $path->xy_to_n($x,$y);
      if (! defined $n) {
        print "  ";
        next;
      }

      my $nh = $n - ($n%7);
      my ($hx,$hy) = $path->n_to_xy($nh);
      my $pos = '?';
      if ($hy > $y) {
        $pos = 'T';
      } elsif ($hx > $x) {
        $pos = '.';
      } else {
        $pos = '*';
        $pos = $n%7;
      }

      print "$pos ";
    }
    print "\n";
  }
  exit 0;
}

{
  require Math::BaseCnv;
  require Math::PlanePath::Flowsnake;
  require Math::PlanePath::FlowsnakeCentres;
  my $f = Math::PlanePath::Flowsnake->new;
  my $c = Math::PlanePath::FlowsnakeCentres->new;
  my $width = 5;
  foreach my $n (0 .. 7**($width-1)) {
    my $n7 = sprintf '%*s', $width, Math::BaseCnv::cnv($n,10,7);
    my ($x,$y) = $f->n_to_xy($n);

    my $cn = $c->xy_to_n($x,$y) || 0;
    my $cn7 = sprintf '%*s', $width, Math::BaseCnv::cnv($cn,10,7);

    my $m = ($x + 2*$y) % 7;
    if ($m == 2) {  # 2,0  = 2
      $x -= 2;
    } elsif ($m == 5) {  # 3,1 = 3+2*1 = 5
      $x -= 3;
      $y -= 1;
    } elsif ($m == 3) {  # 1,1 = 1+2 = 3
      $x -= 1;
      $y -= 1;
    } elsif ($m == 4) {  # 0,2 = 0+2*2 = 4
      $y -= 2;
    } elsif ($m == 6) {  # 2,2 = 2+2*2 = 6
      $x -= 2;
      $y -= 2;
    } elsif ($m == 1) {  # 4,2 = 4+2*2 = 8 = 1
      $x -= 4;
      $y -= 2;
    }
    my $mn = $c->xy_to_n($x,$y) || 0;
    my $mn7 = sprintf '%*s', $width, Math::BaseCnv::cnv($mn,10,7);

    my $nh = $n - ($n%7);
    my $mh = $mn - ($mn%7);

    my $diff = ($nh == $mh ? "" : "   **");
    print "$n7 $mn7   $cn7$diff\n";
  }
  exit 0;
}

{
  # xy_to_n
  require Math::PlanePath::Flowsnake;
  require Math::PlanePath::FlowsnakeCentres;
  my $path = Math::PlanePath::FlowsnakeCentres->new;
  my $k = 4000;
  my ($n_lo,$n_hi) = $path->rect_to_n_range(-$k,-$k, $k,$k);
  print "$n_lo, $n_hi\n";
  exit 0;
}

{
  # xy_to_n
  require Math::PlanePath::Flowsnake;
  require Math::PlanePath::FlowsnakeCentres;
  my $path = Math::PlanePath::FlowsnakeCentres->new;
  my $y = 0;
  for (my $x = 6; $x >= -5; $x-=2) {
    $x -= ($x^$y)&1;
    my $n = $path->xy_to_n($x,$y);
    print "$x,$y   ",($n//'undef'),"\n";
  }
  exit 0;
}

{
  # modulo
  require Math::PlanePath::Flowsnake;
  my $path = Math::PlanePath::Flowsnake->new;
  for (my $n = 0; $n <= 49; $n++) {
    if (($n % 7) == 0) { print "\n"; }
    my ($x,$y) = $path->n_to_xy($n);
    my $c = $x + 2*$y;
    my $m = $c % 7;
    print "$n  $x,$y  $c  $m\n";
  }
  exit 0;
}
{
  require Math::PlanePath::Flowsnake;
  my $path = Math::PlanePath::Flowsnake->new;
  for (my $n = 0; $n <= 49; $n+=7) {
    my ($x,$y) = $path->n_to_xy($n);
    my ($rx,$ry) = ((3*$y + 5*$x) / 14,
                    (5*$y - $x) / 14);
    print "$n  $x,$y  $rx,$ry\n";
  }
  exit 0;
}
  
{
  # radius
  require Math::PlanePath::Flowsnake;
  my $path = Math::PlanePath::Flowsnake->new;
  my $prev_max = 1;
  for (my $level = 1; $level < 10; $level++) {
    print "level $level\n";

    my ($x2,$y2) = $path->n_to_xy(2 * 7**($level-1));
    my ($x3,$y3) = $path->n_to_xy(3 * 7**($level-1));
    my $cx = ($x2+$x3)/2;
    my $cy = ($y2+$y3)/2;
    my $max_hypot = 0;
    my $max_pos = '';
    foreach my $n (0 .. 7**$level - 1) {
      my ($x,$y) = $path->n_to_xy($n);
      my $h = ($x-$cx)**2 + 3*($y-$cy);
      if ($h > $max_hypot) {
        $max_hypot = $h;
        $max_pos = "$x,$y";
      }
    }
    my $factor = $max_hypot / $prev_max;
    $prev_max = $max_hypot;
    print "  cx=$cx,cy=$cy  max $max_hypot   at $max_pos  factor $factor\n";
  }
  exit 0;
}


{
  require Math::PlanePath::Flowsnake;
  my $path = Math::PlanePath::Flowsnake->new;
  my $prev_max = 1;
  for (my $level = 1; $level < 10; $level++) {
    my $n_start = 0;
    my $n_end = 7**$level - 1;
    my $min_hypot = $n_end;
    my $min_x = 0;
    my $min_y = 0;
    my $max_hypot = 0;
    my $max_pos = '';
    print "level $level\n";
    my ($xend,$yend) = $path->n_to_xy(7**($level-1));
    print "   end $xend,$yend\n";
    $yend *= sqrt(3);
    my $cx = -$yend;  # rotate +90
    my $cy = $xend;
    print "   rot90  $cx, $cy\n";
    # $cx *= sqrt(3/4) * .5;
    # $cy *= sqrt(3/4) * .5;
    $cx *= 1.5;
    $cy *= 1.5;
    print "   scale  $cx, $cy\n";
    $cx += $xend;
    $cy += $yend;
    print "   offset to  $cx, $cy\n";
    $cy /= sqrt(3);
    printf "  centre %.1f, %.1f\n", $cx,$cy;
    foreach my $n ($n_start .. $n_end) {
      my ($x,$y) = $path->n_to_xy($n);
      my $h = ($cx-$x)**2 + 3*($cy-$y)**2;

      if ($h > $max_hypot) {
        $max_hypot = $h;
        $max_pos = "$x,$y";
      }
      # if ($h < $min_hypot) {
      #   $min_hypot = $h;
      #   $min_x = $x;
      #   $min_y = $y;
      # }
    }
    # print "  min $min_hypot   at $min_x,$min_y\n";
    my $factor = $max_hypot / $prev_max;
    print "  max $max_hypot   at $max_pos  factor $factor\n";
    $prev_max = $max_hypot;
  }
  exit 0;
}

{
  # diameter
  require Math::PlanePath::Flowsnake;
  my $path = Math::PlanePath::Flowsnake->new;
  my $prev_max = 1;
  for (my $level = 1; $level < 10; $level++) {
    print "level $level\n";
    my $n_start = 0;
    my $n_end = 7**$level - 1;
    my ($xend,$yend) = $path->n_to_xy($n_end);
    print "   end $xend,$yend\n";
    my @x;
    my @y;
    foreach my $n ($n_start .. $n_end) {
      my ($x,$y) = $path->n_to_xy($n);
      push @x, $x;
      push @y, $y;
    }
    my $max_hypot = 0;
    my $max_pos = '';
    my ($cx,$cy);
    foreach my $i (0 .. $#x-1) {
      foreach my $j (1 .. $#x) {
        my $h = ($x[$i]-$x[$j])**2 + 3*($y[$i]-$y[$j]);
        if ($h > $max_hypot) {
          $max_hypot = $h;
          $max_pos = "$x[$i],$y[$i], $x[$j],$y[$j]";
          $cx = ($x[$i] + $x[$j]) / 2;
          $cy = ($y[$i] + $y[$j]) / 2;
        }
      }
    }
    my $factor = $max_hypot / $prev_max;
    print "  max $max_hypot   at $max_pos  factor $factor\n";
    $prev_max = $max_hypot;
  }

  exit 0;
}

{
  require Math::PlanePath::GosperIslands;
  my $path = Math::PlanePath::GosperIslands->new;
  foreach my $level (0 .. 20) {
    my $n_start = 3**($level+1) - 2;
    my $n_end = 3**($level+2) - 2 - 1;
    my ($prev_x) = $path->n_to_xy($n_start);
    foreach my $n ($n_start .. $n_end) {
      my ($x,$y) = $path->n_to_xy($n);

      # if ($y == 0 && $x > 0) {
      #   print "level $level  x=$x y=$y n=$n\n";
      # }

      if (($prev_x>0) != ($x>0) && $y > 0) {
        print "level $level  x=$x y=$y n=$n\n";
      }
      $prev_x = $x;
    }
    print "\n";
  }
  exit 0;
}



sub hij_to_xy {
  my ($h, $i, $j) = @_;
  return ($h*2 + $i - $j,
          $i+$j);
}

{
  # y<0 at n=8598  x=-79,y=-1
  require Math::PlanePath::Flowsnake;
  my $path = Math::PlanePath::Flowsnake->new;
  for (my $n = 3; ; $n++) {
    my ($x,$y) = $path->n_to_xy($n);
    if ($y == 0) {
      print "zero n=$n  $x,$y\n";
    }
    if ($y < 0) {
      print "yneg n=$n  $x,$y\n";
      exit 0;
    }
    # if ($y < 0 && $x >= 0) {
    #   print "yneg n=$n  $x,$y\n";
    #   exit 0;
    # }
  }
  exit 0;
}

{
  {
    my $sh = 1;
    my $si = 0;
    my $sj = 0;
    my $n = 1;
    foreach my $level (1 .. 20) {
      $n *= 7;
      ($sh, $si, $sj) = (2*$sh - $sj,
                         2*$si + $sh,
                         2*$sj + $si);
      my ($x, $y) = hij_to_xy($sh,$si,$sj);
      $n = sprintf ("%f",$n);
      print "$level $n  $sh,$si,$sj  $x,$y\n";
    }
  }
  exit 0;
}


our $level;

my $n = 0;
my $x = 0;
my $y = 0;

my %seen;
my @row;
my $x_offset = 8;
my $dir = 0;

sub step {
  $dir %= 6;
  print "$n  $x, $y   dir=$dir\n";
  my $key = "$x,$y";
  if (defined $seen{$key}) {
    print "repeat   $x, $y  from $seen{$key}\n";
  }
  $seen{"$x,$y"} = $n;
  if ($y >= 0) {
    $row[$y]->[$x+$x_offset] = $n;
  }

  if ($dir == 0) { $x += 2; }
  elsif ($dir == 1) { $x++, $y++; }
  elsif ($dir == 2) { $x--, $y++; }
  elsif ($dir == 3) { $x -= 2; }
  elsif ($dir == 4) { $x--, $y--; }
  elsif ($dir == 5) { $x++, $y--; }
  else { die; }
  $n++;
}

sub forward {
  if ($level == 1) {
    step ();
    return;
  }
  local $level = $level-1;
  forward(); $dir++;           # 0
  backward(); $dir += 2;       # 1
  backward(); $dir--;          # 2
  forward(); $dir -= 2;           # 3
  forward();                   # 4
  forward();  $dir--;                 # 5
  backward(); $dir++;          # 6
}

sub backward {
  my ($dir) = @_;
  if ($level == 1) {
    step ();
    return;
  }
  print "backward\n";
  local $level = $level-1;

  $dir += 2;
  forward();
  forward();
  $dir--;                 # 5
  forward();
  $dir--;                 # 5
  forward();
  $dir--;                 # 5
  backward();
  $dir--;                 # 5
  backward();
  $dir--;                 # 5
  forward();
  $dir--;                 # 5
}

$level = 3;
forward (2);


foreach my $y (reverse 0 .. $#row) {
  my $aref = $row[$y];
  foreach my $x (0 .. $#$aref) {
    printf ('%*s', 3, (defined $aref->[$x] ? $aref->[$x] : ''));
  }
  print "\n";
}
