#!/usr/bin/perl -w

# Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2018 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.004;
use strict;
use List::Util 'min', 'max';
use Math::PlanePath::TerdragonCurve;
use Math::PlanePath;
*_divrem_mutate = \&Math::PlanePath::_divrem_mutate;
use Math::PlanePath::Base::Digits
  'digit_split_lowtohigh',
  'digit_join_lowtohigh';
use List::Pairwise;
use Math::BaseCnv;
use lib 'xt';
use MyOEIS;

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

# # skip low zeros
# # 1 left
# # 2 right
# ones(n) - ones(n+1)

# 1*3^k  left
# 2*3^k  right

{
  # arms=6 sample points for the POD

  require Math::PlanePath::TerdragonCurve;
  my $path = Math::PlanePath::TerdragonCurve->new (arms => 6);
  my $show = sub {
    my ($x,$y) = @_;
    my @n_list = $path->xy_to_n_list($x,$y);
    [join(',',@n_list)];
  };

  print "
                  \\         /             \\           /
                   \\       /               \\         /
                --- @{$show->(-1,1)} ---------------- @{$show->(1,1)} ---
                  /        \\               /         \\
     \\           /          \\             /           \\          /
      \\         /            \\           /             \\        /
    --- @{$show->(-2,0)} ------------- @{$show->(0,0)} -------------- @{$show->(2,0)} ---
      /         \\            /           \\             /        \\
     /           \\          /             \\           /          \\
                  \\        /               \\         /
               --- @{$show->(-1,-1)} ---------------- @{$show->(1,-1)} ---
                  /        \\               /         \\
                 /          \\             /           \\
";
  exit 0;
}

{
  require Math::PlanePath::TerdragonMidpoint;
  # my $path = Math::PlanePath::TerdragonMidpoint->new;
  my $path = Math::PlanePath::TerdragonCurve->new;
  require POSIX;
  my @n_list = $path->xy_to_n_list(POSIX::DBL_MAX(),POSIX::DBL_MAX());
  ### @n_list
  exit 0;
}

{
  # A062756 == 1-abs(A229215)  mod 3
  # A062756(n) = vecsum(apply(d->d==1,digits(n,3)));
  # A229215(n) = [1,-3,-2,-1,3,2][(A062756(n-1) % 6)+1];
  # A229215(n) = [1,2,3,-1,-2,-3][(-A062756(n-1) % 6)+1];
  # vector(20,n,n--; A062756(n))
  # vector(20,n, A229215(n))
  # A229215(n) = (digits(n,3))
  # A229215
  # 1, -3, 1, -3, -2, -3, 1, -3, 1, -3, -2, -3, -2, -1, -2, -3, -2, -3, 1,    


  require Math::NumSeq::OEIS;
  my $A062756 = Math::NumSeq::OEIS->new(anum=>'A062756');
  my $A229215 = Math::NumSeq::OEIS->new(anum=>'A229215');
  my @map = (1,2,3,-1,-2,-3);
  for (;;) {
    my ($i1,$value1) = $A062756->next or last;
    my ($i2,$value2) = $A229215->next or last;
    # $value1 %= 3;
    # $value2 = (1 - abs($value2)) % 3;

    $value1 = $map[-$value1 % 6];

    print "i=$i1  $value1 $value2\n";
     $value1 == $value2 or die;
  }
  exit 0;
}
{
  # some variations

  # cf A106154 terdragon 6 something
  #    A105499 terdragon permute something
  #     1->{2,1,2}, 2->{1,3,1}, 3->{3,2,3}.
  #     212323212131212131212323212323131323212323212323

  #  *   *              3   2 
  #   \ / \              \ /
  #    *---*        -1 ---*--- 1
  #     \                / \
  #  *---*             -2   -3      
  #    
  # A062756
  # 0, 1, 0, 1, 2, 1, 0, 1, 0, 1, 2, 1, 2, 3, 2, 1, 2, 1, 0, 1, 0, 1, 2, 1,   


  # 1,2,3 = 0,1,2
  # -1,-2,-3 = 3,4,5
  my @map123 = (undef, 0,1,2, 5,4,3);

  require Math::NumSeq::OEIS;
  my $seq;
  $seq = Math::NumSeq::OEIS->new(anum=>'A105969');
  $seq = Math::NumSeq::OEIS->new(anum=>'A106154');
  $seq = Math::NumSeq::OEIS->new(anum=>'A229215');

  require Language::Logo;
  my $lo = Logo->new(update => 2, port => 8200 + (time % 100));
  my $draw;
  # $lo->command("seth 135; backward 200; seth 90");
  $lo->command("pendown; hideturtle");
  my $angle = 0;
  while (my ($i,$value) = $seq->next) {
    last if $i > 3**3;
    $value = $map123[$value];
    $angle = $value*120;
    # $angle = 90-$angle;
    $angle += 90;
    $lo->command("seth $angle; forward 13");
  }
  $lo->disconnect("Finished...");
  exit 0;
}

{
  # powers (1+w)^k
  # w^2 = -1+w
  # (a+bw)*(1+w) = a+bw + aw+bw^2
  #              = a + bw + aw - b + bw
  #              = (a-b) + (a+2b)w
  # a+bw = (a+b) + bw^2
  my $a = 1;
  my $b = 0;
  my @values;
  for (1 .. 30) {
    push @values, -($a+$b);
    ($a,$b) = ($a-$b, $a+2*$b);
  }
  for (1 .. 20) {
    print "$_\n";
    Math::OEIS::Grep->search(array=>\@values);
  }
  exit 0;
}
{
  # mixed ternary grep
  my @values;
  foreach my $n (1 .. 3*2**3) {
    my @digits = Math::PlanePath::TerdragonCurve::_digit_split_mix23_lowtohigh($n);
    push @values, digit_join_lowtohigh(\@digits,3);
  }
  print join(',',@values),"\n";
  Math::OEIS::Grep->search(array => \@values);
  exit 0;
}

=head2 Left Boundary Turn Sequence

The left boundary turn sequence is

    Lt(i) = / if i == 1 mod 3 then  turn -120   (right)
            | otherwise
            | let b = bit above lowest 1-bit of i-floor((i+1)/3)
            | if b = 0 then         turn 0      (straight ahead)
            \ if b = 1 then         turn +120   (left)

    = 1, 0, 0, 1, -1, 0, 1, 0, -1, 1, -1, 0, 1, 0, 0, 1, -1, -1, ...
      starting i=1, multiple of 120 degrees

The sequence can be calculated in a similar way to the right boundary, but
from an initial V part since the "0" and "2" points are on the left boundary
(and "1" is not).

         2
    Vrev  \
           \
      0-----1

This expands as

    2     *      initial
     \   / \       Vtrev[0] = 1
      \ /   \      Rtrev[0] = empty
       a-----1
        \          Vtrev[1] = Vtrev[0], 0, Rtrev[0]
         \                  = 1, 0   (at "*" and "a")
    0-----*

     Vtrev[k+1] = Vtrev[k], 0, Rtrev[k]
     Rtrev[k+1] = Vtrev[k], 1, Rtrev[k]
  The
R and V parts are the same on the left, but are to be taken in reverse.

The left side 0 to 2 is the same V shape as on the right (by symmetry), but
the points are in reverse.

=head2 Right and Left Turn Matching

=cut

{
  # segments by direction
  # A092236, A135254, A133474
  # A057083 half term, offset from 3^k, A103312 similar

  require Math::PlanePath::TerdragonCurve;
  my $path = Math::PlanePath::TerdragonCurve->new;
  my %count;
  my %count_arrays;
  my $n = 0;
  my @dxdy_strs = List::Pairwise::mapp {"$a,$b"} $path->_UNDOCUMENTED__dxdy_list;
  my $width = 36;
  foreach my $k (12 .. 23) {
    my $n_end = 3**$k * 0;
    for ( ; $n < $n_end; $n++) {
      my ($dx,$dy) = $path->n_to_dxdy($n);
      $count{"$dx,$dy"}++;
    }
    # printf "k=%2d ", $k;
    # foreach my $dxdy (@dxdy_strs) {
    #   my $a = $count{$dxdy} || 0;
    #   my $aref = ($count_arrays{$dxdy} ||= []);
    #   push @$aref, $a;
    #
    #   my $ar = Math::BaseCnv::cnv($a,10,3);
    #   printf " %18s", $ar;
    # }
    # print "\n";

    printf "k=%2d ", $k;
    foreach my $dxdy (@dxdy_strs) {
      my $a = _UNDOCUMENTED__level_to_segments_dxdy($path, $k, split(/,/, $dxdy));
      my $ar = Math::BaseCnv::cnv($a,10,3);
      printf " %*s", $width, $ar;
    }
    print "\n";
    print "     ";
    foreach my $dxdy (@dxdy_strs) {
      my $a = _UNDOCUMENTED__level_to_segments_dxdy_2($path, $k, split(/,/, $dxdy));
      my $ar = Math::BaseCnv::cnv($a,10,3);
      printf " %*s", $width, $ar;
    }
    print "\n";
    print "\n";
  }
  my $trim = 1;
  foreach my $dxdy (@dxdy_strs) {
    my $aref = $count_arrays{$dxdy} || [];
    splice @$aref, 0, $trim;
    # @$aref = MyOEIS::first_differences(@$aref);
    print "$dxdy\n";
    print "is ", join(',',@$aref),"\n";
    Math::OEIS::Grep->search (array => \@$aref, name => $dxdy);
  }

  sub _UNDOCUMENTED__level_to_segments_dxdy {
    my ($self, $level, $dx,$dy) = @_;
    my $a = 1;
    my $b = 0;
    my $c = 0;
    for (1 .. $level) {
      ($a,$b,$c) = (2*$a + $c,
                    2*$b + $a,
                    2*$c + $b);
    }
    if ($dx == 2 && $dy == 0) {
      return $a;
    }
    if ($dx == -1) {
      if ($dy == 1) {
        return $b;
      }
      if ($dy == -1) {
        return $c;
      }
    }
    return undef;
  }
  BEGIN {
    my @dir3_to_offset = (0,8,4);
    my @table = (2,1,1, 0,-1,-1, -2,-1,-1, 0,1,1);
    sub _UNDOCUMENTED__level_to_segments_dxdy_2 {
      my ($self, $level, $dx,$dy) = @_;
      my $ret = _dxdy_to_dir3($dx,$dy);
      if (! defined $ret) { return undef; }
      $ret = $table[($dir3_to_offset[$ret] + $level) % 12];
      $level -= 1;
      if ($ret) {
        $ret *= 3**int($level/2);
      }
      return 3**$level + $ret;
    }
  }
  sub _dxdy_to_dir3 {
    my ($dx,$dy) = @_;
    if ($dx == 2 && $dy == 0) {
      return 0;
    }
    if ($dx == -1) {
      if ($dy == 1) {
        return 1;
      }
      if ($dy == -1) {
        return 2;
      }
    }
    return undef;
  }
  # print "\n";
  # foreach my $k (0 .. $#a) {
  #   my $h = int($k/2);
  #   printf "%3d,", $d[$k];
  # }
  # print "\n";
  exit 0;
}

{
  # left boundary N

  # left_boundary_n_pred(14);
  # ### exit 0

  my $path = Math::PlanePath::TerdragonCurve->new;
  my %non_values;
  my %n_values;
  my @n_values;
  my @values;
  foreach my $k (4){
    print "k=$k\n";
    my $n_limit = 2*3**$k;
    foreach my $n (0 .. $n_limit-1) {
      $non_values{$n} = 1;
    }
    my $points = MyOEIS::path_boundary_points ($path, $n_limit,
                                               lattice_type => 'triangular',
                                               side => 'left',
                                              );
    @$points = reverse @$points; # for left
    ### $points
    for (my $i = 0; $i+1 <= $#$points; $i++) {
      my ($x,$y) = @{$points->[$i]};
      my ($x2,$y2) = @{$points->[$i+1]};
      # my @n_list = $path->xy_to_n_list($x,$y);
      my @n_list = path_xyxy_to_n($path, $x,$y, $x2,$y2);
      foreach my $n (@n_list) {
        delete $non_values{$n};
        if ($n <= $n_limit) { $n_values{$n} = 1; }
        my $n3 = Math::BaseCnv::cnv($n,10,3);
        my $pred = $path->_UNDOCUMENTED__n_segment_is_left_boundary($n);
        my $diff = $pred ? '' : '  ***';
        if ($k <= 4) { print "$n  $n3$diff\n"; }
      }
    }
    @n_values = keys %n_values;
    @n_values = sort {$a<=>$b} @n_values;
    my @non_values = keys %non_values;
    @non_values = sort {$a<=>$b} @non_values;
    my $count = scalar(@n_values);
    print "count $count\n";

    # push @values, $count;
    @values = @n_values;

    if ($k <= 4) {
      foreach my $n (@non_values) {
        my $pred = $path->_UNDOCUMENTED__n_segment_is_left_boundary($n);
        my $diff = $pred ? '  ***' : '';
        my $n3 = Math::BaseCnv::cnv($n,10,3);
        print "non $n  $n3$diff\n";
      }
    }
    # @values = @non_values;

    print "func ";
    foreach my $i (0 .. $count-1) {
      my $n = $path->_UNDOCUMENTED__left_boundary_i_to_n($i);
      my $n3 = Math::BaseCnv::cnv($n,10,3);
      print "$n,";
    }
    print "\n";

    print "vals ";
    foreach my $i (0 .. $count-1) {
      my $n = $values[$i];
      my $n3 = Math::BaseCnv::cnv($n,10,3);
      print "$n3,";
    }
    print "\n";
  }

  # @values = MyOEIS::first_differences(@values);
  # shift @values;
  # shift @values;
  # shift @values;
  print join(',',@values),"\n";
  Math::OEIS::Grep->search(array => \@values);
  exit 0;
}

{
  # right boundary N

  # $path->_UNDOCUMENTED__n_segment_is_right_boundary(14);
  # ### exit 0

  my $path = Math::PlanePath::TerdragonCurve->new;
  my %non_values;
  my %n_values;
  my @n_values;
  my @values;
  foreach my $k (4){
    print "k=$k\n";
    my $n_limit = 3**$k;
    foreach my $n (0 .. $n_limit-1) {
      $non_values{$n} = 1;
    }
    my $points = MyOEIS::path_boundary_points ($path, $n_limit,
                                               lattice_type => 'triangular',
                                               side => 'right',
                                              );

    # $points = points_2of3($points);
    for (my $i = 0; $i+1 <= $#$points; $i++) {
      my ($x,$y) = @{$points->[$i]};
      my ($x2,$y2) = @{$points->[$i+1]};
      # my @n_list = $path->xy_to_n_list($x,$y);
      my @n_list = path_xyxy_to_n($path, $x,$y, $x2,$y2);
      foreach my $n (@n_list) {
        delete $non_values{$n};
        if ($n <= $n_limit) { $n_values{$n} = 1; }
        my $n3 = Math::BaseCnv::cnv($n,10,3);
        my $pred = $path->_UNDOCUMENTED__n_segment_is_right_boundary($n);
        my $diff = $pred ? '' : '  ***';
        if ($k <= 4) { print "$n  $n3$diff\n"; }
      }
    }
    @n_values = keys %n_values;
    @n_values = sort {$a<=>$b} @n_values;
    my @non_values = keys %non_values;
    @non_values = sort {$a<=>$b} @non_values;
    my $count = scalar(@n_values);
    print "count $count\n";

    # push @values, $count;
    @values = @n_values;

    if ($k <= 4) {
      foreach my $n (@non_values) {
        my $pred = $path->_UNDOCUMENTED__n_segment_is_right_boundary($n);
        my $diff = $pred ? '  ***' : '';
        my $n3 = Math::BaseCnv::cnv($n,10,3);
        print "non $n  $n3$diff\n";
      }
    }
    # @values = @non_values;

    print "func ";
    foreach my $i (0 .. $count-1) {
      my $n = $path->_UNDOCUMENTED__right_boundary_i_to_n($i);
      my $n3 = Math::BaseCnv::cnv($n,10,3);
      print "$n3,";
    }
    print "\n";

    print "vals ";
    foreach my $i (0 .. $count-1) {
      my $n = $values[$i];
      my $n3 = Math::BaseCnv::cnv($n,10,3);
      print "$n,";
    }
    print "\n";
  }

  # @values = MyOEIS::first_differences(@values);
  # shift @values;
  # shift @values;
  # shift @values;
  print join(',',@values),"\n";
  Math::OEIS::Grep->search(array => \@values);
  exit 0;

  sub path_xyxy_to_n {
    my ($path, $x1,$y1, $x2,$y2) = @_;
    ### path_xyxy_to_n(): "$x1,$y1, $x2,$y2"
    my @n_list = $path->xy_to_n_list($x1,$y1);
    ### @n_list
    my $arms = $path->arms_count;
    foreach my $n (@n_list) {
      my ($x,$y) = $path->n_to_xy($n + $arms);
      if ($x == $x2 && $y == $y2) {
        return $n;
      }
    }
    return;
  }
}






{

=head2 Boundary Straight 2s

1 x straight
Right
j=2  010    left        j == 2 mod 8
j=3   11    straight    i == 3 mod 12
j=   1100   straight    trailing 0s >= 2
j=   1101   left

2 x straight
Right
i=9  j=6  110
i=10 j=7  111
even ...110    so j == 6 mod 8
odd  ...111       i == 9 mod 12
i=21 +12
i=22 +12

Left
odd   even
N and N+1 both bit-above-low-1 = 1 both straight
2m-1  2m
odd must be ...11
odd+1  x100
must be ...1100
so odd 1011  is 11 mod 16

=cut

       # A083575 length=1
       # 2^(k-2) - 1  length=2
       # 2^(k-3)      length=3
       #
       # 3*2^(k-1) - 2*(2^(k-2) - 1) - 3*2^(k-3)
       # = 12*2^(k-3) - 4*2^(k-3) + 1 - 3*2^(k-3)
       # = 5*2^(k-3) + 1
       #
       require Math::NumSeq::PlanePathTurn;
       my $path = Math::PlanePath::TerdragonCurve->new;
       my $seq = Math::NumSeq::PlanePathTurn->new(planepath_object => $path,
                                                  turn_type => 'LSR');
       my @values;
       foreach my $k (1 .. 12) {
         print "k=$k\n";
         my $points = MyOEIS::path_boundary_points ($path, 3**$k,
                                                    lattice_type => 'triangular',
                                                    side => 'right',
                                                   );
         my $run = 0;
         my @count = (0,0,0);
         for (my $i = 0; $i+2 <= $#$points; $i++) {
           my $tturn6 = points_to_tturn6($points->[$i], $points->[$i+1], $points->[$i+2]);
           if ($tturn6 == 0) {
             $run++;
           } else {
             $count[$run]++;
             $run = 0;
           }
         }
         print "$count[0]  $count[1]  $count[2]\n";
         push @values, $count[0];
       }
       shift @values;
       shift @values;
       Math::OEIS::Grep->search(array => \@values);
       exit 0;
     }



=head2 Boundary Isolated Triangles

When the boundary visits a point twice it does so by enclosing a single unit
triangle.  This is seen for example in the turn sequence diagram above where
turns 5 and 8 are at the same point and the turns go -1, 1, 1, -1 to enclose
a single unit triangle.

    \     7  Rt(7)=1
     \   / \
      \8/   \
       *-----6  Rt(6)=1
        \5  Rt(5)=-1
         \
          \

             *     *
            / \   / \
           /   \ /   \
    \     *-----*-----*
     \   / \   / \
      \ /   \ /   \
       *     *-----*
              \
               \
                \

=cut

{
  # shortcut boundary length = 2^k  area = 2*3^(k-1)
  #
  #        *-----*
  #         \
  #          \
  #     *-----*
  #
  my $path = Math::PlanePath::TerdragonCurve->new;
  my @values;
  foreach my $k (1 .. 7) {
    print "k=$k\n";
    my $points = MyOEIS::path_boundary_points ($path, 3**$k,
                                               lattice_type => 'triangular',
                                               # side => 'right',
                                              );
    $points = points_2of3($points);
    # points_shortcut_triangular($points);
    if (@$points < 10) {
      print join(" ", map{"$_->[0],$_->[1]"} @$points),"\n";
    }
    my $length = scalar(@$points) - 0;

    require Math::Geometry::Planar;
    my $polygon = Math::Geometry::Planar->new;
    $polygon->points($points);
    my $area = $polygon->area;

    print "  shortcut boundary $length area $area\n";
    push @values, $area;
  }
  Math::OEIS::Grep->search(array => \@values);
  exit 0;

  sub points_2of3 {
    my ($points) = @_;
    my @ret;
    foreach my $i (0 .. $#$points) {
      if ($i % 3 != 2) { push @ret, $points->[$i]; }
    }
    return \@ret;
  }

  sub points_shortcut_triangular {
    my ($points) = @_;
    my $print = (@$points < 20);
    my $i = 0;
    while ($i+2 <= $#$points) {
      my $tturn6 = points_to_tturn6($points->[$i], $points->[$i+1], $points->[$i+2]);
      if ($tturn6 == 4) {
        splice @$points, $i+1, 1;
        if ($print) { print "  delete point ",$i+1,"\n"; }
      } else {
        if ($print) { print "  keep point ",$i+1,"\n"; }
        $i++;
      }
      # my $p1 = $points->[$i];
      # my $p2 = $points->[$i+2];
      # if (abs($p1->[0] - $p2->[0]) + abs($p1->[1] - $p2->[1]) == 2) {
      #   splice @$points, $i+1, 1;
      #   if ($print) { print "  delete point ",$i+1,"\n"; }
      # } else {
      #   if ($print) { print "  keep point ",$i+1,"\n"; }
      #   $i++;
      # }
    }
  }
}

{
  # shortcut turn sequence, is dragon turn sequence by 60 degrees
  #
  my $path = Math::PlanePath::TerdragonCurve->new;
  my @values;
  foreach my $k (1 .. 7) {
    print "k=$k\n";
    my $points = MyOEIS::path_boundary_points ($path, 3**$k,
                                               lattice_type => 'triangular',
                                                side => 'right',
                                              );
    points_shortcut_triangular($points);
    for (my $i = 0; $i+2 <= $#$points; $i++) {
      my $tturn6 = points_to_tturn6($points->[$i], $points->[$i+1], $points->[$i+2]);
      print "$tturn6";
      if ($k == 5) {
        push @values, ($tturn6 == 1 ? 1 : $tturn6 == 5 ? -1 : die);
      }
    }
    print "\n";
  }
  Math::OEIS::Grep->search(array => \@values);
  exit 0;
}



{
  # boundary turn sequence

  #    26----27      0 to 8    2 4 2 0 4
  #      \           9 to 26   2 2 4 0 0 4
  #       \         27         2 2 4 2 0 4 0 2 4 0 0 4
  #       22        81         2 2 4 2 0 4 2 2 4 0 0 4 0 2 4 2 0 4 0 2 4 0 0 4
  #         \                  2 2 4 2 0 4 2 2 4 0 0 4 2 2 4 2 0 4 0 2 4 0 0 4 0 2 4 2 0 4 2 2 4 0 0 4 0 2 4 2 0 4 0 2 4 0 0 4
  #          \
  #          12    10
  #          / \   / \
  #         /   \ /   \
  # 18    13-----8-----9          Rlen = 1, 3*2^(k-1)
  #   \   / \   / \  V            Vlen = 2, 3*2^(k-1)
  #    \ /   \ /   \
  #    17     6----7,4            R -> R,2,V      R[1] = 2,4
  #            \   / \  R         V -> R,0,V      V[1] = 0,4
  #             \ /   \
  #             5,2----3          R[2] = 2,4 2 0,4
  #               \   V           V[2] = 2,4 0 0,4
  #                \
  #           0-----1             bit above lowest 1 like dragon
  #              R
  #
  # R[k+1]

  my $side = 'left';

  my (@R, @V);
  if ($side eq 'right') {
    @R = ('');
    @V = ('4');
  } else {
    @R = ('');
    @V = ('2');
  }

  #   2 4     0    0    turn = ternary lowest non-zero 1=left 2=right
  # 2 0 4     1    1
  # 2 2 4    10    2
  # 0 0 4    11   10
  # 2 2 4   100   11
  # 2 0 4   101   12
  # 0 2 4   110   20
  # 0 0 4   111   21
  # 2 2 4  1000   22
  # 2 0 4        100
  # 2 2 4        101
  # 0 0 4        102
  # 0 2 4        110
  # 2 0 4        111
  # 0 2 4        112
  # 0 0 4        120
  # 2 2 4        121
  # 2 0 4        122
  # 2 2 4        200
  # 0 0 4        201
  # 2 2 4
  # 2 0 4
  # 0 2 4
  # 0 0 4
  # 0 2 4
  # 2 0 4
  # 2 2 4
  # 0 0 4
  # 0 2 4
  # 2 0 4
  # 0 2 4
  # 0 0 4

  sub Tt_to_tturn6 {
    if ($side eq 'right') {
      goto &Rt_to_tturn6;
    } else {
      goto &Lt_to_tturn6;
    }
  }

  sub Rt_to_tturn6 {
    my ($i) = @_;
    {
      if ($i % 3 == 2) { return 4; }
      my $j = $i - int($i/3);
      return (bit_above_lowest_zero($j) ? 0 : 2);
    }
    {
      my $mod = _divrem_mutate($i, 3);
      if ($mod == 2) { return 4; }
      if ($mod == 1) { return ($i % 2 ? 0 : 2); }
      do {
        $mod = _divrem_mutate($i, 2);
      } while ($mod == 0);
      $mod = _divrem_mutate($i, 2);
      return ($mod % 2 ? 0 : 2);
    }
  }

  # i=0
  # i=1  2
  # i=2     j=1
  # i=3     j=2
  # i=4  2
  # i=5     j=3
  # i=6     j=4
  # i=7  2
  # i=8     j=5
  # i=9     j=6
  sub Lt_to_tturn6 {
    my ($i) = @_;
    {
      if ($i % 3 == 1) { return 2; }
      my $j = $i - int(($i+1)/3);
      # print "i=$i j=$j\n";
      return (bit_above_lowest_one($j) ? 4 : 0);
    }
  }

  sub bit_above_lowest_one {
    my ($n) = @_;
    for (;;) {
      if (! $n || ($n % 2) != 0) {
        last;
      }
      $n = int($n/2);
    }
    $n = int($n/2);
    return ($n % 2);
  }
  sub bit_above_lowest_zero {
    my ($n) = @_;
    for (;;) {
      if (($n % 2) == 0) {
        last;
      }
      $n = int($n/2);
    }
    $n = int($n/2);
    return ($n % 2);
  }

  my @dir6_to_dx = (2, 1,-1,-2, -1, 1);
  my @dir6_to_dy = (0, 1, 1, 0, -1,-1);

  my $path = Math::PlanePath::TerdragonCurve->new;
  require Math::NumSeq::PlanePathTurn;
  require Math::NumSeq::PlanePathDelta;

  foreach my $k (1 .. 7) {
    print "k=$k\n";
    if ($side eq 'right') {
      $R[$k] = $R[$k-1] . '2' . $V[$k-1];
      $V[$k] = $R[$k-1] . '0' . $V[$k-1];
    } else {
      $V[$k] = $V[$k-1] . '0' . $R[$k-1];
      $R[$k] = $V[$k-1] . '4' . $R[$k-1];
    }

    my $n_limit = ($side eq 'right' ? 3**$k : 2*3**$k);
    my $points = MyOEIS::path_boundary_points ($path, $n_limit,
                                               lattice_type => 'triangular',
                                               side => $side);
    if ($side eq 'left') {
      @$points = reverse @$points;
    }
    if (@$points < 20) {
      print "points";
      foreach my $p (@$points) {
        print "  $p->[0],$p->[1]";
      }
      print "\n";
    }
    my @values;
    foreach my $i (1 .. $#$points - 1) {
      my $tturn6 = points_to_tturn6($points->[$i-1], $points->[$i], $points->[$i+1]);
      # if ($tturn6 > 3) { $tturn6 -= 6; }
      # my $dir6 = Math::NumSeq::PlanePathDelta::_delta_func_TDir6($dx,$dy);
      # if ($dir6 > 3) { $dir6 -= 6; }
      push @values, $tturn6;
    }

    # {
    #   my @new_values;
    #   for (my $i = 2; $i <= $#values; $i += 3) {
    #     push @new_values, $values[$i] / 2;
    #   }
    #   @values = @new_values;
    # }

    Math::OEIS::Grep->search(array => \@values);

    my $v = join('',@values);
    print "p $v\n";
    if ($side eq 'right') {
      print "R $R[$k]\n";
      if ($v ne $R[$k]) {
        print "  wrong\n";
      }
    } else {
      print "V $V[$k]\n";
      if ($v ne $V[$k]) {
        print "  wrong\n";
      }
    }
    my $f = join('', map {Tt_to_tturn6($_)} 1 .. scalar(@values));
    print "f $f\n";
    if ($v ne $f) {
      print "  wrong\n";
    }
  }

  foreach my $i (1 .. 18) {
    my $tturn6 =  Tt_to_tturn6($i);
    my $pn = ($tturn6 == 2 ? 1 : $tturn6 == 0 ? 0 : $tturn6 == 4 ? -1 : die);
    print "$pn, ";
  }
  print "\n";

  exit 0;

  sub points_to_tturn6 {
    my ($p1,$p2,$p3) = @_;
    my ($x1,$y1) = @$p1;
    my ($x2,$y2) = @$p2;
    my ($x3,$y3) = @$p3;
    my $dx = $x2-$x1;
    my $dy = $y2-$y1;
    my $next_dx = $x3-$x2;
    my $next_dy = $y3-$y2;
    require Math::NumSeq::PlanePathTurn;
    return Math::NumSeq::PlanePathTurn::_turn_func_TTurn6($dx,$dy, $next_dx,$next_dy);
  }
}




{
  # dRadius range
  my $n = 118088;
  require Math::PlanePath::TerdragonMidpoint;
  my $path = Math::PlanePath::TerdragonMidpoint->new;
  my ($x1,$y1) = $path->n_to_xy($n);
    my ($x2,$y2) = $path->n_to_xy($n+1);
  print "$x1,$y1   $x2,$y2\n";
  exit 0;
}
{
  # A+Yw  A=X-Y
  require Math::BaseCnv;
  my $path = Math::PlanePath::TerdragonCurve->new;
  my $dx_min = 0;
  my $dx_max = 0;
  foreach my $n (1 .. 3**10) {
    my ($dx,$dy) = $path->n_to_dxdy($n);
    if ($dx == 299) {
      my $n3 = Math::BaseCnv::cnv($n,10,3);
      printf "%3d  %s\n", $n, $n3;
    }
    $dx_min = min($dx_min,$dx);
    $dx_max = max($dx_max,$dx);
  }
  print "$dx_min $dx_max\n";
  exit 0;
}
{
  # A+Yw  A=X-Y
  require Math::BaseCnv;
  my $path = Math::PlanePath::TerdragonCurve->new;
  my @values;
  foreach my $n (1 .. 3**6) {
    my @n_list = $path->n_to_n_list($n);
    if (@n_list == 1) {
      push @values, $n;
    }

    if (@n_list == 1 && $n == $n_list[0]) {
      my $n3 = Math::BaseCnv::cnv($n,10,3);
      printf "%3d  %s\n", $n, $n3;
    }
  }

  print join(',',@values),"\n";
  Math::OEIS::Grep->search(array=>\@values);
  exit 0;
}
{
  # A+Yw  A=X-Y
  my $path = Math::PlanePath::TerdragonCurve->new;
  my @values;
  foreach my $n (1 .. 20) {
    my ($x,$y) = $path->n_to_xy($n);
    push @values, ($x-$y);
  }
  Math::OEIS::Grep->search(array=>\@values);
  exit 0;
}


{
  # TerdragonCurve direction away from a point

  require Image::Base::Text;
  my $arms = 6;
  my $path = Math::PlanePath::TerdragonCurve->new (arms => $arms);

  my $width = 78;
  my $height = 40;
  my $x_lo = -$width/2;
  my $y_lo = -$height/2;

  my $x_hi = $x_lo + $width - 1;
  my $y_hi = $y_lo + $height - 1;
  my $image = Image::Base::Text->new (-width => $width,
                                      -height => $height);

  my $plot = sub {
    my ($x,$y,$char) = @_;
    $x -= $x_lo;
    $y -= $y_lo;
    return if $x < 0 || $y < 0 || $x >= $width || $y >= $height;
    $image->xy ($x,$height-1-$y,$char);
  };

  my ($n_lo, $n_hi) = $path->rect_to_n_range($x_lo-2,$y_lo-2, $x_hi+2,$y_hi+2);
  print "n_hi $n_hi\n";
  for my $n (0 .. $n_hi) {
    my $arm = $n % $arms;

    my ($x,$y) = $path->n_to_xy($n);
    next if $x < $x_lo || $y < $y_lo || $x > $x_hi || $y > $y_hi;

    my ($nx,$ny) = $path->n_to_xy($n + $arms);
    my $dir = dxdy_to_dir6($nx-$x,$ny-$y);
    if ($dir == 2) {
      $plot->($x, $y, $dir);
    }
  }
  $plot->(0,0, '+');
  $image->save('/dev/stdout');

  exit 0;
}

{
  # TerdragonCurve xy_to_n offsets to Midpoint

  require Math::PlanePath::TerdragonMidpoint;
  my $arms = 6;
  my $curve = Math::PlanePath::TerdragonCurve->new (arms => $arms);
  my $midpoint = Math::PlanePath::TerdragonMidpoint->new (arms => $arms);
  my %seen;
  for my $n (0 .. 1000) {
    my ($x,$y) = $curve->n_to_xy($n);
    $x *= 2;
    $y *= 2;

    for my $dx (-2 .. 2) {
      for my $dy (-1 .. 1) {

        my $m = $midpoint->xy_to_n($x+$dx,$y+$dy) // next;
        if ($m == $n) {
          $seen{"$dx,$dy"} = 1;
        }
      }
    }
  }
  ### %seen
  exit 0;
}

{
  # TerdragonCurve xy cf Midpoint

  require Image::Base::Text;
  require Math::PlanePath::TerdragonMidpoint;
  my $arms = 6;
  my $curve = Math::PlanePath::TerdragonCurve->new (arms => $arms);
  my $midpoint = Math::PlanePath::TerdragonMidpoint->new (arms => $arms);

  my $width = 50;
  my $height = 30;
  my $x_lo = -$width/2;
  my $y_lo = -$height/2;

  my $x_hi = $x_lo + $width - 1;
  my $y_hi = $y_lo + $height - 1;
  my $image = Image::Base::Text->new (-width => $width,
                                      -height => $height);

  my $plot = sub {
    my ($x,$y,$char) = @_;
    $x -= $x_lo;
    $y -= $y_lo;
    return if $x < 0 || $y < 0 || $x >= $width || $y >= $height;
    $image->xy ($x,$height-1-$y,$char);
  };

  my ($n_lo, $n_hi) = $curve->rect_to_n_range($x_lo-2,$y_lo-2, $x_hi+2,$y_hi+2);
  print "n_hi $n_hi\n";
  for my $y ($y_lo .. $y_hi) {
    for my $x ($x_lo .. $x_hi) {
      my $n = $curve->xy_to_n($x,$y) // next;
      my $arm = $n % $arms;

      my ($nx,$ny) = $curve->n_to_xy($n + $arms);
      my $dir = dxdy_to_dir6($nx-$x,$ny-$y);
      $plot->($x, $y, $dir);
    }
  }
  $plot->(0,0, '+');
  $image->save('/dev/stdout');

  exit 0;
}

{
  # TerdragonMidpoint xy absolute direction

  require Image::Base::Text;
  require Math::PlanePath::TerdragonMidpoint;
  my $arms = 6;
  my $path = Math::PlanePath::TerdragonMidpoint->new (arms => $arms);

  my $width = 50;
  my $height = 30;
  my $x_lo = -$width/2;
  my $y_lo = -$height/2;

  my $x_hi = $x_lo + $width - 1;
  my $y_hi = $y_lo + $height - 1;
  my $image = Image::Base::Text->new (-width => $width,
                                      -height => $height);

  my $plot = sub {
    my ($x,$y,$char) = @_;
    $x -= $x_lo;
    $y -= $y_lo;
    return if $x < 0 || $y < 0 || $x >= $width || $y >= $height;
    $image->xy ($x,$height-1-$y,$char);
  };

  my ($n_lo, $n_hi) = $path->rect_to_n_range($x_lo-2,$y_lo-2, $x_hi+2,$y_hi+2);
  print "n_hi $n_hi\n";
  for my $n (0 .. $n_hi) {
    my $arm = $n % $arms;

    my ($x,$y) = $path->n_to_xy($n);
    # if (($n % $arms) == 1) {
    #   $x += 1;
    #   $y += 1;
    # }
    next if $x < $x_lo || $y < $y_lo || $x > $x_hi || $y > $y_hi;

    my ($nx,$ny) = $path->n_to_xy($n + $arms);
    # if (($n % $arms) == 1) {
    #   $nx += 1;
    #   $ny += 1;
    # }

    # if ($nx == $x+1) {
    #   $image->xy($x,$y,$n&3);
    # }
    # if ($ny == $y+1) {
    #   $image->xy($x,$y,$n&3);
    # }
    # if ($ny == $y) {
    # }

    my $show;
    my $dir = dxdy_to_dir6($nx-$x,$ny-$y);
    my $digit = (($x + 3*$y) + 0) % 3;
    my $d9 = ((2*$x + $y) + 0) % 9;
    my $c = ($x+$y)/2;
    my $flow = sprintf "%X", ($x + 3*$y) % 12;

    my $prev_dir = -1;
    if ($n >= $arms) {
      my ($px,$py) = $path->n_to_xy($n - $arms);
      $prev_dir = dxdy_to_dir6($x-$px,$y-$py);
    }

    foreach my $r (0,1,2) {
      $flow = ($r == 0 ? '-'
               : $r == 1 ? '/'
               : '\\');
      if ($arm & 1) {
        if (($digit == 0 || $digit == 1)
            && (($dir%3) == $r)) {
          $show = $flow;
        }
        if (($digit == 2)
            && (($prev_dir%3) == $r)) {
          $show = $flow;
        }
      } else {
        if (($digit == 0 || $digit == 2)
            && (($dir%3) == $r)) {
          $show = $flow;
        }
        if (($digit == 1)
            && (($prev_dir%3) == $r)) {
          $show = $flow;
        }
      }
    }
    if (! defined $show) {
      $show = '.';
    }


    # if ($digit == 1) {
    #   if ($dir == 0 || $dir == 3) {
    #     $show = $dir;
    #     $show = 'x';
    #   }
    # }
    # if ($digit == 2) {
    #   if ($dir == 0 || $dir == 3) {
    #     $show = $prev_dir;
    #     $show = 'x';
    #   }
    # }
    # if ($digit == 0) {
    #   $show = 'x';
    # }

    my $mod = (int($n/$arms) % 3);

    # if (($arm == 0 && $mod == 0)
    #     || ($arm == 1 && $mod == 2)
    #     || ($arm == 2 && $mod == 0)
    #     || ($arm == 3 && $mod == 2)
    #     || ($arm == 4 && $mod == 0)
    #     || ($arm == 5 && $mod == 2)) {
    #   # $show = '0';
    #   # $show = $digit;
    #   if ($n < 3*$arms) {
    #     print "n=$n $x,$y  mod=$mod\n";
    #   }
    # }
    # if (($arm == 0 && $mod == 1)
    #     || ($arm == 1 && $mod == 1)
    #     || ($arm == 2 && $mod == 1)
    #     || ($arm == 3 && $mod == 1)
    #     || ($arm == 4 && $mod == 1)
    #     || ($arm == 5 && $mod == 1)) {
    #   # $show = '1';
    # }
    # if (($arm == 0 && $mod == 2)
    #     || ($arm == 1 && $mod == 0)
    #     || ($arm == 2 && $mod == 2)
    #     || ($arm == 3 && $mod == 0)
    #     || ($arm == 4 && $mod == 2)
    #     || ($arm == 5 && $mod == 0)) {
    #   #      $show = '2';
    # }

    if (defined $show) {
      $plot->($x, $y, $show);
    }
    # if ($dir == 0) {
    #   $image->xy($x-$x_lo,$y-$y_lo, $dir);
    # }
  }
#  $plot->(0,0, '+');
  $image->save('/dev/stdout');

  exit 0;
}

{
  require Math::PlanePath::TerdragonMidpoint;
  my $path = Math::PlanePath::TerdragonMidpoint->new;
  $path->xy_to_n(5,3);
  exit 0;
}

{
  # TerdragonMidpoint modulo

  require Math::PlanePath::TerdragonMidpoint;
  my $arms = 2;
  my $path = Math::PlanePath::TerdragonMidpoint->new (arms => $arms);

  for my $n (0 .. 3**4) {
    my $arm = $n % $arms;
    my $mod = (int($n/$arms) % 3);

    my ($x,$y) = $path->n_to_xy($n);
    my $digit = (($x + 3*$y) + 0) % 3;
    print "n=$n $x,$y  mod=$mod  k=$digit\n";
  }
  exit 0;
}

{
  # cumulative turn +/- 1 list
  require Math::BaseCnv;
  my $path = Math::PlanePath::TerdragonCurve->new;
  my $cumulative = 0;
  for (my $n = $path->n_start + 1; $n < 35; $n++) {
    my $n3 = Math::BaseCnv::cnv($n,10,3);
    my $turn = calc_n_turn ($n);
    #    my $turn = path_n_turn($path, $n);
    if ($turn == 2) { $turn = -1 }
    $cumulative += $turn;
    printf "%3s  %4s  %d\n", $n, $n3, $cumulative;
  }
  print "\n";
  exit 0;
}

{
  # cumulative turn +/- 1
  my $path = Math::PlanePath::TerdragonCurve->new;
  my $cumulative = 0;
  my $max = 0;
  my $min = 0;
  for (my $n = $path->n_start + 1; $n < 35; $n++) {
    my $turn = calc_n_turn ($n);
    #    my $turn = path_n_turn($path, $n);
    if ($turn == 2) { $turn = -1 }
    $cumulative += $turn;
    $max = max($cumulative,$max);
    $min = min($cumulative,$min);
        print "$cumulative,";
  }
  print "\n";
  print "min $min  max $max\n";
  exit 0;

  sub calc_n_turn {
    my ($n) = @_;

    die if $n == 0;
    while (($n % 3) == 0) {
      $n = int($n/3); # skip low 0s
    }
    return ($n % 3);  # next digit is the turn
  }
}

{
  # turn
  my $path = Math::PlanePath::TerdragonCurve->new;

  my $n = $path->n_start;
  # my ($n0_x, $n0_y) = $path->n_to_xy ($n);
  # $n++;
  # my ($prev_x, $prev_y) = $path->n_to_xy ($n);
  # my ($prev_dx, $prev_dy) = ($prev_x - $n0_x, $prev_y - $n0_y);
  # my $prev_dir = dxdy_to_dir ($prev_dx, $prev_dy);
  $n++;

  my $pow = 3;
  for ( ; $n < 128; $n++) {
    # my ($x, $y) = $path->n_to_xy ($n);
    # my $dx = $x - $prev_x;
    # my $dy = $y - $prev_y;
    # my $dir = dxdy_to_dir ($dx, $dy);
    # my $turn = ($dir - $prev_dir) % 3;
    #
    # $prev_dir = $dir;
    # ($prev_x,$prev_y) = ($x,$y);

    my $turn = path_n_turn($path, $n);

    my $azeros = digit_above_low_zeros($n);
    my $azx = ($azeros == $turn ? '' : '*');

    # my $aones = digit_above_low_ones($n-1);
    # if ($aones==0) { $aones=1 }
    # elsif ($aones==1) { $aones=0 }
    # elsif ($aones==2) { $aones=2 }
    # my $aox = ($aones == $turn ? '' : '*');
    #
    # my $atwos = digit_above_low_twos($n-2);
    # if ($atwos==0) { $atwos=1 }
    # elsif ($atwos==1) { $atwos=2 }
    # elsif ($atwos==2) { $atwos=0 }
    # my $atx = ($atwos == $turn ? '' : '*');
    #
    # my $lzero = digit_above_low_zeros($n);
    # my $lone = digit_above_lowest_one($n);
    # my $ltwo = digit_above_lowest_two($n);
    # print "$n  $turn   ones $aones$aox   twos $atwos$atx  zeros $azeros${azx}[$lzero]    $lone $ltwo\n";

    print "$n  $turn   zeros got=$azeros ${azx}\n";
  }
  print "\n";
  exit 0;

  sub digit_above_low_zeros {
    my ($n) = @_;
    if ($n == 0) {
      return 0;
    }
    while (($n % 3) == 0) {
      $n = int($n/3);
    }
    return ($n % 3);
  }

  sub path_n_turn {
    my ($path, $n) = @_;
    my $prev_dir = path_n_dir ($path, $n-1);
    my $dir = path_n_dir ($path, $n);
    return ($dir - $prev_dir) % 3;
  }
  sub path_n_dir {
    my ($path, $n) = @_;
    my ($prev_x, $prev_y) = $path->n_to_xy ($n);
    my ($x, $y) = $path->n_to_xy ($n+1);
    return dxdy_to_dir($x - $prev_x, $y - $prev_y);
  }
}

{
  # min/max for level
  require Math::BaseCnv;
  my $path = Math::PlanePath::TerdragonCurve->new;
  my $prev_min = 1;
  my $prev_max = 1;
  for (my $level = 1; $level < 25; $level++) {
    my $n_start = 3**($level-1);
    my $n_end = 3**$level;

    my $min_hypot = 128*$n_end*$n_end;
    my $min_x = 0;
    my $min_y = 0;
    my $min_pos = '';

    my $max_hypot = 0;
    my $max_x = 0;
    my $max_y = 0;
    my $max_pos = '';

    print "level $level  n=$n_start .. $n_end\n";

    foreach my $n ($n_start .. $n_end) {
      my ($x,$y) = $path->n_to_xy($n);
      my $h = $x*$x + 3*$y*$y;

      if ($h < $min_hypot) {
        $min_hypot = $h;
        $min_pos = "$x,$y";
      }
      if ($h > $max_hypot) {
        $max_hypot = $h;
        $max_pos = "$x,$y";
      }
    }
    # print "  min $min_hypot   at $min_x,$min_y\n";
    # print "  max $max_hypot   at $max_x,$max_y\n";
    {
      my $factor = $min_hypot / $prev_min;
      my $min_hypot3 = Math::BaseCnv::cnv($min_hypot,10,3);
      print "  min h= $min_hypot  [$min_hypot3]   at $min_pos  factor $factor\n";
      my $calc = (4/3/3) * 2.9**$level;
      print "    cf $calc\n";
    }
    # {
    #   my $factor = $max_hypot / $prev_max;
    # my $max_hypot3 = Math::BaseCnv::cnv($max_hypot,10,3);
    #   print "  max h= $max_hypot  [$max_hypot3]  at $max_pos  factor $factor\n";
    #   # my $calc = 4 * 3**($level*.9) * 4**($level*.1);
    #   # print "    cf $calc\n";
    # }
    $prev_min = $min_hypot;
    $prev_max = $max_hypot;
  }
  exit 0;
}




{
  # turn
  my $path = Math::PlanePath::TerdragonCurve->new;

  my $n = $path->n_start;
  my ($n0_x, $n0_y) = $path->n_to_xy ($n);
  $n++;
  my ($prev_x, $prev_y) = $path->n_to_xy ($n);
  my ($prev_dx, $prev_dy) = ($prev_x - $n0_x, $prev_y - $n0_y);
  my $prev_dir = dxdy_to_dir ($prev_dx, $prev_dy);
  $n++;

  my $pow = 3;
  for ( ; $n < 128; $n++) {
    my ($x, $y) = $path->n_to_xy ($n);
    my $dx = ($x - $prev_x);
    my $dy = ($y - $prev_y);
    my $dir = dxdy_to_dir ($dx, $dy);
    my $turn = ($dir - $prev_dir) % 3;

    $prev_dir = $dir;
    ($prev_x,$prev_y) = ($x,$y);

    print "$turn";
    if ($n-1 == $pow) {
      $pow *= 3;
      print "\n";
    }
  }
  print "\n";
  exit 0;
}

sub path_to_dir6 {
  my ($path,$n) = @_;
  my ($x,$y) = $path->n_to_xy($n);
  my ($nx,$ny) = $path->n_to_xy($n + $path->arms_count);
  return dxdy_to_dir6($nx-$x,$ny-$y);
}
sub dxdy_to_dir6 {
  my ($dx,$dy) = @_;
  if ($dy == 0) {
    if ($dx == 2) { return 0; }
    if ($dx == -2) { return 3; }
  }
  if ($dy == 1) {
    if ($dx == 1) { return 1; }
    if ($dx == -1) { return 2; }
  }
  if ($dy == -1) {
    if ($dx == 1) { return 5; }
    if ($dx == -1) { return 4; }
  }
  die "unrecognised $dx,$dy";
}

# per KochCurve.t
sub dxdy_to_dir3 {
  my ($dx,$dy) = @_;
  if ($dy == 0) {
    if ($dx == 2) { return 0/2; }
    # if ($dx == -2) { return 3; }
  }
  if ($dy == 1) {
    # if ($dx == 1) { return 1; }
    if ($dx == -1) { return 2/2; }
  }
  if ($dy == -1) {
    # if ($dx == 1) { return 5; }
    if ($dx == -1) { return 4/2; }
  }
  die "unrecognised $dx,$dy";
}

sub digit_above_low_ones {
  my ($n) = @_;
  if ($n == 0) {
    return 0;
  }
  while (($n % 3) == 1) {
    $n = int($n/3);
  }
  return ($n % 3);
}
sub digit_above_low_twos {
  my ($n) = @_;
  if ($n == 0) {
    return 0;
  }
  while (($n % 3) == 2) {
    $n = int($n/3);
  }
  return ($n % 3);
}

sub digit_above_lowest_zero {
  my ($n) = @_;
  for (;;) {
    if (($n % 3) == 0) {
      last;
    }
    $n = int($n/3);
  }
  $n = int($n/3);
  return ($n % 3);
}
sub digit_above_lowest_one {
  my ($n) = @_;
  for (;;) {
    if (! $n || ($n % 3) != 0) {
      last;
    }
    $n = int($n/3);
  }
  $n = int($n/3);
  return ($n % 3);
}
sub digit_above_lowest_two {
  my ($n) = @_;
  for (;;) {
    if (! $n || ($n % 3) != 0) {
      last;
    }
    $n = int($n/3);
  }
  $n = int($n/3);
  return ($n % 3);
}


#------------------------------------------------------------------------------
# Old xy_to_n_list based on TerdragonMidpoint::xy_to_n


# maximum extent -- no, not quite right
#
#          .----*
#           \
#       *----.
#
# Two triangle heights, so
#     rnext = 2 * r * sqrt(3)/2
#           = r * sqrt(3)
#     rsquared_next = 3 * rsquared
# Initial X=2,Y=0 is rsquared=4
# then X=3,Y=1 is 3*3+3*1*1 = 9+3 = 12 = 4*3
# then X=3,Y=3 is 3*3+3*3*3 = 9+3 = 36 = 4*3^2
#
# my @try_dx = (2, 1, -1, -2, -1,  1);
# my @try_dy = (0, 1,  1, 0,  -1, -1);
#
  # my $xm = 2*$x;  # doubled out
  # my $ym = 2*$y;
  # foreach my $i (0 .. $#try_dx) {
  #   my $t = $self->Math::PlanePath::TerdragonMidpoint::xy_to_n
  #     ($xm+$try_dx[$i], $ym+$try_dy[$i]);
  # 
  #   ### try: ($xm+$try_dx[$i]).",".($ym+$try_dy[$i])
  #   ### $t
  # 
  #   next unless defined $t;
  # 
  #   # function call here to get our n_to_xy(), not the overridden method
  #   # when in TerdragonRounded or other subclass
  #   my ($tx,$ty) = n_to_xy($self,$t)
  #     or next;
  # 
  #   if ($tx == $x && $ty == $y) {
  #     ### found: $t
  #     if (@n_list && $t < $n_list[0]) {
  #       unshift @n_list, $t;
  #     } elsif (@n_list && $t < $n_list[-1]) {
  #       splice @n_list, -1,0, $t;
  #     } else {
  #       push @n_list, $t;
  #     }
  #     if (@n_list == 3) {
  #       return @n_list;
  #     }
  #   }
  # }
  # return @n_list;
