Math-PlanePath

 view release on metacpan or  search on metacpan

devel/c-curve.pl  view on Meta::CPAN

  sub points_str {
    my ($points) = @_;
    ### points_str(): $points
    my $count = scalar(@$points);
    return  "count=$count  ".join(' ',map{join(',',@$_)}@$points)
  }
}

{
  my @sdir = (2,2,0,-2, -2,-2,0,2);
  sub s0_by_formula {
    my ($k) = @_;
    {
      my $h = int($k/2);
      return 2**$k/4 + $sdir[$k%8]*2**$h/4;
    }
    {
      # (1/4)*(2^k +   (1-I)^k +   (1+I)^k))
      require Math::Complex;
      return (2**$k / 2
              + (Math::Complex->new(1,-1)**$k * Math::Complex->new(1,-1)
                 + Math::Complex->new(1,1) **$k * Math::Complex->new(1,1)) / 4);
    }
  }
  my @s1dir = (0,2,2,2, 0,-2,-2,-2);
  sub s1_by_formula {
    my ($k) = @_;
    my $h = int($k/2);
    return 2**$k/4 + $sdir[($k-2)%8]*2**$h/4;
  }
  my @s2dir = (0,2,2,2, 0,-2,-2,-2);
  sub s2_by_formula {
    my ($k) = @_;
    my $h = int($k/2);
    return 2**$k/4 + $sdir[($k-4)%8]*2**$h/4;
  }
  sub s3_by_formula {
    my ($k) = @_;
    my $h = int($k/2);
    return 2**$k/4 + $sdir[($k-6)%8]*2**$h/4;
  }
#  print "  1,  1,  1,  1,  2,  6, 16, 36, 72,136,256,496,992,2016,4096,8256,16512,32896,65536,\n";  # M0
#  print "  0,  1,  2,  3,  4,  6, 12, 28, 64,136,272,528,1024,2016,4032,8128,16384,32896,65792,\n"; # M1
#  print "  0,  0,  1,  3,  6, 10, 16, 28, 56,120,256,528,1056,2080,4096,8128,16256,32640,65536,\n"; # M2
  print "  0,  0,  0,  1,  4, 10, 20, 36, 64,120,240,496,1024,2080,4160,8256,16384,32640,65280,\n"; # M3
  foreach my $k (0 .. 17) {
    printf "%3d,", s3_by_formula($k);
  }
  exit 0;
}







devel/c-curve.pl  view on Meta::CPAN

    my $want = $want[$k];
    my $got = $L->($k);
    my $diff = $want - $got;
     print "$k  $want  $got   $diff\n";
    # print "$got,";
  }
  exit 0;
}

{
  # right boundary formula vs recurrence
  # R[k] = 2*R[k-1] + R[k-2] - 4*R[k-3] + 2*R[k-4]
  #
  # R[2k]   = 4*R[2k-2] - 5*R[2k-4] + 2*R[2k-6]
  # R[2k+1] = 4*R[2k-1] - 5*R[2k-3] + 2*R[2k-5]

  my $R;
  $R = sub {
    my ($k) = @_;
    if ($k < 4) { return R_formula($k); }
    return (2*$R->($k-4)
            - 4*$R->($k-3)
            + $R->($k-2)
            + 2*$R->($k-1));
  };
  require Memoize;
  $R = Memoize::memoize($R);

  my $R2;
  $R2 = sub {
    my ($k) = @_;
    if ($k < 3) { return R_formula(2*$k); }
    return (2*$R2->($k-3)
            - 5*$R2->($k-2)
            + 4*$R2->($k-1));
  };
  require Memoize;
  $R2 = Memoize::memoize($R2);

  my $R2P1;
  $R2P1 = sub {
    my ($k) = @_;
    if ($k < 3) { return R_formula(2*$k+1); }
    return (2*$R2P1->($k-3)
            - 5*$R2P1->($k-2)
            + 4*$R2P1->($k-1));
  };
  require Memoize;
  $R2P1 = Memoize::memoize($R2P1);

  foreach my $k (0 .. 50) {
    # my $want = R_formula($k);
    # print "$k  $want  ",$R->($k),"\n";
    my $want = R_formula(2*$k);
    print "$k  $want  ",$R2->($k),"\n";
  }
  exit 0;
}



{
  # right outer boundary with sqrt(2)

  sub S_formula {
    my ($h) = @_;
    return 2**$h;
  };
  sub Z_formula {
    my ($h) = @_;
    return 2*2**$h - 2;
  };
  my $S_cum = sub {   # sum S[0] .. S[h] inclusive
    my ($h) = @_;
    return 2**($h+1) - 1;
  };
  my $Z_cum = sub {   # sum Z[0] .. Z[h] inclusive
    my ($h) = @_;
    return 2*(2**($h+1) - 1) - 2*($h+1);
  };
  my $S_inR = sub {
    my ($k) = @_;
    my ($h, $rem) = Math::PlanePath::_divrem($k,2);
    if ($rem) {
      return 2*$S_cum->($h);
    } else {
      return 2*$S_cum->($h-1) + S_formula($h);
    }
  };
  my $Z_inR = sub {
    my ($k) = @_;
    my ($h, $rem) = Math::PlanePath::_divrem($k,2);
    if ($rem) {
      return 2*$Z_cum->($h-1) + Z_formula($h);
    } else {
      return 2*$Z_cum->($h-1);
    }
  };
  my $R_bySZ = sub {
    my ($k) = @_;
    return $S_inR->($k) + $Z_inR->($k);
  };

  {
    my $total = 0;
    foreach my $h (0 .. 10) {
      $total == $S_cum->($h-1) or die;
      $total += S_formula($h);
    }
  }
  {
    my $total = 0;
    foreach my $h (0 .. 10) {
      $total == $Z_cum->($h-1) or die;
      $total += Z_formula($h);
    }
  }
  # {
  #   print $S_cum->(-1),"\n";
  #   foreach my $h (0 .. 10) {
  #     print "+ ",S_formula($h), " = ",$S_cum->($h),"\n";
  #   }
  # }
  {
    foreach my $k (0 .. 10) {
      my $s = $S_inR->($k);
      my $z = $S_inR->($k);
      my $rby = $R_bySZ->($k);
      my $rformula = R_formula($k);
      # print "$k  $s + $z = $rby   rf=$rformula\n";
      $rby == $rformula or die "$k $rby $rformula";
    }
  }

  {
    foreach my $k (0 .. 100) {
      my $s = $S_inR->($k);
      my $z = $S_inR->($k);
      my $t = sqrt(2)**$k;
      my $f = ($s + $z/sqrt(2)) / $t;
      print "$k  $s + $z    f=$f\n";
    }
    print "2+  2*sqrt(2)=",2 + 2*sqrt(2),"\n";   # k odd
    print "3+3/2*sqrt(2)=",3+1.5*sqrt(2),"\n";   # k even
  }
  exit 0;

}
{
  # right outer boundary

  sub R_formula {
    my ($k) = @_;
    my $h = int($k/2);

    return ($k & 1
            ? 10*2**$h - 2*$k - 6   # yes
            :  7*2**$h - 2*$k - 6); # yes

    if ($k & 1) {
      my $j = ($k+1)/2;
      return 5*2**$j - 4*$j - 4;  # yes

devel/c-curve.pl  view on Meta::CPAN

    return 2*$Scum_recurrence->($k-1) + $k; # yes
  };
  $R_recurrence = sub {
    my ($k) = @_;
    if ($k == 0) { return 1; }
    if ($k == 1) { return 2; }
    return 2*$R_recurrence->($k-2) + 2*$k-2;
  };

  for (my $k = 0; $k < 15; $k++) {
    print R_formula($k),", ";
  }
  print "\n";

  require MyOEIS;
  my $path = Math::PlanePath::CCurve->new;
  foreach my $k (0 .. 17) {
    my $n_end = 2**$k;
    my $p = MyOEIS::path_boundary_length($path, $n_end, side => 'right');
    # my $b = $B->($k);
    my $srec = $Scum_recurrence->($k);
    my $zrec = $Zcum_recurrence->($k);
    my $rszrec = $srec + $zrec + 1;
    my $rrec = $R_recurrence->($k);
    # my $t = $T->($k);
    # my $u = $U->($k);
    # my $u2 = $U2->($k);
    # my $u_lr = $U_from_LsubR->($k);
    # my $v = $V->($k);
    my ($s, $z) = path_S_and_Z($path, $n_end);
    my $r = $s + $z + 1;
    my $rformula = R_formula($k);
    my $drformula = $r - $rformula;
    # next unless $k & 1;
    print "$k $p  $s $z $r   $srec $zrec $rszrec $rrec $rformula  small by=$drformula\n";
  }
  exit 0;

  sub path_S_and_Z {
    my ($path, $n_end) = @_;
    ### path_S_and_Z(): $n_end
    my $s = 0;
    my $z = 0;
    my $x = 1;
    my $y = 0;

devel/chan-tree.pl  view on Meta::CPAN

    $poly *= make_poly_k4($level);
    foreach my $i (0 .. 30) {
      print " ",$poly->coeff($i);
    }
    print "\n";
  }
  exit 0;
}

{
  # children formulas
  foreach my $k (3 .. 8) {
    my $half_ceil = int(($k+1) / 2);
    foreach my $digit (0 .. $k-1) {
      my $c1 = ($digit < $half_ceil ? $digit+1 : $k-$digit);
      my $c0 = ($digit <= $half_ceil ? $digit : $k-$digit+1);
      my $c2 = ($digit < $half_ceil-1 ? $digit+2 : $k-$digit-1);
      print "${c1}x + ${c0}y / ${c2}x + ${c1}y\n";
    }
    print "\n";
  }

devel/complex-minus.pl  view on Meta::CPAN

        $max_pos = "$x,$y";
      }
    }
    # print "$min_hypot,";

    # 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;
      print "  min r^2 $min_hypot 0b".sprintf('%b',$min_hypot)."   at $min_pos  factor $factor\n";
      print "  cf formula ", 2**($level-7), "\n";
    }
    # {
    #   my $factor = $max_hypot / $prev_max;
    #   print "  max r^2 $max_hypot 0b".sprintf('%b',$max_hypot)."   at $max_pos  factor $factor\n";
    # }
    $prev_min = $min_hypot;
    $prev_max = $max_hypot;
  }
  exit 0;
}

devel/complex-revolving.pl  view on Meta::CPAN

    # print "  max $max_hypot   at $max_x,$max_y\n";
    {
      my $factor = $min_hypot / $min[$level-1];
      my $factor4_level = max($level-4,0);
      my $factor4 = $min_hypot / $min[max($factor4_level)];
      # printf "  min r^2 %5d", $min_hypot;
      printf " 0b%-20b", $min_hypot;
      # print "   at $min_pos";
      # print "  factor $factor";
      # print "  factor[$factor4_level] $factor4";
      # print "  cf formula ", 2**($level-7), "\n";
      print "\n";
    }
    # {
    #   my $factor = $max_hypot / $prev_max;
    #   print "  max r^2 $max_hypot 0b".sprintf('%b',$max_hypot)."   at $max_pos  factor $factor\n";
    # }
    $prev_max = $max_hypot;
  }
  exit 0;
}

devel/dragon.pl  view on Meta::CPAN


    return ($path->_UNDOCUMENTED_level_to_right_line_boundary($level+1)
            - $path->_UNDOCUMENTED_level_to_left_line_boundary($level+1)) / 4;

    return ($path->_UNDOCUMENTED_level_to_line_boundary($level) / 2
            - $path->_UNDOCUMENTED_level_to_line_boundary($level+1) / 4);

    return ($path->_UNDOCUMENTED_level_to_enclosed_area($level+1)
            - 2*$path->_UNDOCUMENTED_level_to_enclosed_area($level));
  }
  sub level_to_join_points_by_formula {
    my ($level) = @_;
    {
      if ($level == 0) { return 1; }
      if ($level == 1) { return 1; }
      if ($level == 2) { return 1; }
      if ($level == 3) { return 2; }
      my $j0 =  1;
      my $j1 =  1;
      my $j2 =  1;
      my $j3 =  2;

devel/dragon.pl  view on Meta::CPAN

    # foreach my $n (0 .. $n_end) {
    #   my ($x,$y) = $path->n_to_xy($n);
    #   $seen{"$x,$y"}++;
    # }
    my $u = $path->_UNDOCUMENTED_level_to_u_left_line_boundary($k);
    my $ru = $path->_UNDOCUMENTED_level_to_u_right_line_boundary($k);
    my $bu = $path->_UNDOCUMENTED_level_to_u_line_boundary($k);
    my $ja = level_to_join_area($k);
    my $join_points = path_level_to_join_points($path,$k);
    my $join_area = $join_points - 1;
    my $j = level_to_join_points_by_formula($k);
    my $da = level_to_denclosed($k);
    my $area = $path->_UNDOCUMENTED_level_to_enclosed_area($k);
    my $area_next = $path->_UNDOCUMENTED_level_to_enclosed_area($k+1);
    my $darea = $area_next - $area;
    my $v = $path->_UNDOCUMENTED_level_to_visited($k);
    my $visited = $v; # MyOEIS::path_n_to_visited($path,$n_end);
    my $dvisited = $visited - $prev_visited;

    my $singles = 0 && MyOEIS::path_n_to_singles($path, $n_end-1);
    my $doubles = 0 && MyOEIS::path_n_to_doubles($path, $n_end-1);

devel/dragon.pl  view on Meta::CPAN

  print 3*$xmin/$len+.001," / 3\n";
  print 6*$xmax/$len+.001," / 6\n";
  print 3*$ymin/$len+.001," / 3\n";
  print 3*$ymax/$len+.001," / 3\n";
}




{
  # A073089 midpoint vertical/horizontal formula

  require Math::NumSeq::OEIS::File;
  my $A073089 = Math::NumSeq::OEIS::File->new (anum => 'A073089');

  my $A014577 = Math::NumSeq::OEIS::File->new (anum => 'A014577'); # 0=left n=0
  my $A014707 = Math::NumSeq::OEIS::File->new (anum => 'A014707'); # 1=left
  my $A038189 = Math::NumSeq::OEIS::File->new (anum => 'A038189');
  my $A082410 = Math::NumSeq::OEIS::File->new (anum => 'A082410');

  my $A000035 = Math::NumSeq::OEIS::File->new (anum => 'A000035'); # n mod 2

devel/koch-squareflakes.pl  view on Meta::CPAN

    my $a = $polygon->area;
    my $len = $polygon->perimeter;

    my $a_log = log($a);
    my $len_log = log($len);

    my $d_a_log = $a_log - $prev_a_log;
    my $d_len_log = $len_log - $prev_len_log;
    my $f = $d_a_log / $d_len_log;

    my $formula = area_by_formula($level);

    print "$level  $a $formula\n";
    # print "$level  $d_len_log  $d_a_log   $f\n";
    push @values, $a;

    $prev_a_log = $a_log;
    $prev_len_log = $len_log;
  }
  shift @values;
  require Math::OEIS::Grep;
  Math::OEIS::Grep->search(array => \@values);
  exit 0;

  sub area_by_formula {
    my ($n) = @_;
    return (9**$n - 4**$n)/5;
    # return (4 * (9**$n - 4**$n)/5 + 16**$n);
  }
}
{
  # max extents of a single side

  # horiz: 1, 4, 14, 48, 164, 560, 1912, 6528, 22288, 76096, 259808, 887040
  # A007070 a(n+1) = 4*a(n) - 2*a(n-1), starting 1,4

devel/lib/Math/PlanePath/SquaRecurve.pm  view on Meta::CPAN

# vector(25,n, (-1)^valuation(n,3))
# not in OEIS: 1,1,-1,1,1,-1,1,1,1,1,1,-1,1,1,-1,1,1,1,1,1,-1,1,1,-1,1,1,-1,1
# vector(100,n, valuation(n,3)%2)
# A182581 num ternary low 0s mod 2

=pod

The power of -1 means left or right flip for each low ternary 0 of N, and
flip again if N is odd.  Odd N is an odd number of ternary 1 digits.

This formula follows from the turns in a new low base-9 digit.  The start
and end of the base figure are in the same directions so the turns at 9*N
are unchanged.  Then 9*N+r goes as r in the base figure, but flipped
LE<lt>-E<gt>R when N odd since blocks are mirrored alternately.

    turn(9N)   = turn(N)
    turn(9N+r) = turn(r)*(-1)^N         for  1 <= r <= 8

=cut

# GP-Test  vector(900,n, turn(9*n)) == \

devel/quadric.pl  view on Meta::CPAN

    }
    {
      my $factor = $max_width / $prev_max;
      print "  max width $max_width oct ".sprintf('%o',$max_width)."   at $max_pos  factor $factor\n";
    }
    {
      my $factor = $min_width / ($prev_min||1);
      print "  min width $min_width oct ".sprintf('%o',$min_width)."   at $min_pos  factor $factor\n";
    }
    {
      my $formula = (2*4**($level-1) + 1) / 3;
      print "  cf min formula $formula\n";
    }
    {
      my $formula = (10*4**($level-1) - 1) / 3;
      print "  cf max formula $formula\n";
    }
    $prev_max = $max_width;
    $prev_min = $min_width;
  }
  exit 0;
}

{
  # min/max for level
  require Math::PlanePath::QuadricCurve;

devel/quadric.pl  view on Meta::CPAN

        $min_width = $w;
        $min_pos = "$x,$y n=$n (oct ".sprintf('%o',$n).")";
      }
    }
    # print "  max $max_width   at $max_x,$max_y\n";

    my $factor = $max_width / $prev_max;
    print "  min width $min_width oct ".sprintf('%o',$min_width)."   at $min_pos  factor $factor\n";
    #    print "  max width $max_width oct ".sprintf('%o',$max_width)."   at $max_pos  factor $factor\n";

    # print "  cf formula ",(10*4**($level-1) - 1)/3,"\n";
    # print "  cf formula ",2* (4**($level-0) - 1)/3,"\n";
    print "  cf formula ",2*4**($level-1),"\n";

    $prev_max = $max_width;
  }
  exit 0;
}

devel/r5-dragon.pl  view on Meta::CPAN

  #  [a]   [v0 v1 v2] -1   [v1]
  #  [b] = [v1 v2 v3]    * [v2]
  #  [c]   [v2 v3 v4]      [v3]

  $|=1;
  my @array = (
54,90,150,250,422,714,1206,2042,3462
              );
  # @array = ();
  # foreach my $k (5 .. 10) {
  #   push @array, R_formula(2*$k+1);
  # }
  # require MyOEIS;
  # my $path = Math::PlanePath::R5DragonCurve->new;
  # foreach my $k (0 .. 30) {
  #   my $value = MyOEIS::path_boundary_length($path, 5**$k,
  #                                            # side => 'left',
  #                                           );
  #   last if $value > 10_000;
  #   push @array, $value;
  #   print "$value,";

devel/r5-dragon.pl  view on Meta::CPAN

  foreach my $i (0 .. 10) { print wrong($i),","; }
  print "\n";

  print "recurrence_area815(): ";
  foreach my $i (0 .. 10) { print recurrence_area815($i),","; }
  print "\n";

  print "recurrence_area43():  ";
  foreach my $i (0 .. 10) { print recurrence_area43($i),","; }
  print "\n";
  print "formula_pow():        ";
  foreach my $i (0 .. 10) { print formula_pow($i),","; }
  print "\n";

  print "recurrence_areaSU():  ";
  foreach my $i (0 .. 10) { print recurrence_areaSU($i),","; }
  print "\n";
  print "recurrence_area2S():  ";
  foreach my $i (0 .. 10) { print recurrence_area2S($i),","; }
  print "\n";
  exit 0;

devel/r5-dragon.pl  view on Meta::CPAN


  sub wrong {
    my ($n) = @_;
    if ($n <= 0) { return 0; }
    if ($n == 1) { return 0; }
    return 4*wrong($n-1) + 4*5**($n-2);
  }


  # A[n] = (5^k - 2*3^k + 1)/2
  sub formula_pow {
    my ($n) = @_;
    return (5**$n - 2*3**$n + 1) / 2;
  }

  sub recurrence_area43 {
    my ($n) = @_;
    if ($n <= 0) { return 0; }
    if ($n == 1) { return 0; }
    return 4*recurrence_area43($n-1) - 3*recurrence_area43($n-2) + 4*5**($n-2);
  }

devel/sierpinski-curve.pl  view on Meta::CPAN

    for (my $n = $n_hi; $n >= 0; $n-=2) {
      my ($x,$y) = $path->n_to_xy($n);
      push @points, [$x,$y];
    }
    ### @points
    my $polygon = Math::Geometry::Planar->new;
    $polygon->points(\@points);
    my $area = $polygon->area;
    my $length = $polygon->perimeter;

    my $formula = two_area_by_formula($level);

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

  sub two_area_by_formula {
    my ($k) = @_;

    return (13*4**$k - 7) / 3;

    $k++;
    return (27*4**($k-1) - 18*2**($k-1) -14 * 4**($k-1) + 18 * 2**($k-1) - 4) / 3 - 1;
    return 9*4**($k-1) - 6*2**($k-1) + (-14 * 4**($k-1) + 18 * 2**($k-1) - 4) / 3 - 1;
    return 9*4**($k-1) - 6*2**($k-1) - (14 * 4**($k-1) - 18 * 2**($k-1) + 4) / 3 - 1;
    return 9*2**(2*$k-2) - 2*3*2**($k-1) + 1 - (14 * 4**($k-1) - 18 * 2**($k-1) + 4) / 3 - 2;
    return (3*2**($k-1) - 1)**2 - (14 * 4**($k-1) - 18 * 2**($k-1) + 4) / 3 - 2;
    return (3*2**($k-1) - 1)**2 - 4*(7 * 4**($k-1) - 9 * 2**($k-1) + 2) / 6 - 2;
    return (3*2**($k-1) - 2 + 1)**2 - 4*((7 * 4**($k-1) - 9 * 2**($k-1) + 2) / 6) - 2;
    return (3*2**($k-1) - 2 + 1)**2 - 4*area_by_formula($k-1) - 2;
  }
}

{
  # area under the curve
  #   (7*4^k - 9*2^k + 2)/6
  #
  require Math::Geometry::Planar;
  my $path = Math::PlanePath::SierpinskiCurve->new;
  my @values;

devel/sierpinski-curve.pl  view on Meta::CPAN

    my @points;
    foreach my $n ($n_lo .. $n_hi) {
      my ($x,$y) = $path->n_to_xy($n);
      push @points, [$x,$y];
    }
    my $polygon = Math::Geometry::Planar->new;
    $polygon->points(\@points);
    my $area = $polygon->area;
    my $length = $polygon->perimeter;

    my $formula = area_by_formula($level);

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

  sub area_by_formula {
    my ($k) = @_;
    if ($k == 0) { return 0; }

    return (7 * 4**$k - 9 * 2**$k + 2) / 6;
    return (28 * 4**($k-1) - 18 * 2**($k-1) + 2) / 6;

    {
      return (
              4**($k-1) * 6
              - 4**($k-1) + 1

devel/sierpinski-curve.pl  view on Meta::CPAN

    }

    {
      # A(k) = 4*A(k-1) + Ytop(k) + 1      k >= 2
      # A(1) = 2
      # A(0) = 0
      # Ytop(k) = 3*2^(k-1) - 2
      # A(k) = 4*A(k-1) + 3*2^(k-1) - 1
      #
      if ($k == 1) { return 2; }
      return 4*area_by_formula($k-1) + 3 * 2**($k-1) - 1;
    }

  }
}


{
  # Y coordinate sequence
  require Math::NumSeq::PlanePathCoord;
  my $seq = Math::NumSeq::PlanePathCoord->new (planepath => 'SierpinskiCurve',

devel/theodorus.pl  view on Meta::CPAN


  exit 0;
}

{
  {
    package Math::Symbolic::Custom::MySimplification;
    use base 'Math::Symbolic::Custom::Simplification';

    # use Math::Symbolic::Custom::Pattern;
    # my $formula = Math::Symbolic->parse_from_string("TREE_a * (TREE_b / TREE_c)");
    # my $pattern = Math::Symbolic::Custom::Pattern->new($formula);

    use Math::Symbolic::Custom::Transformation;
    my $trafo = Math::Symbolic::Custom::Transformation::Group->new
      (',',
       'TREE_a * (TREE_b / TREE_c)' => '(TREE_a * TREE_b) / TREE_c',
       'TREE_a * (TREE_b + TREE_c)' => 'TREE_a * TREE_b + TREE_a * TREE_c',
       '(TREE_b + TREE_c) * TREE_a' => 'TREE_b * TREE_a + TREE_c * TREE_a',

       # '(TREE_a / TREE_b) / TREE_c' => 'TREE_a / (TREE_b * TREE_c)',

examples/koch-svg.pl  view on Meta::CPAN

# You should have received a copy of the GNU General Public License along
# with Math-PlanePath.  If not, see <http://www.gnu.org/licenses/>.


# Usage: perl koch-svg.pl >output.svg
#        perl koch-svg.pl LEVEL >output.svg
#
# Print SVG format graphics to standard output for a Koch snowflake curve of
# given LEVEL fineness.  The default level is 4.
#
# The range of N values to plot follows the formulas in the
# Math::PlanePath::KochSnowflakes module POD.
#
# The svg output size is a fixed 300x300, but of course the point of svg is
# that it can be resized by a graphics viewer program.

use 5.006;
use strict;
use warnings;
use List::Util 'min';
use Math::PlanePath::KochSnowflakes;

lib/Math/NumSeq/PlanePathTurn.pm  view on Meta::CPAN

  use constant _NumSeq_Turn_Turn4_max => 3.5;
}
# { package Math::PlanePath::DragonMidpoint;
# }
{ package Math::PlanePath::AlternatePaper;
  use constant _NumSeq_Turn_Turn4_min => 1; # left or right only

  # A209615 is (-1)^e for each p^e prime=4k+3 or prime=2
  # 3*3 mod 4 = 1 mod 4
  # so picks out bit above lowest 1-bit, and factor -1 if an odd power-of-2
  # which is the AlternatePaper turn formula
  #
  use constant _NumSeq_Turn_oeis_anum =>
    { 'arms=1' =>
      { LSR => 'A209615',
        # OEIS-Catalogue: A209615 planepath=AlternatePaper turn_type=LSR
        Right => 'A292077',
        # OEIS-Catalogue: A292077 planepath=AlternatePaper turn_type=Right

        # # Not quite, A106665 has OFFSET=0 cf first here i=1
        # 'Left' => 'A106665', # turn, 1=left,0=right

lib/Math/PlanePath.pm  view on Meta::CPAN

The sqrt(3) factor can be worked into a hypotenuse radial distance
calculation as follows if comparing distances from the origin.

    hypot = sqrt(X*X + 3*Y*Y)

See for instance C<TriangularHypot> which is triangular points ordered by
this radial distance.

=head1 FORMULAS

The formulas section in the POD of each class describes some of the
calculations.  This might be of interest even if the code is not.

=head2 Triangular Calculations

For a triangular lattice, the rotation formulas above allow calculations to
be done in the rectangular X,Y coordinates which are the inputs and outputs
of the PlanePath functions.  Another way is to number vertically on a 60
degree angle with coordinates i,j,

          ...
          *   *   *      2
        *   *   *       1
      *   *   *      j=0
    i=0  1   2

lib/Math/PlanePath.pm  view on Meta::CPAN

    this_dX,this_dY  at int
    next_dX,next_dY  at int+1

    at fractional N
      dX = this_dX * (1-frac) + next_dX * frac
      dY = this_dY * (1-frac) + next_dY * frac

This is combination of this_dX,this_dY and next_dX,next_dY in proportion to
the distances from positions N to int+1 and from int+1 to N+1.

The formulas can be rearranged to

    dX = this_dX + frac*(next_dX - this_dX)
    dY = this_dY + frac*(next_dY - this_dY)

which is like dX,dY at the integer position plus fractional part of a turn
or change to the next dX,dY.

=head2 N to dX,dY -- Self-Similar

For most of the self-similar paths such as C<HilbertCurve>, the change dX,dY

lib/Math/PlanePath/AlternatePaper.pm  view on Meta::CPAN

Return C<(0, 2**$level)>, or for multiple arms return C<(0, $arms *
2**$level + ($arms-1))>.

This is the same as L<Math::PlanePath::DragonCurve/Level Methods>.  Each
level is an unfold (on alternate sides left or right).

=back

=head1 FORMULAS

Various formulas for coordinates, lengths and area can be found in the
author's mathematical write-up

=over

L<http://user42.tuxfamily.org/alternate/index.html>

=back

=head2 Turn

lib/Math/PlanePath/AlternatePaper.pm  view on Meta::CPAN

       = /  GRS(k) if k even
         \  -GRS(k) if k odd

For dY reducing 2k+1 to k drops a 1 bit from the low end.  If the second
lowest bit is also a 1 then they were a "11" bit pair which is lost from
GRS(k).  The factor (-1)^k adjusts for that, being +1 if k even or -1 if k
odd.

=head2 dSum

From the dX and dY formulas above it can be seen that their sum is simply
GRS(N),

    dSum = dX + dY = GRS(N)

The sum X+Y is a numbering of anti-diagonal lines,

   | \ \ \
   |\ \ \ \
   | \ \ \ \
   |\ \ \ \ \

lib/Math/PlanePath/AlternateTerdragon.pm  view on Meta::CPAN

There are 3^level segments in a curve level, so 3^level+1 points numbered
from 0.  For multiple arms there are arms*(3^level+1) points, numbered from
0 so n_hi = arms*(3^level+1)-1.

=back

=head1 FORMULAS

=cut

# Various formulas for coordinates, boundary, area and more can be found in
# the author's mathematical write-up
#
# =over
#
# L<http://user42.tuxfamily.org/terdragon/index.html>
#
# =back
#
# =head2 N to X,Y
#

lib/Math/PlanePath/ArchimedeanChords.pm  view on Meta::CPAN

# is an upper bound, though a fairly slack one
#
#
# cf. arc length along the spiral r=a*theta with a=1/2pi
#     arclength = (1/2) * a * (theta*sqrt(1+theta^2) + asinh(theta))
#               = (1/4*pi) * (theta*sqrt(1+theta^2) + asinh(theta))
# and theta = 2*pi*r
#               = (1/4*pi) * (4*pi^2*r^2 * sqrt(1+1/theta^2) + asinh(theta))
#               = pi * (r^2 * sqrt(1+1/r^2) + asinh(theta)/(4*pi^2))
#
# and to compare to the circles formula
#
#               = pi * (r*(r+1) * r/(r+1) * sqrt(1+1/r^2)
#                       + asinh(theta)/(4*pi^2))
#
# so it's smaller hence better upper bound.  Only a little smaller than the
# squaring once get to 100 loops or so.
#
#
# not exact
sub rect_to_n_range {

lib/Math/PlanePath/ArchimedeanChords.pm  view on Meta::CPAN

    total = sum         ------------
             k=0        arcsin(1/2k)

and this is less than the chords along the spiral.  Is there a good
polynomial over-estimate of arcsin, to become an under-estimate total,
without giving away so much?

=head2 Rectangle to N Range

For the C<rect_to_n_range()> upper bound, the current code takes the arc
length along with spiral with the usual formula

    arc = 1/4pi * (theta*sqrt(1+theta^2) + asinh(theta))

Written in terms of the r radius (theta = 2pi*r) as calculated from the
biggest of the rectangle x,y corners,

    arc = pi*r^2*sqrt(1+1/r^2) + asinh(2pi*r)/4pi

The arc length is longer than chords, so N=ceil(arc) is an upper bound for
the N range.

lib/Math/PlanePath/AztecDiamondRings.pm  view on Meta::CPAN

    Y<0  X>=0     d=X-Y  N=(2d+2)*d+1 + Y
    Y<0  X<0      d=X+Y  N=(2d+4)*d+2 - Y

For example

    Y=2 X=3       d=2+3=5      N=(2*5+2)*5+1  + 2  = 63
    Y=2 X=-1      d=2-(-1)=3   N=2*3*3        - 2  = 16
    Y=-1 X=4      d=4-(-1)=5   N=(2*5+2)*5+1  + -1 = 60
    Y=-2 X=-3     d=-3+(-2)=-5 N=(2*-5+4)*-5+2 - (-2) = 34

The two XE<gt>=0 cases are the same N formula and can be combined with an
abs,

    X>=0          d=X+abs(Y)   N=(2d+2)*d+1 + Y

This works because at Y=0 the last line of one ring joins up to the start of
the next.  For example N=11 to N=15,

    15             2
      \
       14          1

lib/Math/PlanePath/BetaOmega.pm  view on Meta::CPAN

    -----     --------------       -------------
      0         0                    0
      1         0          0         1 =       1
      2        -2 =      -10         1 =      01
      3        -2 =     -010         5 =     101
      4       -10 =    -1010         5 =    0101
      5       -10 =   -01010        21 =   10101
      6       -42 =  -101010        21 =  010101
      7       -42 = -0101010        85 = 1010101

The power of 4 divided by 3 formulas above for Ymin/Ymax have the effect of
producing alternating bit patterns like this.

For odd levels -Ymin/height approaches 1/3 and Ymax/height approaches 2/3,
ie. the start point is about 1/3 up the total extent.  For even levels it's
the other way around, with -Ymin/height approaching 2/3 and Ymax/height
approaching 1/3.

=head2 Closed Curve

Wierum's idea for the curve is a closed square made from four betas,

lib/Math/PlanePath/CCurve.pm  view on Meta::CPAN

    if ($level == 2) { return (6,0); }
    return (2, ($level == 0 ? 0 : 1));
  }

  my ($h, $rem) = _divrem($level, 2);
  my $pow = 2**($h-1);

  if ($rem) {
    return (6*$pow, 7*$pow-4);

    # return (2*S_formula($h) + 2*S_formula($h-1),
    #         Z_formula($h)/2 + Z_formula($h-1)
    #         + Z_formula($h)/2 + (S_formula($h)-S_formula($h-1)) );

  } else {
    return (7*$pow, 3*$pow-4);

    # return (S_formula($h) + 2*S_formula($h-1) + S_formula($h)+(Z_formula($h-1)-Z_formula($h-2)),
    #         (Z_formula($h-1) + Z_formula($h-2)));
  }
}

#------------------------------------------------------------------------------
{
  my @_UNDOCUMENTED_level_to_hull_area = (0, 1/2, 2);

  sub _UNDOCUMENTED_level_to_hull_area {
    my ($self, $level) = @_;

    if ($level < 3) {
      if ($level < 0) { return undef; }
      return $_UNDOCUMENTED_level_to_hull_area[$level];
    }
    my ($h, $rem) = _divrem($level, 2);
    return 35*2**($level-4) - ($rem ? 13 : 10)*2**($h-1) + 2;

    #   if ($rem) {
    #     return 35*2**($level-4) - 13*$pow + 2;
    #
    #     my $width = S_formula($h) + Z_formula($h)/2 + Z_formula($h-1)/2;
    #     my $ul = Z_formula($h-1)/2;
    #     my $ur = Z_formula($h)/2;
    #     my $bl = $width - Z_formula($h-1)/2 - S_formula($h-1);
    #     my $br = Z_formula($h-1)/2;
    #     return $width**2 - $ul**2/2 - $ur**2/2 - $bl**2/2 - $br**2/2;
    #
    #   } else {
    #     return 35*2**($level-4) - 10*$pow + 2;
    #     return 0;
    #     return 35*2**($level-4) - 5*2**$h + 2;
    #
    #     # my $width = S_formula($h) + Z_formula($h-1);
    #     # my $upper = Z_formula($h-1)/2;
    #     # my $lower = Z_formula($h-2)/2;
    #     # my $height = S_formula($h-1) + $upper + $lower;
    #     # return $width; # * $height - $upper*$upper - $lower*$lower;
    #   }
    # }
  }
}

#------------------------------------------------------------------------------
# levels

sub level_to_n_range {

lib/Math/PlanePath/CCurve.pm  view on Meta::CPAN

       |                  |         |  1
    *--*                  *--*      |         total
    |                        |      |         height
    *   N=4^k         N=0    *    <-+         -> 1+1/4
    |     |            |     |      |  1/4
    *--*--*            *--*--*    <-+

    ^-----^            ^-----^
      1/2        1       1/2     total width -> 2

The extent formulas can be found by considering the self-similar blocks.
The initial k=0 is a single line segment and all its extents are 0.

                          h[0] = 0
          N=1 ----- N=0
                          l[0] = 0
                    w[0] = 0

Thereafter the replication overlap as

       +-------+---+-------+

lib/Math/PlanePath/CCurve.pm  view on Meta::CPAN

=over

=item C<($n_lo, $n_hi) = $path-E<gt>level_to_n_range($level)>

Return C<(0, 2**$level)>.

=back

=head1 FORMULAS

Some formulas and results can also be found in the author's mathematical
write-up

=over

L<http://user42.tuxfamily.org/c-curve/index.html>

=back

=head2 Direction

lib/Math/PlanePath/CCurve.pm  view on Meta::CPAN

described in L<Math::PlanePath/N to dX,dY -- Fractional>.

    # apply turn to make direction at Nint+1
    turn = count_low_1_bits(N) - 1      # N integer part
    dir = (dir - turn) mod 4            # direction at N+1

    # adjust dx,dy by fractional amount in this direction
    dx += Nfrac * (dir_to_dx[dir] - dx)
    dy += Nfrac * (dir_to_dy[dir] - dy)

A small optimization can be made by working the "-1" of the turn formula
into a +90 degree rotation of the C<dir_to_dx[]> and C<dir_to_dy[]> parts by
swap and sign change,

    turn_plus_1 = count_low_1_bits(N)     # on N integer part
    dir = (dir - turn_plus_1) mod 4       # direction-1 at N+1

    # adjustment including extra +90 degrees on dir
    dx -= $n*(dir_to_dy[dir] + dx)
    dy += $n*(dir_to_dx[dir] - dy)

=head2 X,Y to N

The N values at a given X,Y can be found by taking terms low to high from
the complex number formula (the same as given above)

    X+iY = b^k            N = 2^k + 2^(k1) + 2^(k2) + ... in binary
         + b^k1 * i       base b=1+i
         + b^k2 * i^2
         + ...

If the lowest term is b^0 then X+iY has X+Y odd.  If the lowest term is not
b^0 but instead some power b^n then X+iY has X+Y even.  This is because a
multiple of b=1+i,

lib/Math/PlanePath/CCurve.pm  view on Meta::CPAN

             |
    -1,0 -- 0,0 -- 1,0
             |
            0,-1

It's not possible to wait for X=0,Y=0 to be reached because some dX,dY
directions will step infinitely among the four non-zeros.  Only the case
X=dX,Y=dY is sure to reach 0,0.

The successive p decrements which rotate dX,dY by -90 degrees must end at p
== 0 mod 4 for highest term in the X+iY formula having i^0=1.  This means
must end dX=1,dY=0 East.  If this doesn't happen then there is no N for that
p direction.

The number of 1-bits in N is == p mod 4.  So the order the N values are
obtained follows the order the p directions are attempted.  In general the N
values will not be smallest to biggest N so a little sort is necessary if
that's desired.

It can be seen that sum X+Y is used for the bit calculation and then again
in the divide by 1+i.  It's convenient to write the whole loop in terms of

lib/Math/PlanePath/CCurve.pm  view on Meta::CPAN


The characteristic polynomial  of these recurrences is

    x^3 - 4x^2 + 6x - 4
    = (x-2) * (x - (1-i)) * (x - (1+i))

=for GP-Test  x^3 - 4*x^2 + 6*x - 4 == (x-2)*(x^2 - 2*x + 2)

=for GP-Test  x^3 - 4*x^2 + 6*x - 4 == (x-2) * (x + (I-1)) * (x - (I+1))

So explicit formulas can be written in powers of the roots 2, 1-i and 1+i,

    M0[k] = ( 2^k +   (1-i)^k +   (1+i)^k )/4      for k>=1
    M1[k] = ( 2^k + i*(1-i)^k - i*(1+i)^k )/4
    M2[k] = ( 2^k -   (1-i)^k -   (1+i)^k )/4
    M3[k] = ( 2^k - i*(1-i)^k + i*(1+i)^k )/4

=cut

# GP-DEFINE  M0pow(k) = if(k==0,1, (1/4)*(2^k +   (1-I)^k +   (1+I)^k))
# GP-DEFINE  M1pow(k) = if(k==0,0, (1/4)*(2^k + I*(1-I)^k - I*(1+I)^k))

lib/Math/PlanePath/CellularRule190.pm  view on Meta::CPAN

       14 15 16    17 18 19    20             4 
           8  9 10    11 12 13                3 
              4  5  6     7                   2 
                 1  2  3                      1 
                    0                     <- Y=0

    -5 -4 -3 -2 -1 X=0 1  2  3  4  5

The effect is to push each N rightwards by 1, and wrapping around.  So the
N=0,1,4,8,14,etc on the left were on the right of the default n_start=1.
This also has the effect of removing the +1 in the Nright formula given
above, so

    Nleft = triangular(Y) + quartersquare(Y)

=head1 FUNCTIONS

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

=over 4

lib/Math/PlanePath/ChanTree.pm  view on Meta::CPAN

      6 |                     4       7
      5 |                 3       8         N=6 is the 7/7 middle term
      4 |             2       9
      3 |         1      10
      2 |     0      11
      1 |        12
    Y=0 |
        +------------------------------
        X=0   1   2   3   4   5   6   7

Each node has k children.  The formulas for the children can be seen from
sample cases k=5 and k=6.  A node X/Y descends to

    k=5                     k=6

    1X+0Y / 2X+1Y           1X+0Y / 2X+1Y
    2X+1Y / 3X+2Y           2X+1Y / 3X+2Y
    3X+2Y / 2X+3Y           3X+2Y / 3X+3Y
    2X+3Y / 1X+2Y           3X+3Y / 2X+3Y
    1X+2Y / 0X+1Y           2X+3Y / 1X+2Y
                            1X+2Y / 0X+1Y

lib/Math/PlanePath/ChanTree.pm  view on Meta::CPAN


    parent = floor(N/k)       when n_start=1

For other C<n_start> adjust before and after to an C<n_start=1> basis,

    parent = floor((N-(Nstart-1)) / k) + Nstart-1

For example in the default k=0 Nstart=1 the parent of N=3 is
floor((3-(1-1))/3)=1.

The post-adjustment can be worked into the formula with (k-1)*(Nstart-1)
similar to the children above,

    parent = floor((N + (k-1)*(Nstart-1)) / k)

But the first style is more convenient to compare to see that N is past the
top nodes and therefore has a parent.

    N-(Nstart-1) >= k      to check N is past top-nodes

=head2 N Root

lib/Math/PlanePath/ComplexMinus.pm  view on Meta::CPAN


=item C<($n_lo, $n_hi) = $path-E<gt>level_to_n_range($level)>

Return C<(0, 2**$level - 1)>, or with C<realpart> option return C<(0,
$norm**$level - 1)> where norm=realpart^2+1.

=back

=head1 FORMULAS

Various formulas and pictures etc for the i-1 case can be found in the
author's long mathematical write-up (section "Complex Base i-1")

=over

L<http://user42.tuxfamily.org/dragon/index.html>

=back

=head2 X,Y to N

A given X,Y representing X+Yi can be turned into digits of N by successive
complex divisions by i-r.  Each digit of N is a real remainder 0 to r*r
inclusive from that division.

The base formula above is

    X+Yi = a[n]*b^n + ... + a[2]*b^2 + a[1]*b + a[0]

and want the a[0]=digit to be a real 0 to r*r inclusive.  Subtracting a[0]
and dividing by b will give

    (X+Yi - digit) / (i-r)
    = - (X-digit + Y*i) * (i+r) / norm
    = (Y - (X-digit)*r)/norm
      + i * - ((X-digit) + Y*r)/norm

lib/Math/PlanePath/ComplexMinus.pm  view on Meta::CPAN

first norm^k many points, ie. N=0 to N=norm^k-1 inclusive, is calculated in

=over

William J. Gilbert, "The Fractal Dimension of Sets Derived From Complex
Bases", Canadian Mathematical Bulletin, volume 29, number 4, 1986.
L<http://www.math.uwaterloo.ca/~wgilbert/Research/GilbertFracDim.pdf>

=back

The boundary formula is a 3rd-order recurrence.  For the twindragon case it
is

    for realpart=1
    boundary[k] = boundary[k-1] + 2*boundary[k-3]
    = 4, 6, 10, 18, 30, 50, 86, 146, 246, 418, ...  (2*A003476)

                           4 + 2*x + 4*x^2
    generating function    ---------------
                            1 - x - 2*x^3

lib/Math/PlanePath/ComplexMinus.pm  view on Meta::CPAN

    C -> B

    starting from
      A = 2*r
      B = 2
      C = 2 - 2*r

    total boundary = A+B+C

For i-1 realpart=1 these A,B,C are already in the form of a recurrence
A-E<gt>A+2*C, B-E<gt>A, C-E<gt>B, per the formula above.  For other real
parts a little matrix rearrangement turns the A,B,C parts into recurrence

    boundary[k] = boundary[k-1] * (2*r - 1)   
                + boundary[k-2] * (norm - 2*r)
                + boundary[k-3] * norm               

    starting from
      boundary[0] = 4               # single square cell
      boundary[1] = 2*norm + 2      # oblong of norm many cells
      boundary[2] = 2*(norm-1)*(r+2) + 4

lib/Math/PlanePath/ComplexPlus.pm  view on Meta::CPAN

=item C<($n_lo, $n_hi) = $path-E<gt>level_to_n_range($level)>

Return C<(0, 2**$level - 1)>, or for 2 arms return C<(0, 2 * 2**$level -
1)>.  With the C<realpart> option return C<(0, $norm**$level - 1)> where
norm=realpart^2+1.

=back

=head1 FORMULAS

Various formulas and pictures etc for the i+1 case can be found in the
author's long mathematical write-up (section "Complex Base i+1")

=over

L<http://user42.tuxfamily.org/dragon/index.html>

=back

=head1 OEIS

lib/Math/PlanePath/Corner.pm  view on Meta::CPAN

Then the X,Y coordinates are

    if (Off < 0)  then  X=d+Off, Y=d
    if (Off >= 0) then  X=d,     Y=d-Off

=head2 X,Y to N

For a given X,Y the bigger of X or Y determines the d gnomon.

If YE<gt>=X then X,Y is on the horizontal part.  At X=0 have N=StartN(d) per
the Start(N) formula above, and any further X is an offset from there.

    if Y >= X then
      d=Y
      N = StartN(d) + X
        = Y^2 + 1 + X

Otherwise if YE<lt>X then X,Y is on the vertical part.  At Y=0 N is the last
point on the gnomon, and one back from the start of the following gnomon,

    if Y <= X then

lib/Math/PlanePath/Diagonals.pm  view on Meta::CPAN

        X^2 + 3X + 2XY + Y + Y^2
    N = ------------------------ + Nstart
                   2

        (X+Y)^2 + 3X + Y
      = ---------------- + Nstart       (using one square)
                2

=head2 N to X,Y

The above formula N=d*(d+1)/2 can be solved for d as

    d = floor( (sqrt(8*N+1) - 1)/2 )
    # with n_start=0

For example N=12 is d=floor((sqrt(8*12+1)-1)/2)=4 as that N falls in the
fifth diagonal.  Then the offset from the Y axis NY=d*(d-1)/2 is the X
position,

    X = N - d*(d+1)/2
    Y = X - d

In the code, fractional N is handled by imagining each diagonal beginning
0.5 back from the Y axis.  That's handled by adding 0.5 into the sqrt, which
is +4 onto the 8*N.

    d = floor( (sqrt(8*N+5) - 1)/2 )
    # N>=-0.5

The X and Y formulas are unchanged, since N=d*(d-1)/2 is still the Y axis.
But each diagonal d begins up to 0.5 before that and therefore X extends
back to -0.5.

=cut

# GP-DEFINE  \\ diagonal length
# GP-DEFINE  d(n) = floor( (sqrt(8*n+1) - 1)/2 );
# GP-Test  vector(16,n,n--; d(n)) == \
# GP-Test    [0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5]
# GP-DEFINE  X(n) = my(d=d(n)); n - d*(d+1)/2;

lib/Math/PlanePath/DiagonalsOctant.pm  view on Meta::CPAN

  }
  $d = int( _sqrtint($d)/2 );
  ### $d

  # remainder positive or negative relative to the start of the following
  # diagonal
  #
  $n -= $d*($d+1) + 1;
  ### remainder: $n

  # $n first in formulas to preserve n=BigFloat when d=integer is BigInt
  #
  if ($self->{'direction'} eq 'up') {
    if (2*$n >= -1) {
      return (-$n + $d,
              $n + $d);
    } else {
      return (-$n - 1,
              $n + 2*$d);
    }
  } else {

lib/Math/PlanePath/DiagonalsOctant.pm  view on Meta::CPAN

    d = floor sqrt(N-0.5)
      = int( sqrt(int(4*$n)-2)/2 )

Taking /2 out of the sqrt helps with C<Math::BigInt> which circa Perl 5.14
doesn't inter-operate with flonums very well.

In any case N-Nstart is an offset into two diagonals, the first of length d
many points and the second d+1.  For example d=3 starting Y=5 for points
N=10,11,12 followed by Y=6 N=13,14,15,16.

The formulas are simplified by calculating a remainder relative to the
second diagonal, so it's negative for the first and positive for the second,

    Nrem = N - (d*(d+1)+1)

d*(d+1)+1 is 1 past the pronic numbers when end each first diagonal, as
described above.  In any case for example d=3 is relative to N=13 making
Nrem=-3,-2,-1 or Nrem=0,1,2,3.

To include the preceding 0.5 in the second diagonal simply means reckoning
NremE<gt>=-0.5 as belonging to the second.  In that base

lib/Math/PlanePath/DivisibleColumns.pm  view on Meta::CPAN

Return the X,Y coordinates of point number C<$n> on the path.  Points begin
at 0 and if C<$n E<lt> 0> then the return is an empty list.

=back

=head1 FORMULAS

=head2 Rectangle to N Range

The cumulative divisor count up to and including a given X column can be
calculated from the fairly well-known sqrt formula, a sum from 1 to sqrt(X).

    S = floor(sqrt(X))
                              /   i=S             \
    numdivs cumulative = 2 * |   sum  floor(X/i)   | - S^2
                              \   i=1             /

This means the N range for 0 to X can be calculated without working out all
each column count up to X.  In the current code if column counts have been
worked out then they're used, otherwise this formula.

=head1 OEIS

This pattern is in Sloane's Online Encyclopedia of Integer Sequences in the
following forms,

=over

L<http://oeis.org/A061017> (etc)

lib/Math/PlanePath/DragonCurve.pm  view on Meta::CPAN

         10--11,7---6   3---2
               |            |
    16   13---12        0---1
     |    |
    15---14                      <- Wmin = -(2^2 - 1)/3 = -1

     ^                      ^Lmin = Wmin = -1
     |
     Lmax = (7*2^2 - 4)/6 = 4

The formulas are all integer values, but the fractions 7/6, 1/3 and 2/3 show
the limits as the level increases.  If scaled so that length Lend=2^k is
reckoned as 1 unit then Lmax extends 1/6 past the end, Lmin and Wmin extend
1/3, and Wmax extends across 2/3.

    +--------+ --
    | -      | 1/6   total length
    || |     |          = 1/6+1+1/3 = 3/2
    || E     | --
    ||       |
    ||       |

lib/Math/PlanePath/DragonCurve.pm  view on Meta::CPAN

2**$level + ($arms-1))>.

There are 2^level segments in a curve level, so 2^level+1 points numbered
from 0.  For multiple arms there are arms*(2^level+1) points, numbered from
0 so n_hi = arms*(2^level+1)-1.

=back

=head1 FORMULAS

Various formulas for coordinates, lengths and area can be found in the
author's long mathematical write-up

=over

L<http://user42.tuxfamily.org/dragon/index.html>

=back

=cut

lib/Math/PlanePath/GcdRationals.pm  view on Meta::CPAN

    "rows_reverse"
    "diagonals_down"
    "diagonals_up"

=back

=head1 FORMULAS

=head2 X,Y to N -- Rows

The defining formula above for X,Y can be inverted to give i,j and N.  This
calculation doesn't notice if X,Y have a common factor, so a coprime(X,Y)
test must be made separately if necessary (for C<xy_to_n()> it is).

    X/Y = g-1 + (i/g)/(j/g)

The g-1 integer part is recovered by a division X divide Y,

    X = quot*Y + rem   division by Y rounded towards 0
      where 0 <= rem < Y
      unless Y=1 in which case use quot=X-1, rem=1
    g-1 = quot
    g = quot+1

The Y=1 special case can instead be left as the usual kind of division
quot=X,rem=0, so 0E<lt>=remE<lt>Y.  This will give i=0 which is outside the
intended 1E<lt>=iE<lt>=j range, but j is 1 bigger and the combination still
gives the correct N.  It's as if the i=g,j=g point at the end of a row is
moved to i=0,j=g+1 just before the start of the next row.  If only N is of
interest not the i,j then it can be left rem=0.

Equating the denominators in the X/Y formula above gives j by

    Y = j/g          the definition above

    j = g*Y
      = (quot+1)*Y
    j = X+Y-rem      per the division X=quot*Y+rem

And equating the numerators gives i by

    X = (g-1)*Y + i/g     the definition above

lib/Math/PlanePath/GcdRationals.pm  view on Meta::CPAN


   g = ceil(X/Y)
     = cquot for division X=cquot*Y - crem

But division in most programming languages is towards 0 or towards
-infinity, not upwards towards +infinity.

=head2 X,Y to N -- Rows Reverse

For pairs_order="rows_reverse", the horizontal i is reversed to j-i+1.  This
can be worked into the triangular part of the N formula as

    Nrrev = (j-i+1) + j*(j-1)/2        for 1<=i<=j
          = j*(j+1)/2 - i + 1

The Y=1 case described above cannot be left to go through with rem=0 giving
i=0 and j+1 since the reversal j-i+1 is then not correct.  Either use rem=1
as described, or if not then compensate at the end,

    if r=0 then j -= 2            adjust
    Nrrev = j*(j+1)/2 - i + 1     same Nrrev as above

lib/Math/PlanePath/GcdRationals.pm  view on Meta::CPAN

j-=2 so that j=6-2=4 gives the desired Nrrev=4*5/2-0+1=11 (per the table in
L</Rows Reverse> above).

=cut

# No, not quite
#
# =head2 Rectangle N Range -- Rows
#
# An over-estimate of the N range can be calculated just from the X,Y to N
# formula above.
#
# Within a row N increases with increasing X, so for a rectangle the minimum
# is in the left column and the maximum in the right column.
#
# Within a column N values increase until reaching the end of a "g" wedge,
# then drop down a bit.  So the maximum is either the top-right corner of the
# rectangle, or the top of the next lower wedge, ie. smaller Y but bigger g.
# Conversely the minimum is either the bottom right of the rectangle, or the
# bottom of the next higher wedge, ie. smaller g but bigger Y.  (Is that
# right?)



( run in 0.853 second using v1.01-cache-2.11-cpan-26ccb49234f )