# Copyright 2011, 2012, 2013, 2014 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/>.
# math-image --values=PlanePathTurn
#
# maybe:
# Turn4 0,1,2,3 and fractional
# Turn4n 0,1,2,-1 negatives Turn4mid Turn4n Turn4s
# TTurn6n 0,1,2,3, -1,-2, eg. flowsnake TTurn6s
# TTurn6 0,1,2,3,4,5
package Math::NumSeq::PlanePathTurn;
use 5.004;
use strict;
use Carp 'croak';
use vars '$VERSION','@ISA';
$VERSION = 118;
use Math::NumSeq;
@ISA = ('Math::NumSeq');
use Math::NumSeq::PlanePathCoord;
use Math::PlanePath;
use Math::PlanePath::Base::Generic
'is_infinite';
use Math::NumSeq::PlanePathDelta;
# uncomment this to run the ### lines
# use Smart::Comments;
use constant characteristic_smaller => 1;
sub description {
my ($self) = @_;
if (ref $self) {
return "Turn values $self->{'turn_type'} from path $self->{'planepath'}";
} else {
# class method
return 'Turns from a PlanePath';
}
}
use constant::defer parameter_info_array =>
sub {
return [
Math::NumSeq::PlanePathCoord::_parameter_info_planepath(),
{
name => 'turn_type',
display => 'Turn Type',
type => 'enum',
default => 'Left',
choices => ['Left','Right','LSR','SLR','SRL',
# 'RSL',
# 'Straight',
# 'Turn4', # Turn4 is 0<=value<4.
# 'Turn4n',
# 'TTurn6',
],
description => 'Left is 1=left, 0=right or straight.
Right is 1=right, 0=left or straight.
LSR is 1=left,0=straight,-1=right.
SLR is 0=straight,1=left,2=right.
SRL is 0=straight,1=right,2=left.',
},
];
};
sub characteristic_integer {
my ($self) = @_;
my $planepath_object = $self->{'planepath_object'};
if (my $func = $planepath_object->can("_NumSeq_Turn_$self->{'turn_type'}_integer")) {
return $planepath_object->$func();
}
return undef;
}
#------------------------------------------------------------------------------
sub oeis_anum {
my ($self) = @_;
### PlanePathTurn oeis_anum() ...
my $planepath = $self->{'planepath_object'};
my $key = Math::NumSeq::PlanePathCoord::_planepath_oeis_anum_key($self->{'planepath_object'});
### planepath: ref $planepath
### $key
### whole table: $planepath->_NumSeq_Turn_oeis_anum
### key href: $planepath->_NumSeq_Turn_oeis_anum->{$key}
return $planepath->_NumSeq_Turn_oeis_anum->{$key}->{$self->{'turn_type'}};
}
#------------------------------------------------------------------------------
sub new {
### PlanePathTurn new(): @_
my $self = shift->SUPER::new(@_);
### self from SUPER: $self
$self->{'planepath_object'}
||= Math::NumSeq::PlanePathCoord::_planepath_name_to_object($self->{'planepath'});
### turn_func: "_turn_func_$self->{'turn_type'}", $self->{'turn_func'}
$self->{'turn_func'} = $self->can('_turn_func_'.$self->{'turn_type'})
|| croak "Unrecognised turn_type: ",$self->{'turn_type'};
$self->rewind;
### $self
return $self;
}
sub i_start {
my ($self) = @_;
my $planepath_object = $self->{'planepath_object'} || return 0;
return $planepath_object->n_start + $planepath_object->arms_count;
}
sub rewind {
my ($self) = @_;
my $planepath_object = $self->{'planepath_object'} || return;
$self->{'i'} = $self->i_start;
$self->{'arms'} = $planepath_object->arms_count;
undef $self->{'prev_dx'};
}
sub next {
my ($self) = @_;
### NumSeq-PlanePathTurn next(): "i=$self->{'i'}"
my $planepath_object = $self->{'planepath_object'};
my $i = $self->{'i'}++;
my $arms = $self->{'arms'};
my $prev_dx = $self->{'prev_dx'};
my $prev_dy;
if (defined $prev_dx) {
$prev_dy = $self->{'prev_dy'};
### use prev dxdy: "$prev_dx,$prev_dy"
} else {
($prev_dx, $prev_dy) = $planepath_object->n_to_dxdy($i-$arms)
or do {
### nothing in path at n: $i
return;
};
### calc prev dxdy: "at i=".($i-$arms)." $prev_dx,$prev_dy"
}
my ($dx, $dy) = $planepath_object->n_to_dxdy($i)
or do {
### nothing in path at previous n: $i-$arms
return;
};
### calc dxdy: "at i=$i $dx,$dy"
if ($arms == 1) {
$self->{'prev_dx'} = $dx;
$self->{'prev_dy'} = $dy;
}
return ($i, $self->{'turn_func'}->($prev_dx,$prev_dy, $dx,$dy));
}
sub ith {
my ($self, $i) = @_;
### PlanePathTurn ith(): $i
if (is_infinite($i)) {
return undef;
}
my $planepath_object = $self->{'planepath_object'};
my $arms = $self->{'arms'};
my ($prev_dx, $prev_dy) = $planepath_object->n_to_dxdy($i - $arms)
or return undef;
my ($dx, $dy) = $planepath_object->n_to_dxdy($i)
or return undef;
return $self->{'turn_func'}->($prev_dx,$prev_dy, $dx,$dy);
}
# dx1,dy1
# dx2,dy2 /
# * /
# /
# /
# /
# /
# O
#
# cmpy = dx2 * dy1/dx1
# left if dy2 > cmpy
# dy2 > dx2 * dy1/dx1
# dy2 * dx1 > dx2 * dy1
#
# if dx1=0, dy1 > 0 then left if dx2 < 0
# dy2 * 0 > dx2 * dy1
# 0 > dx2*dy1 good
#
sub _turn_func_Left {
my ($dx,$dy, $next_dx,$next_dy) = @_;
### _turn_func_Left() ...
return ($next_dy * $dx > $next_dx * $dy ? 1 : 0);
}
sub _turn_func_Right {
my ($dx,$dy, $next_dx,$next_dy) = @_;
### _turn_func_Right() ...
return ($next_dy * $dx < $next_dx * $dy ? 1 : 0);
}
sub _turn_func_LSR {
my ($dx,$dy, $next_dx,$next_dy) = @_;
### _turn_func_LSR() ...
return (($next_dy * $dx <=> $next_dx * $dy) || 0); # 1,0,-1
}
sub _turn_func_RSL {
return - _turn_func_LSR(@_);
}
{
my @LSR_to_SLR = (0, # LSR=0 straight -> SLR=0
1, # LSR=1 left -> SLR=1
2); # LSR=-1 right -> SLR=2
sub _turn_func_SLR {
return $LSR_to_SLR[_turn_func_LSR(@_)];
}
}
{
my @LSR_to_SRL = (0, # LSR=0 straight -> SRL=0
2, # LSR=1 left -> SRL=2
1); # LSR=-1 right -> SRL=1
sub _turn_func_SRL {
return $LSR_to_SRL[_turn_func_LSR(@_)];
}
}
sub _turn_func_Straight {
my ($dx,$dy, $next_dx,$next_dy) = @_;
### _turn_func_Left() ...
return ($next_dy * $dx == $next_dx * $dy ? 1 : 0);
}
# sub _turn_func_LR_01 {
# my ($dx,$dy, $next_dx,$next_dy) = @_;
# ### _turn_func_LR_01() ...
# return ($next_dy * $dx >= $next_dx * $dy || 0);
# }
sub _turn_func_Turn4 {
my ($dx,$dy, $next_dx,$next_dy) = @_;
### _turn_func_Turn4(): "$dx,$dy $next_dx,$next_dy"
return
(((Math::NumSeq::PlanePathDelta::_delta_func_Dir360($next_dx,$next_dy)
- Math::NumSeq::PlanePathDelta::_delta_func_Dir360($dx,$dy)) % 360)
/ 90);
}
sub _turn_func_Turn4n {
my ($dx,$dy, $next_dx,$next_dy) = @_;
require Math::NumSeq::PlanePathDelta;
my $ret
= (((Math::NumSeq::PlanePathDelta::_delta_func_Dir360($next_dx,$next_dy)
- Math::NumSeq::PlanePathDelta::_delta_func_Dir360($dx,$dy)) % 360)
/ 90);
if ($ret > 2) { $ret -= 4; }
return $ret;
}
sub _turn_func_TTurn6 {
my ($dx,$dy, $next_dx,$next_dy) = @_;
require Math::NumSeq::PlanePathDelta;
return
(((Math::NumSeq::PlanePathDelta::_delta_func_TDir360($next_dx,$next_dy)
- Math::NumSeq::PlanePathDelta::_delta_func_TDir360($dx,$dy)) % 360)
/ 60);
}
sub pred {
my ($self, $value) = @_;
### PlanePathTurn pred(): $value
my $planepath_object = $self->{'planepath_object'};
if (defined (my $values_min = $self->values_min)) {
if ($value < $values_min) {
return 0;
}
}
if (defined (my $values_max = $self->values_max)) {
if ($value > $values_max) {
return 0;
}
}
my $turn_type = $self->{'turn_type'};
if ($turn_type eq 'Left' || $turn_type eq 'Right' || $turn_type eq 'Straight') {
unless ($value == 0 || $value == 1) {
return 0;
}
} elsif ($turn_type eq 'LSR' || $turn_type eq 'RSL') {
unless ($value == 1 || $value == 0 || $value == -1) {
return 0;
}
} else { # ($turn_type eq 'SLR' || $turn_type eq 'SRL') {
unless ($value == 0 || $value == 1 || $value == 2) {
return 0;
}
}
if (my $func = $planepath_object->can('_NumSeq_Turn_'.$self->{'turn_type'}.'_pred_hash')) {
my $href = $self->$func();
unless ($href->{$value}) {
return 0;
}
}
return 1;
}
#------------------------------------------------------------------------------
sub values_min {
my ($self) = @_;
my $method = '_NumSeq_Turn_' . $self->{'turn_type'} . '_min';
return $self->{'planepath_object'}->can($method)
? $self->{'planepath_object'}->$method()
: undef;
}
sub values_max {
my ($self) = @_;
my $method = '_NumSeq_Turn_' . $self->{'turn_type'} . '_max';
return $self->{'planepath_object'}->can($method)
? $self->{'planepath_object'}->$method()
: undef;
}
sub characteristic_increasing {
my ($self) = @_;
my $planepath_object = $self->{'planepath_object'};
if (my $func = $planepath_object->can("_NumSeq_Turn_$self->{'turn_type'}_increasing")) {
return $planepath_object->$func();
}
return undef; # unknown
}
sub characteristic_non_decreasing {
my ($self) = @_;
my $planepath_object = $self->{'planepath_object'};
if (my $func = $planepath_object->can("_NumSeq_Turn_$self->{'turn_type'}_non_decreasing")) {
return $planepath_object->$func();
}
if (defined (my $values_min = $self->values_min)) {
if (defined (my $values_max = $self->values_max)) {
if ($values_min == $values_max) {
# constant seq is non-decreasing
return 1;
}
}
}
# increasing means non_decreasing too
return $self->characteristic_increasing;
}
# my $all_Left_predhash = { 0=>1, 1=>1 };
# my $all_LSR_predhash = { 0=>1, 1=>1, -1=>1 };
# my $straight_Left_predhash = { 0=>1 };
# my $straight_LSR_predhash = { 0=>1 };
{ package Math::PlanePath;
use constant 1.02; # for leading underscore
use constant _NumSeq_Turn_Left_min => 0;
use constant _NumSeq_Turn_Left_max => 1;
use constant _NumSeq_Turn_Left_integer => 1;
use constant _NumSeq_Turn_Right_min => 0;
use constant _NumSeq_Turn_Right_max => 1;
use constant _NumSeq_Turn_Right_integer => 1;
use constant _NumSeq_Turn_LSR_min => -1;
use constant _NumSeq_Turn_LSR_max => 1;
use constant _NumSeq_Turn_LSR_integer => 1;
# Right_min == 1 then always Right
# Left_min == 1 then always Left
sub _NumSeq_Turn_SLR_min {
my ($self) = @_;
return (($self->_NumSeq_Turn_Right_min && 2) # if always Right
|| $self->_NumSeq_Turn_Left_min); # 0,1 Left
}
sub _NumSeq_Turn_SLR_max {
my ($self) = @_;
return (($self->_NumSeq_Turn_Right_max && 2) # if always Right
|| $self->_NumSeq_Turn_Left_max); # 0,1 Left
}
use constant _NumSeq_Turn_SLR_integer => 1;
sub _NumSeq_Turn_SRL_min {
my ($self) = @_;
return (($self->_NumSeq_Turn_Left_min && 2) # if always Left
|| $self->_NumSeq_Turn_Right_min); # 0,1 Right
}
sub _NumSeq_Turn_SRL_max {
my ($self) = @_;
return (($self->_NumSeq_Turn_Left_max && 2) # if always Left
|| $self->_NumSeq_Turn_Right_max); # 0,1 Right
}
use constant _NumSeq_Turn_SRL_integer => 1;
use constant _NumSeq_Turn_Turn4_min => 0;
sub _NumSeq_Turn_Turn4_integer {
my ($self) = @_;
return $self->_NumSeq_Delta_Dir4_integer;
}
sub _NumSeq_Turn_Turn4_max {
my ($self) = @_;
return ($self->_NumSeq_Turn_Turn4_integer ? 3 : 4);
}
use constant _NumSeq_Turn_oeis_anum => {};
}
{ package Math::PlanePath::SquareSpiral;
use constant _NumSeq_Turn_LSR_min => 0; # left or straight
use constant _NumSeq_Turn_Right_max => 0; # left or straight
use constant _NumSeq_Turn_Right_non_decreasing => 1;
use constant _NumSeq_Turn_Turn4_max => 1; # left or straight
# SquareSpiral
# abs(A167752)==Left=LSR=Turn4 if that really is the quarter-squares
# abs(A167753)==Left=LSR=Turn4 of wider=1 if that really is the ceil(n+1)^2
}
{ package Math::PlanePath::GreekKeySpiral;
sub _NumSeq_Turn_LSR_min {
my ($self) = @_;
return ($self->{'turns'} == 0 ? 0 # SquareSpiral, left or straight only
: -1); # any left,straight,right
}
sub _NumSeq_Turn_Right_max {
my ($self) = @_;
return ($self->{'turns'} == 0 ? 0 # SquareSpiral, left or straight only
: 1);
}
sub _NumSeq_Turn_Right_non_decreasing {
my ($self) = @_;
return ($self->{'turns'} == 0 ? 1 # SquareSpiral, left or straight only
: 0);
}
sub _NumSeq_Turn_Turn4_max {
my ($self) = @_;
return ($self->{'turns'} == 0
? 1 # SquareSpiral, left or straight only
: 3); # otherwise turn right too
}
}
{ package Math::PlanePath::PyramidSpiral;
use constant _NumSeq_Turn_LSR_min => 0; # left or straight
use constant _NumSeq_Turn_LSR_max => 1;
use constant _NumSeq_Turn_Right_max => 0; # left or straight
use constant _NumSeq_Turn_Right_non_decreasing => 1;
use constant _NumSeq_Turn_Turn4_max => 1.5;
}
{ package Math::PlanePath::TriangleSpiral;
use constant _NumSeq_Turn_LSR_min => 0; # left or straight
use constant _NumSeq_Turn_LSR_max => 1;
use constant _NumSeq_Turn_Right_max => 0; # left or straight
use constant _NumSeq_Turn_Right_non_decreasing => 1;
use constant _NumSeq_Turn_Turn4_max => 1.5;
use constant _NumSeq_Turn_oeis_anum =>
{ 'n_start=-1' =>
{ Left => 'A023531', # 1 at k*(k+3)/2
LSR => 'A023531',
Straight => 'A023532', # 0 at k*(k+3)/2, 1 otherwise
# OEIS-Other: A023531 planepath=TriangleSpiral,n_start=-1
# OEIS-Other: A023531 planepath=TriangleSpiral,n_start=-1 turn_type=LSR
# OEIS-Other: A023532 planepath=TriangleSpiral,n_start=-1 turn_type=Straight
},
# PlanePathTurn planepath=TriangleSpiral,n_start=1, turn_type=TTurn6
# A089799 Expansion of Jacobi theta function theta_2(q^(1/2))/q^(1/8)
# is this 2s with runs of 0s ?
};
}
{ package Math::PlanePath::TriangleSpiralSkewed;
use constant _NumSeq_Turn_LSR_min => 0; # left or straight
use constant _NumSeq_Turn_LSR_max => 1;
use constant _NumSeq_Turn_Right_max => 0; # left or straight
use constant _NumSeq_Turn_Right_non_decreasing => 1;
use constant _NumSeq_Turn_Turn4_max => 1.5;
use constant _NumSeq_Turn_oeis_anum =>
{
do {
my $href = { Left => 'A023531', # 1 at k*(k+3)/2
LSR => 'A023531',
Straight => 'A023532', # 0 at k*(k+3)/2, 1 otherwise
};
('skew=left,n_start=-1' => $href,
'skew=right,n_start=-1' => $href,
'skew=up,n_start=-1' => $href,
'skew=down,n_start=-1' => $href)
# OEIS-Other: A023531 planepath=TriangleSpiralSkewed,n_start=-1
# OEIS-Other: A023531 planepath=TriangleSpiralSkewed,n_start=-1 turn_type=LSR
# OEIS-Other: A023532 planepath=TriangleSpiralSkewed,n_start=-1 turn_type=Straight
# OEIS-Other: A023531 planepath=TriangleSpiralSkewed,n_start=-1,skew=right
# OEIS-Other: A023531 planepath=TriangleSpiralSkewed,n_start=-1,skew=up
# OEIS-Other: A023531 planepath=TriangleSpiralSkewed,n_start=-1,skew=down
},
};
}
{ package Math::PlanePath::DiamondSpiral;
use constant _NumSeq_Turn_LSR_min => 0; # left or straight
use constant _NumSeq_Turn_LSR_max => 1;
use constant _NumSeq_Turn_Right_max => 0; # left or straight
use constant _NumSeq_Turn_Right_non_decreasing => 1;
use constant _NumSeq_Turn_Turn4_max => 1.5;
}
{ package Math::PlanePath::AztecDiamondRings;
use constant _NumSeq_Turn_LSR_min => 0; # left or straight
use constant _NumSeq_Turn_Right_max => 0; # left or straight
use constant _NumSeq_Turn_Right_non_decreasing => 1;
use constant _NumSeq_Turn_Turn4_max => 1; # left or straight
}
{ package Math::PlanePath::PentSpiral;
use constant _NumSeq_Turn_LSR_min => 0; # left or straight
use constant _NumSeq_Turn_Right_max => 0; # left or straight
use constant _NumSeq_Turn_Right_non_decreasing => 1;
use constant _NumSeq_Turn_Turn4_max =>
Math::NumSeq::PlanePathTurn::_turn_func_Turn4(2,0, -2,1);
}
{ package Math::PlanePath::PentSpiralSkewed;
use constant _NumSeq_Turn_LSR_min => 0; # left or straight
use constant _NumSeq_Turn_Right_max => 0; # left or straight
use constant _NumSeq_Turn_Right_non_decreasing => 1;
use constant _NumSeq_Turn_Turn4_max => 1.5;
}
{ package Math::PlanePath::HexSpiral;
use constant _NumSeq_Turn_LSR_min => 0; # left or straight
use constant _NumSeq_Turn_Right_max => 0; # left or straight
use constant _NumSeq_Turn_Right_non_decreasing => 1;
use constant _NumSeq_Turn_Turn4_max => 1.5;
}
{ package Math::PlanePath::HexSpiralSkewed;
use constant _NumSeq_Turn_LSR_min => 0; # left or straight
use constant _NumSeq_Turn_Right_max => 0; # left or straight
use constant _NumSeq_Turn_Right_non_decreasing => 1;
use constant _NumSeq_Turn_Turn4_max => 1.5;
}
{ package Math::PlanePath::HeptSpiralSkewed;
use constant _NumSeq_Turn_LSR_min => 0; # left or straight
use constant _NumSeq_Turn_Right_max => 0; # left or straight
use constant _NumSeq_Turn_Right_non_decreasing => 1;
use constant _NumSeq_Turn_Turn4_max => 1.5; # at N=2 turn +135
}
{ package Math::PlanePath::AnvilSpiral;
use constant _NumSeq_Turn_Turn4_max => 3;
}
{ package Math::PlanePath::OctagramSpiral;
use constant _NumSeq_Turn_Turn4_max => 3; # +90 right
}
{ package Math::PlanePath::KnightSpiral;
# use constant _NumSeq_Turn_Turn4_min => ...; # 2,1
}
# { package Math::PlanePath::CretanLabyrinth;
# }
{ package Math::PlanePath::SquareArms;
use constant _NumSeq_Turn_LSR_min => 0; # left or straight
use constant _NumSeq_Turn_LSR_max => 1;
use constant _NumSeq_Turn_Right_max => 0; # left or straight
use constant _NumSeq_Turn_Right_non_decreasing => 1;
use constant _NumSeq_Turn_Turn4_max => 1; # left or straight
}
{ package Math::PlanePath::DiamondArms;
use constant _NumSeq_Turn_LSR_min => 0; # left or straight
use constant _NumSeq_Turn_LSR_max => 1;
use constant _NumSeq_Turn_Right_max => 0; # left or straight
use constant _NumSeq_Turn_Right_non_decreasing => 1;
use constant _NumSeq_Turn_Turn4_max => 1; # left or straight
use constant _NumSeq_Turn_Turn4_integer => 1;
}
{ package Math::PlanePath::HexArms;
use constant _NumSeq_Turn_LSR_min => 0; # left or straight
use constant _NumSeq_Turn_LSR_max => 1;
use constant _NumSeq_Turn_Right_max => 0; # left or straight
use constant _NumSeq_Turn_Right_non_decreasing => 1;
use constant _NumSeq_Turn_Turn4_max => 1; # at N=8
}
{ package Math::PlanePath::SacksSpiral;
use constant _NumSeq_Turn_Left_min => 1; # left always
use constant _NumSeq_Turn_Left_max => 1;
use constant _NumSeq_Turn_Left_non_decreasing => 1;
use constant _NumSeq_Turn_LSR_min => 1;
use constant _NumSeq_Turn_LSR_max => 1;
use constant _NumSeq_Turn_LSR_non_decreasing => 1;
use constant _NumSeq_Turn_Right_max => 0; # left always
use constant _NumSeq_Turn_Right_non_decreasing => 1;
sub _NumSeq_Turn_Turn4_max {
my ($self) = @_;
# at N=1 is maximum turn
return Math::NumSeq::PlanePathTurn::_turn_func_Turn4(1,0,
$self->n_to_dxdy(1));
}
use constant _NumSeq_Turn4_min_is_infimum => 1;
use constant _NumSeq_Turn_oeis_anum =>
{ '' =>
{ 'Left' => 'A000012', # left always, all ones
'LSR' => 'A000012',
# OEIS-Other: A000012 planepath=SacksSpiral
# OEIS-Other: A000012 planepath=SacksSpiral turn_type=LSR
},
};
}
{ package Math::PlanePath::VogelFloret;
sub _NumSeq_Turn_Left_min { # always left if rot<=0.5
my ($self) = @_;
return ($self->{'rotation_factor'} > 0.5 ? 0 : 1);
}
sub _NumSeq_Turn_Left_max {
my ($self) = @_;
return ($self->{'rotation_factor'} > 0.5 ? 0 : 1);
}
use constant _NumSeq_Turn_Left_non_decreasing => 1; # constant 0 or 1
sub _NumSeq_Turn_Right_min { # always right if rot>0.5
my ($self) = @_;
return ($self->{'rotation_factor'} > 0.5 ? 1 : 0);
}
sub _NumSeq_Turn_Right_max {
my ($self) = @_;
return ($self->{'rotation_factor'} > 0.5 ? 1 : 0);
}
use constant _NumSeq_Turn_Right_non_decreasing => 1; # constant 0 or 1
sub _NumSeq_Turn_LSR_min {
my ($self) = @_;
return ($self->{'rotation_factor'} > 0.5 ? -1 : 1);
}
sub _NumSeq_Turn_LSR_max {
my ($self) = @_;
return ($self->{'rotation_factor'} > 0.5 ? -1 : 1);
}
use constant _NumSeq_Turn_LSR_non_decreasing => 1; # constant 1 or -1
# sub _NumSeq_Turn_LSR_pred_hash {
# my ($self) = @_;
# return ($self->{'rotation_factor'} > 0.5 ? 1 : 0);
# }
}
{ package Math::PlanePath::TheodorusSpiral;
use constant _NumSeq_Turn_Left_min => 1; # left always
use constant _NumSeq_Turn_Left_max => 1;
use constant _NumSeq_Turn_Left_non_decreasing => 1;
use constant _NumSeq_Turn_LSR_min => 1;
use constant _NumSeq_Turn_LSR_max => 1;
use constant _NumSeq_Turn_LSR_non_decreasing => 1;
use constant _NumSeq_Turn_Right_max => 0; # left always
use constant _NumSeq_Turn_Right_non_decreasing => 1;
use constant _NumSeq_Turn4_min_is_infimum => 1; # approaches straight
use constant _NumSeq_Turn_Turn4_max => 1; # initial 90deg
use constant _NumSeq_Turn_oeis_anum =>
{ '' =>
{ 'Left' => 'A000012', # left always, all ones
'LSR' => 'A000012',
# OEIS-Other: A000012 planepath=TheodorusSpiral
# OEIS-Other: A000012 planepath=TheodorusSpiral turn_type=LSR
},
};
}
{ package Math::PlanePath::ArchimedeanChords;
use constant _NumSeq_Turn_Left_min => 1; # left always
use constant _NumSeq_Turn_Left_max => 1;
use constant _NumSeq_Turn_Left_non_decreasing => 1;
use constant _NumSeq_Turn_LSR_min => 1;
use constant _NumSeq_Turn_LSR_max => 1;
use constant _NumSeq_Turn_LSR_non_decreasing => 1;
use constant _NumSeq_Turn_Right_max => 0; # left always
use constant _NumSeq_Turn_Right_non_decreasing => 1;
sub _NumSeq_Turn_Turn4_max {
my ($self) = @_;
# at N=1 is maximum turn
return Math::NumSeq::PlanePathTurn::_turn_func_Turn4(1,0,
$self->n_to_dxdy(1));
}
use constant _NumSeq_Turn4_min_is_infimum => 1; # approaches straight ahead
use constant _NumSeq_Turn_oeis_anum =>
{ '' =>
{ 'Left' => 'A000012', # left always, all ones
'LSR' => 'A000012',
# OEIS-Other: A000012 planepath=ArchimedeanChords
# OEIS-Other: A000012 planepath=ArchimedeanChords turn_type=LSR
},
};
}
{ package Math::PlanePath::MultipleRings;
# step=1 and step=2 are mostly 1 for left, but after a while each ring
# endpoint is to the right
sub _NumSeq_Turn_Left_max {
my ($self) = @_;
return ($self->{'step'} <= 0
? 0 # step == 0 is always straight ahead
: 1);
}
sub _NumSeq_Turn_Left_non_decreasing {
my ($self) = @_;
# step=0 always straight
# step=1 straight,straight, then always left
return ($self->{'step'} <= 1);
}
sub _NumSeq_Turn_Right_max {
my ($self) = @_;
# step=0 is always straight ahead
# step=1 is never right
return ($self->{'step'} <= 1
? 0
: 1);
}
*_NumSeq_Turn_Right_non_decreasing = \&_NumSeq_Turn_Left_non_decreasing;
sub _NumSeq_Turn_LSR_min {
my ($self) = @_;
return ($self->{'step'} <= 1
? 0 # step == 0 is always straight ahead
: -1);
}
*_NumSeq_Turn_LSR_max = \&_NumSeq_Turn_Left_max;
*_NumSeq_Turn_LSR_non_decreasing = \&_NumSeq_Turn_Left_non_decreasing;
sub _NumSeq_Turn_SLR_min {
my ($self) = @_;
# step=0 straight line
# step=1 straight at N=2
# step=2 straight at N=2
return ($self->{'step'} <= 2 ? 0
: $self->{'ring_shape'} eq 'circle' ? 1 # never straight
: 0); # ring_shape=polygon sides straight
}
sub _NumSeq_Turn_SLR_max {
my ($self) = @_;
return ($self->{'step'} == 0 ? 0 # straight line only
: $self->{'step'} == 1 ? 1
: 2);
}
sub _NumSeq_Turn_SLR_non_decreasing {
my ($self) = @_;
return ($self->{'step'} <= 1 ? 1 # straight line only
: 0);
}
*_NumSeq_Turn_SRL_min = \&_NumSeq_Turn_SLR_min;
sub _NumSeq_Turn_SRL_max {
my ($self) = @_;
return ($self->{'step'} == 0 ? 0 # straight line only
: 2);
}
*_NumSeq_Turn_SRL_non_decreasing = \&_NumSeq_Turn_SLR_non_decreasing;
sub _NumSeq_Turn_Turn4_max {
my ($self) = @_;
my $step = $self->{'step'};
return ($step == 0
? 0 # step == 0 is always straight ahead
: 4/$step);
}
use constant _NumSeq_Turn_oeis_anum =>
{
# MultipleRings step=0 is trivial X=N,Y=0
'step=0,ring_shape=circle' =>
{ Left => 'A000004', # all-zeros
LSR => 'A000004', # all zeros, straight
# OEIS-Other: A000004 planepath=MultipleRings,step=0
# OEIS-Other: A000004 planepath=MultipleRings,step=0 turn_type=LSR
},
'step=0,ring_shape=polygon' =>
{ Left => 'A000004', # all-zeros
LSR => 'A000004', # all zeros, straight
# OEIS-Other: A000004 planepath=MultipleRings,step=0,ring_shape=polygon
# OEIS-Other: A000004 planepath=MultipleRings,step=0,ring_shape=polygon turn_type=LSR
},
};
}
{ package Math::PlanePath::PixelRings;
# has right turns between rings
use constant _NumSeq_Turn_Turn4_max => 3.5;
}
{ package Math::PlanePath::FilledRings;
use constant _NumSeq_Turn_Turn4_max => 3.5;
}
{ package Math::PlanePath::Hypot;
sub _NumSeq_Turn_Left_min {
my ($self) = @_;
return ($self->{'points'} eq 'all'
? 1 # all, left always
: 0); # odd,even left or straight
}
sub _NumSeq_Turn_Left_non_decreasing {
my ($self) = @_;
return ($self->{'points'} eq 'all'
? 1 # all, left always
: 0); # odd,even any
}
use constant _NumSeq_Turn_Right_max => 0; # always left or straight
use constant _NumSeq_Turn_Right_non_decreasing => 1;
sub _NumSeq_Turn_LSR_min {
my ($self) = @_;
return ($self->{'points'} eq 'all'
? 1 # all, left always
: 0); # odd,even left or straight
}
*_NumSeq_Turn_LSR_non_decreasing = \&_NumSeq_Turn_Left_non_decreasing;
sub _NumSeq_Turn4_min_is_infimum {
my ($self) = @_;
return ($self->{'points'} eq 'all');
}
{
my %_NumSeq_Turn_Turn4_max
= (all => 1.5, # at N=2, apparent maximum
even => 1.5, # at N=2, apparent maximum
odd => Math::NumSeq::PlanePathTurn::_turn_func_Turn4(3,-3, 3,5),
);
sub _NumSeq_Turn_Turn4_max {
my ($self) = @_;
return ($_NumSeq_Turn_Turn4_max{$self->{'points'}} || 0);
}
}
}
{ package Math::PlanePath::HypotOctant;
# apparently approaches +360 degrees
use constant _NumSeq_Turn4_max_is_supremum => 1;
}
{ package Math::PlanePath::TriangularHypot;
sub _NumSeq_Turn_Left_min {
my ($self) = @_;
return ($self->{'points'} eq 'hex'
? 1 # hex, left always
: 0); # other, various left/right
}
*_NumSeq_Turn_Left_non_decreasing = \&_NumSeq_Turn_Left_min;
sub _NumSeq_Turn_Right_max {
my ($self) = @_;
return ($self->{'points'} =~ /hex|even/
? 0 # even,hex, left or straight, so Right=0 always
: 1); # odd,all both left or right
}
sub _NumSeq_Turn_Right_non_decreasing {
my ($self) = @_;
return ($self->{'points'} =~ /hex|even/
? 1 # even,hex, left or straight, so Right=0 always
: 0); # odd,all both left or right
}
sub _NumSeq_Turn_LSR_min {
my ($self) = @_;
return ($self->{'points'} eq 'hex'
? 1 # hex, left always
: $self->{'points'} =~ /even|hex_/
? 0 # even,hex, left or straight
: -1); # odd,all any
}
*_NumSeq_Turn_LSR_non_decreasing = \&_NumSeq_Turn_Left_min;
{
my %_NumSeq_Turn_SLR_min = (odd => 1, # never straight
hex => 1, # always left
);
sub _NumSeq_Turn_SLR_min {
my ($self) = @_;
return ($_NumSeq_Turn_SLR_min{$self->{'points'}} || 0);
}
}
{
my %_NumSeq_Turn_SRL_min = (odd => 1, # never straight
hex => 2, # always left
);
sub _NumSeq_Turn_SRL_min {
my ($self) = @_;
return ($_NumSeq_Turn_SRL_min{$self->{'points'}} || 0);
}
}
# points=even Turn4=0 at N=31
#
# points=all apparently approaches 0
# min i=473890[1303230202] 0.00000 px=-11,py=1 dx=-13,dy=1 -13.000
#
# points=odd apparently approaches 0
# min i=95618[113112002] 0.01111 px=-14,py=4 dx=-16,dy=4 -4.000
#
# points=hex apparently approaches 0
# min i=44243[22303103] 0.01111 px=-15,py=3 dx=-12,dy=2 -6.000
#
# points=hex_rotated Turn4=0 at N=58
# points=hex_centred Turn4=0 at N=24
{
my %_NumSeq_Turn4_min_is_infimum = (all => 1,
odd => 1,
hex => 1,
);
sub _NumSeq_Turn4_min_is_infimum {
my ($self) = @_;
return ($_NumSeq_Turn4_min_is_infimum{$self->{'points'}} || 0);
}
}
{
my %_NumSeq_Turn_Turn4_max
= (even => 1.5, # at N=2
odd => Math::NumSeq::PlanePathTurn::_turn_func_Turn4(5,3, 0,-6),
all => Math::NumSeq::PlanePathTurn::_turn_func_Turn4(5,3, 0,-6),
hex => Math::NumSeq::PlanePathTurn::_turn_func_Turn4(2,0, -3,1),
hex_rotated => Math::NumSeq::PlanePathTurn::_turn_func_Turn4(1,1, -3,-1),
hex_centred => Math::NumSeq::PlanePathTurn::_turn_func_Turn4(3,1, -2,2),
);
sub _NumSeq_Turn_Turn4_max {
my ($self) = @_;
return ($_NumSeq_Turn_Turn4_max{$self->{'points'}} || 0);
}
}
}
{ package Math::PlanePath::PythagoreanTree;
{
my %UAD_coordinates_always_right = (PQ => 1,
AB => 1,
AC => 1);
sub _NumSeq_Turn_always_Right {
my ($self) = @_;
return ($self->{'tree_type'} eq 'UAD'
&& $UAD_coordinates_always_right{$self->{'coordinates'}});
}
}
{
my %UAD_coordinates_always_left = (BC => 1);
sub _NumSeq_Turn_always_Left {
my ($self) = @_;
return ($self->{'tree_type'} eq 'UAD'
&& $UAD_coordinates_always_left{$self->{'coordinates'}});
}
}
{
my %UMT_coordinates_any_straight = (BC => 1, # UMT at N=5
PQ => 1); # UMT at N=5
sub _NumSeq_Turn_never_straight {
my ($self) = @_;
return ($self->{'tree_type'} eq 'UMT'
&& $UMT_coordinates_any_straight{$self->{'coordinates'}}
? 0 : 1);
}
}
sub _NumSeq_Turn_Left_min {
my ($self) = @_;
return (_NumSeq_Turn_always_Left($self) ? 1 : 0);
}
sub _NumSeq_Turn_Left_max {
my ($self) = @_;
return (_NumSeq_Turn_always_Right($self) ? 0 : 1);
}
sub _NumSeq_Turn_Right_min {
my ($self) = @_;
return (_NumSeq_Turn_always_Right($self) ? 1 : 0);
}
sub _NumSeq_Turn_Right_max {
my ($self) = @_;
return (_NumSeq_Turn_always_Left($self) ? 0 : 1);
}
sub _NumSeq_Turn_LSR_min {
my ($self) = @_;
return (_NumSeq_Turn_always_Left($self) ? 1 : -1);
}
sub _NumSeq_Turn_LSR_max {
my ($self) = @_;
return (_NumSeq_Turn_always_Right($self) ? -1 : 1);
}
sub _NumSeq_Turn_SLR_min {
my ($self) = @_;
return (_NumSeq_Turn_always_Right($self) ? 2
: $self->_NumSeq_Turn_never_straight);
}
sub _NumSeq_Turn_SLR_max {
my ($self) = @_;
return (_NumSeq_Turn_always_Left($self) ? 1 : 2);
}
sub _NumSeq_Turn_SRL_min {
my ($self) = @_;
return (_NumSeq_Turn_always_Left($self) ? 2
: $self->_NumSeq_Turn_never_straight);
}
sub _NumSeq_Turn_SRL_max {
my ($self) = @_;
return (_NumSeq_Turn_always_Right($self) ? 1 : 2);
}
sub _NumSeq_Turn_Left_non_decreasing {
my ($self) = @_;
return (_NumSeq_Turn_always_Left($self)
|| _NumSeq_Turn_always_Right($self)
? 1 : 0);
}
*_NumSeq_Turn_Right_non_decreasing = \&_NumSeq_Turn_Left_non_decreasing;
*_NumSeq_Turn_LSR_non_decreasing = \&_NumSeq_Turn_Left_non_decreasing;
# A000004 all-zeros and A000012 all-ones are OFFSET=0 which doesn't match
# start N=1 here for always turn left or right in UAD.
}
{ package Math::PlanePath::RationalsTree;
{
my %_NumSeq_Turn_SLR_min = (SB => 1,
CW => 1,
# Bird => 0, # straight at N=7 and N=8
Drib => 1,
# AYT => 0, # straight at N=7
HCS => 1,
L => 1);
sub _NumSeq_Turn_SLR_min {
my ($self) = @_;
return ($_NumSeq_Turn_SLR_min{$self->{'tree_type'}} || 0);
}
}
*_NumSeq_Turn_SRL_min = \&_NumSeq_Turn_SLR_min;
# SB turn cf A021913 0,0,1,1
# A133872 1,1,0,0
# A057077 1,1,-1,-1
# A087960 1,-1,-1,1
# HCS turn left close to A010059 thue-morse or A092436
# right A010060
# LSR => 'A106400', # thue-morse +/-1
# CfracDigits radix=1 likewise
}
# { package Math::PlanePath::FractionsTree;
# }
{ package Math::PlanePath::ChanTree;
use constant _NumSeq_Turn_SLR_min => 1; # never straight
use constant _NumSeq_Turn_SRL_min => 1; # never straight
# # FIXME: k=4,5,6 are Right-only, maybe
# # sub _NumSeq_Turn_Left_max {
# # my ($self) = @_;
# # return ($self->{'k'} >= 4
# # ? 0 # never Left
# # : 1);
# # }
# # sub _NumSeq_Turn_Right_min {
# # my ($self) = @_;
# # return ($self->{'k'} >= 4
# # ? 1 # always Right
# # : 0);
# # }
# # sub _NumSeq_Turn_LSR_max {
# # my ($self) = @_;
# # return ($self->{'k'} >= 4
# # ? -1 # always Right
# # : 1);
# # }
}
{ package Math::PlanePath::DiagonalRationals;
sub _NumSeq_Turn_Turn4_max {
my ($self) = @_;
return ($self->{'direction'} eq 'down'
? 2.5 # N=2
: Math::NumSeq::PlanePathTurn::_turn_func_Turn4(-1,1, 2,-1)); # N=3
}
}
# { package Math::PlanePath::FactorRationals;
# # revbinary
# # max i=296[10220] 3.98889 px=-258,py=1 dx=-26,dy=1[-122,1] -26.000
# N=295=5*59 X=5*59 Y=1 N=297
# N=296=2^3*37 X=37 Y=2 -26,+1 \ N=296 <-------- N=295
# N=297=3^3*11 X=11 Y=3 -258,+1
# }
{ package Math::PlanePath::GcdRationals;
# Turn4 minimum
# pairs_order=rows
# min=0 at N=12
# max i=216[3120] 3.98889 px=11,py=-14 dx=3,dy=-4[3,-10] -0.750
#
# pairs_order=rows_reverse
# min i=13[31] 0.00000 px=-1,py=0 dx=-1,dy=0 0.000
# max i=611[21203] 3.98889 px=-1,py=2 dx=-13,dy=28[-31,130] -0.464
#
# pairs_order=diagonals_down
# min i=2[2] 0.00000 px=0,py=1 dx=0,dy=1 0.000
# max i=561[20301] 3.98889 px=-7,py=8 dx=-5,dy=6[-11,12] -0.833
#
# pairs_order=diagonals_up
# min i=11[23] 0.00000 px=-1,py=1 dx=-1,dy=1 -1.000
# max i=4886[1030112] 3.98889 px=6,py=-6 dx=15,dy=-16[33,-100] -0.938
#
# Are these exact maximums or more when bigger N?
}
{ package Math::PlanePath::CfracDigits;
sub _NumSeq_Turn_SLR_min {
my ($self) = @_;
return ($self->{'radix'} == 1
? 1 # never straight
: 0);
}
*_NumSeq_Turn_SRL_min = \&_NumSeq_Turn_SLR_min;
}
{ package Math::PlanePath::AR2W2Curve; # NSEW+diag
use constant _NumSeq_Turn_Turn4_max => 3.5;
}
{ package Math::PlanePath::PeanoCurve;
use constant _NumSeq_Turn_oeis_anum =>
{ 'radix=3' =>
{
# 2---0---0---0---0---2
# | |
# 2---0---1 1---0---2
# | |
# .---0---1 1---0---0-...
SLR => 'A163536', # turn 0=ahead,1=left,2=right, OFFSET=1
SRL => 'A163537',
# OEIS-Catalogue: A163536 planepath=PeanoCurve turn_type=SLR
# OEIS-Catalogue: A163537 planepath=PeanoCurve turn_type=SRL
# Not quite, A039963 is OFFSET=0 vs first turn N=1 here
# Straight => 'A039963',
},
};
}
# { package Math::PlanePath::WunderlichSerpentine;
# }
{ package Math::PlanePath::HilbertCurve;
use constant _NumSeq_Turn_oeis_anum =>
{ '' =>
{ SLR => 'A163542', # relative direction ahead=0,left=1,right=2 OFFSET=1
SRL => 'A163543', # relative direction transpose
# OEIS-Catalogue: A163542 planepath=HilbertCurve turn_type=SLR
# OEIS-Catalogue: A163543 planepath=HilbertCurve turn_type=SRL
},
};
}
{ package Math::PlanePath::ZOrderCurve;
sub _NumSeq_Turn_SLR_min {
my ($self) = @_;
return ($self->{'radix'} == 2
? 1 # radix=2 never straight
: 0);
}
*_NumSeq_Turn_SRL_min = \&_NumSeq_Turn_SLR_min;
sub _NumSeq_Turn_Turn4_min {
my ($self) = @_;
return ($self->{'radix'} == 2
? 0.5
: 0); # includes straight
}
# radix max at
# ----- ------
# 2 3 *---* Y=radix-1
# 3 8 \
# 4 15 \
# 5 24 * Y=0
sub _NumSeq_Turn_Turn4_max {
my ($self) = @_;
return (Math::NumSeq::PlanePathTurn::_turn_func_Turn4
(1,0, 1,1-$self->{'radix'}));
}
}
{ package Math::PlanePath::GrayCode;
# radix=2 TsF==Fs is always straight or left
sub _NumSeq_Turn_Right_max {
my ($self) = @_;
if ($self->{'radix'} == 2
&& ($self->{'apply_type'} eq 'TsF'
|| $self->{'apply_type'} eq 'Fs')) {
return 0; # never right
}
return 1;
}
sub _NumSeq_Turn_Right_non_decreasing {
my ($self) = @_;
if ($self->{'radix'} == 2
&& ($self->{'apply_type'} eq 'TsF'
|| $self->{'apply_type'} eq 'Fs')) {
return 1; # never right
}
return 0;
}
sub _NumSeq_Turn_LSR_min {
my ($self) = @_;
if ($self->{'radix'} == 2
&& ($self->{'apply_type'} eq 'TsF'
|| $self->{'apply_type'} eq 'Fs')) {
return 0; # never right
}
return -1;
}
sub _NumSeq_Turn_SLR_min {
my ($self) = @_;
return ($self->{'radix'} == 2
&& ($self->{'apply_type'} eq 'sT' || $self->{'apply_type'} eq 'sF')
? 1 # never straight
: 0);
}
# ENHANCE-ME: check this is true
# PlanePathTurn planepath=GrayCode,apply_type=TsF,gray_type=reflected,radix=2, turn_type=SLR
# PlanePathTurn planepath=GrayCode,apply_type=Fs,gray_type=reflected,radix=2, turn_type=SLR
# match 1,1,0,0,1,1,1,1,1,1,0,0,1,1,0,0,1,1,0,0,1,1,1,1,1,1
# A039963 The period-doubling sequence A035263 repeated.
# A039963 ,1,1,0,0,1,1,1,1,1,1,0,0,1,1,0,0,1,1,0,0,1,1,1,1,1,1
# Not quite, A039963 is OFFSET=0 vs first turn at N=1 here
# 'Math::PlanePath::GrayCode' =>
# {
# Left => 'A039963', # duplicated KochCurve
# LSR => 'A039963',
# },
# Koch characteristic of A003159 ending even zeros
# 'Math::PlanePath::GrayCode' =>
use constant _NumSeq_Turn_oeis_anum =>
{
do {
my $peano = Math::PlanePath::PeanoCurve
-> _NumSeq_Turn_oeis_anum -> {'radix=3'};
('apply_type=TsF,gray_type=reflected,radix=3' => $peano,
'apply_type=FsT,gray_type=reflected,radix=3' => $peano,
),
# OEIS-Other: A163536 planepath=GrayCode,apply_type=TsF,radix=3 turn_type=SLR
# OEIS-Other: A163536 planepath=GrayCode,apply_type=FsT,radix=3 turn_type=SLR
# OEIS-Other: A163537 planepath=GrayCode,apply_type=TsF,radix=3 turn_type=SRL
# OEIS-Other: A163537 planepath=GrayCode,apply_type=FsT,radix=3 turn_type=SRL
},
};
}
{ package Math::PlanePath::ImaginaryBase;
sub _NumSeq_Turn_SLR_min {
my ($self) = @_;
return ($self->{'radix'} == 2
? 1 # radix=2 never straight
: 0);
}
*_NumSeq_Turn_SRL_min = \&_NumSeq_Turn_SLR_min;
}
{ package Math::PlanePath::ImaginaryHalf;
{
my %_NumSeq_Turn_SLR_min = (XYX => 1,
# XXY => 0,
YXX => 1,
# XnXY => 0,
XnYX => 1,
YXnX => 1,
);
sub _NumSeq_Turn_SLR_min {
my ($self) = @_;
return ($self->{'radix'} == 2
&& $_NumSeq_Turn_SLR_min{$self->{'digit_order'}}
? 1
: 0);
}
}
*_NumSeq_Turn_SRL_min = \&_NumSeq_Turn_SLR_min;
}
{ package Math::PlanePath::CubicBase;
use constant _NumSeq_Turn_SLR_min => 1; # never straight
use constant _NumSeq_Turn_SRL_min => 1; # never straight
}
{ package Math::PlanePath::Flowsnake;
# inherit from FlowsnakeCentres
}
{ package Math::PlanePath::FlowsnakeCentres;
use constant _NumSeq_Turn_Turn4_max => 3.5;
}
{ package Math::PlanePath::GosperIslands;
use constant _NumSeq_Turn_SLR_min => 1; # never straight
use constant _NumSeq_Turn_SRL_min => 1; # never straight
}
{ package Math::PlanePath::KochCurve;
use constant _NumSeq_Turn_SLR_min => 1; # never straight
use constant _NumSeq_Turn_SRL_min => 1; # never straight
use constant _NumSeq_Turn_oeis_anum =>
{ '' =>
{ Left => 'A035263', # OFFSET=1 matches N=1
# OEIS-Catalogue: A035263 planepath=KochCurve
SLR => 'A056832',
# OEIS-Catalogue: A056832 planepath=KochCurve turn_type=SLR
# A056832 All a(n) = 1 or 2; a(1) = 1; get next 2^k terms by repeating first 2^k terms and changing last element so sum of first 2^(k+1) terms is odd.
# A056832 ,1,2,1,1,1,2,1,2,1,2,1,1,1,2,1
# Not quite, A096268 OFFSET=0 values 0,1,0,0,0,1
# whereas here N=1 first turn values 0,1,0,0,0,1
# Right => 'A096268', # morphism
},
};
}
{ package Math::PlanePath::KochPeaks;
use constant _NumSeq_Turn_SLR_min => 1; # never straight
use constant _NumSeq_Turn_SRL_min => 1; # never straight
}
{ package Math::PlanePath::KochSnowflakes;
use constant _NumSeq_Turn_SLR_min => 1; # never straight
use constant _NumSeq_Turn_SRL_min => 1; # never straight
}
{ package Math::PlanePath::KochSquareflakes;
use constant _NumSeq_Turn_SLR_min => 1; # never straight
use constant _NumSeq_Turn_SRL_min => 1; # never straight
}
# { package Math::PlanePath::QuadricCurve;
# }
# { package Math::PlanePath::QuadricIslands;
# }
{ package Math::PlanePath::SierpinskiTriangle;
{
my %_NumSeq_Turn_Turn4_max = (triangular => 2.5,
left => 2.5,
right => 3,
diagonal => 2.5,
);
sub _NumSeq_Turn_Turn4_max {
my ($self) = @_;
return $_NumSeq_Turn_Turn4_max{$self->{'align'}};
}
}
}
{ package Math::PlanePath::SierpinskiArrowhead;
use constant _NumSeq_Turn_SLR_min => 1; # never straight
use constant _NumSeq_Turn_SRL_min => 1; # never straight
use constant _NumSeq_Turn_Turn4_min => 0.5; # North-East diagonal
use constant _NumSeq_Turn_Turn4_max => 3.5; # South-East diagonal
}
{ package Math::PlanePath::SierpinskiArrowheadCentres;
use constant _NumSeq_Turn_Turn4_max => 3.5; # South-East diagonal
}
{ package Math::PlanePath::SierpinskiCurve;
use constant _NumSeq_Turn_SLR_min => 1; # never straight
use constant _NumSeq_Turn_SRL_min => 1; # never straight
# use constant _NumSeq_Turn_oeis_anum =>
# { 'arms=1' =>
# {
# # Not quite, A039963 numbered OFFSET=0 whereas first turn at N=1 here
# Right => 'A039963', # duplicated KochCurve turns
# },
# },
# }
}
{ package Math::PlanePath::SierpinskiCurveStair;
use constant _NumSeq_Turn_SLR_min => 1; # never straight
use constant _NumSeq_Turn_SRL_min => 1; # never straight
use constant _NumSeq_Turn_Turn4_min => 1; # never straight
}
{ package Math::PlanePath::DragonCurve;
use constant _NumSeq_Turn_SLR_min => 1; # never straight
use constant _NumSeq_Turn_SRL_min => 1; # never straight
use constant _NumSeq_Turn_Turn4_min => 1; # left or right only
use constant _NumSeq_Turn_oeis_anum =>
{ 'arms=1' =>
{
'LSR' => 'A034947', # Jacobi symbol (-1/n)
# OEIS-Catalogue: A034947 planepath=DragonCurve turn_type=LSR
Turn4 => 'A099545', # (odd part of n) mod 4
# OEIS-Catalogue: A099545 planepath=DragonCurve turn_type=Turn4
# 'L1R0' => 'A014577', # left=1,right=0 OFFSET=0
# 'L0R1' => 'A014707', # left=0,right=1 OFFSET=0
# 'L1R2' => 'A014709', # left=1,right=2 OFFSET=0
# 'L1R3' => 'A099545', # left=1,right=3 OFFSET=1
# # Not quite, A014707 has OFFSET=0 cf first elem for N=1
# Left => 'A014707', # turn, 1=left,0=right
# # OEIS-Catalogue: A014707 planepath=DragonCurve
# # Not quite, A014577 has OFFSET=0 cf first elem for N=1
# Right => 'A014577', # turn, 0=left,1=right
# # OEIS-Catalogue: A014577 planepath=DragonCurve turn_type=Right
# Not quite A014709 OFFSET=0 vs first turn at N=1 here
# SLR => 'A014709'
# SRL => 'A014710',
},
};
}
{ package Math::PlanePath::DragonRounded;
use constant _NumSeq_Turn_SLR_min => 1; # never straight
use constant _NumSeq_Turn_SRL_min => 1; # never straight
use constant _NumSeq_Turn_Turn4_min => 0.5;
use constant _NumSeq_Turn_Turn4_max => 3.5;
}
# { package Math::PlanePath::DragonMidpoint;
# }
{ package Math::PlanePath::AlternatePaper;
use constant _NumSeq_Turn_SLR_min => 1; # never straight
use constant _NumSeq_Turn_SRL_min => 1; # never straight
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
# # Not quite, A106665 has OFFSET=0 cf first here i=1
# 'Left' => 'A106665', # turn, 1=left,0=right
# # OEIS-Catalogue: A106665 planepath=AlternatePaper i_offset=1
},
};
}
{ package Math::PlanePath::GosperSide;
use constant _NumSeq_Turn_SLR_min => 1; # never straight
use constant _NumSeq_Turn_SRL_min => 1; # never straight
# Suspect not in OEIS:
# Left or Right according to lowest non-zero ternary digit 1 or 2
#
use constant _NumSeq_Turn_oeis_anum =>
{ '' =>
{ Left => 'A137893', # turn, 1=left,0=right, OFFSET=1
SLR => 'A060236', # base-3 lowest non-zero digit 1=left,2=right
# OEIS-Catalogue: A137893 planepath=GosperSide
# OEIS-Other: A137893 planepath=TerdragonCurve
# OEIS-Catalogue: A060236 planepath=GosperSide turn_type=SLR
# OEIS-Other: A060236 planepath=TerdragonCurve turn_type=SLR
# A060236 would also be a "TTurn3"
# cf A136442 - a(3n)=1, a(3n-1)=0, a(3n+1)=a(n)
# ternary lowest non-1 0->1 2->0
# Not quite, A080846 OFFSET=0 values 0,1,0,0,1 which are N=1 here
# Right => 'A080846',
# # OEIS-Catalogue: A080846 planepath=GosperSide turn_type=Right
# # OEIS-Other: A080846 planepath=TerdragonCurve turn_type=Right
# Or A189640 has extra initial 0.
},
};
}
{ package Math::PlanePath::TerdragonCurve;
# GosperSide and TerdragonCurve same turn sequence, by diff angles
use constant _NumSeq_Turn_SLR_min => 1; # never straight
use constant _NumSeq_Turn_SRL_min => 1; # never straight
use constant _NumSeq_Turn_Turn4_min => 1;
use constant _NumSeq_Turn_Turn4_max => 3;
use constant _NumSeq_Turn_oeis_anum =>
{ 'arms=1' => Math::PlanePath::GosperSide->_NumSeq_Turn_oeis_anum->{''} };
}
{ package Math::PlanePath::TerdragonRounded;
use constant _NumSeq_Turn_SLR_min => 1; # never straight
use constant _NumSeq_Turn_SRL_min => 1; # never straight
use constant _NumSeq_Turn_Turn4_min => 0.5;
use constant _NumSeq_Turn_Turn4_max => 3.5;
}
{ package Math::PlanePath::TerdragonMidpoint;
use constant _NumSeq_Turn_Turn4_max => 3;
}
{ package Math::PlanePath::R5DragonCurve;
use constant _NumSeq_Turn_SLR_min => 1; # never straight
use constant _NumSeq_Turn_SRL_min => 1; # never straight
use constant _NumSeq_Turn_Turn4_min => 1; # right or left turn always
# # Not quite, OFFSET=0 values 0,0,1,1,0
# # cf first turn here N=1 values 0,0,1,1,0
# # 'Math::PlanePath::R5DragonCurve' =>
# # { Right => 'A175337',
# # # OEIS-Catalogue: A175337 planepath=R5DragonCurve turn_type=Right
# # },
}
# { package Math::PlanePath::R5DragonMidpoint;
# }
{ package Math::PlanePath::CCurve;
# Not quite, A096268 OFFSET=1 vs first turn N=1 here
# Straight => 'A096268'
}
{ package Math::PlanePath::ComplexPlus;
sub _NumSeq_Turn_SLR_min {
my ($self) = @_;
return ($self->{'realpart'} == 1
? 1 # realpart=1 never straight
: 0);
}
*_NumSeq_Turn_SRL_min = \&_NumSeq_Turn_SLR_min;
}
{ package Math::PlanePath::ComplexMinus;
sub _NumSeq_Turn_SLR_min {
my ($self) = @_;
return ($self->{'realpart'} == 1
? 1 # realpart=1 never straight
: 0);
}
*_NumSeq_Turn_SRL_min = \&_NumSeq_Turn_SLR_min;
}
{ package Math::PlanePath::ComplexRevolving;
use constant _NumSeq_Turn_SLR_min => 1; # never straight
use constant _NumSeq_Turn_SRL_min => 1; # never straight
}
{ package Math::PlanePath::Rows;
# if width==1 then always straight ahead
sub _NumSeq_Turn_Left_max {
my ($self) = @_;
return ($self->{'width'} > 1
? 1
: 0);
}
sub _NumSeq_Turn_Left_non_decreasing {
my ($self) = @_;
return ($self->{'width'} > 1
? 0
: 1);
}
*_NumSeq_Turn_Right_max = \&_NumSeq_Turn_Left_max;
*_NumSeq_Turn_Right_non_decreasing = \&_NumSeq_Turn_Left_non_decreasing;
sub _NumSeq_Turn_LSR_min {
my ($self) = @_;
return ($self->{'width'} > 1
? -1
: 0);
}
sub _NumSeq_Turn_LSR_max {
my ($self) = @_;
return ($self->{'width'} > 1
? 1
: 0);
}
sub _NumSeq_Turn_LSR_non_decreasing {
my ($self) = @_;
return ($self->{'width'} > 1
? 0
: 1);
}
sub _NumSeq_Turn_SLR_min {
my ($self) = @_;
return ($self->{'width'} == 2
? 1 # width=2 never straight
: 0);
}
*_NumSeq_Turn_SRL_min = \&_NumSeq_Turn_SLR_min;
# 3---4 width=2
# \ N=2 turn4=1.5
# 1---2 N=3 turn4=2.5
sub _NumSeq_Turn_Turn4_min {
my ($self) = @_;
return ($self->{'width'} == 2
? 1.5 # at N=2
: 0); # otherwise has straight points
}
# *--------
# \---\
# *---------*
sub _NumSeq_Turn_Turn4_max {
my ($self) = @_;
return ($self->{'width'} <= 1
? 0 # width=1 always straight
: (Math::NumSeq::PlanePathTurn::_turn_func_Turn4
(1-$self->{'width'},1, 1,0))); # at row start
}
use constant _NumSeq_Turn_oeis_anum =>
{
'n_start=1,width=0' => # Rows width=0 is trivial X=N,Y=0
{ Left => 'A000004', # all-zeros
LSR => 'A000004', # all zeros, straight
# OEIS-Other: A000004 planepath=Rows,width=0
# OEIS-Other: A000004 planepath=Rows,width=0 turn_type=LSR
},
# 4 N=1 turn=2
# \ N=2 turn=4
# 2---3
# \
# 0---1
'n_start=-1,width=2' =>
{ TTurn6 => 'A010694', # repeat 2,4 with OFFSET=0
# OEIS-Other: A010694 planepath=Rows,width=2,n_start=-1 turn_type=TTurn6
},
};
}
{ package Math::PlanePath::Columns;
# if height==1 then always straight ahead
sub _NumSeq_Turn_Left_max {
my ($self) = @_;
return ($self->{'height'} > 1 ? 1 : 0);
}
sub _NumSeq_Turn_Left_non_decreasing {
my ($self) = @_;
return ($self->{'height'} > 1
? 0
: 1);
}
*_NumSeq_Turn_Right_max = \&_NumSeq_Turn_Left_max;
*_NumSeq_Turn_Right_non_decreasing = \&_NumSeq_Turn_Left_non_decreasing;
sub _NumSeq_Turn_LSR_min {
my ($self) = @_;
return ($self->{'height'} > 1 ? -1 : 0);
}
sub _NumSeq_Turn_LSR_max {
my ($self) = @_;
return ($self->{'height'} > 1 ? 1 : 0);
}
sub _NumSeq_Turn_LSR_non_decreasing {
my ($self) = @_;
return ($self->{'height'} > 1
? 0
: 1);
}
sub _NumSeq_Turn_SLR_min {
my ($self) = @_;
return ($self->{'height'} == 2
? 1 # height=2 never straight
: 0);
}
*_NumSeq_Turn_SRL_min = \&_NumSeq_Turn_SLR_min;
# 2 4 height=2
# | \ | N=2 turn4=2.5
# 1 3 N=3 turn4=1.5
sub _NumSeq_Turn_Turn4_min {
my ($self) = @_;
return ($self->{'height'} == 2
? 1.5 # at N=3
: 0); # otherwise has straight points
}
# *
# | \
# | |
# * *
sub _NumSeq_Turn_Turn4_max {
my ($self) = @_;
return ($self->{'height'} <= 1
? 0 # height=1 always straight
: (Math::NumSeq::PlanePathTurn::_turn_func_Turn4
(0,1, 1,1-$self->{'height'}))); # at column top
}
use constant _NumSeq_Turn_oeis_anum =>
{
'n_start=1,height=0' => # Columns height=0 is trivial X=N,Y=0
{ Left => 'A000004', # all-zeros
LSR => 'A000004', # all zeros, straight
# OEIS-Other: A000004 planepath=Columns,height=0
# OEIS-Other: A000004 planepath=Columns,height=0 turn_type=LSR
},
# 'n_start=-1,height=4' =>
# { Straight => 'A133872', # repeat 1,1,0,0 OFFSET=0
# NotStraight => 'A021913', # repeat 0,0,1,1 OFFSET=0
# # OEIS-Other: A133872 planepath=Columns,n_start=-1,height=4 turn_type=Straight
# # OEIS-Other: A021913 planepath=Columns,n_start=-1,height=4 turn_type=NotStraight
# },
};
}
{ package Math::PlanePath::Diagonals;
{
my %_NumSeq_Turn_Turn4_max
= (down => 2.5,
# at N=3 dx=-1,dy=+1 then dx=2,dy=-1
up => Math::NumSeq::PlanePathTurn::_turn_func_Turn4(-1,1, 2,-1));
sub _NumSeq_Turn_Turn4_max {
my ($self) = @_;
return $_NumSeq_Turn_Turn4_max{$self->{'direction'}};
}
}
use constant _NumSeq_Turn_oeis_anum =>
{ 'direction=down,n_start=0,x_start=0,y_start=0' =>
{ Left => 'A129184', # shift of triangle
SRL => 'A156319', # triangle 1, 2, 0, 0, 0, ... in each row OFFSET=1
# OEIS-Catalogue: A129184 planepath=Diagonals,n_start=0
# OEIS-Catalogue: A156319 planepath=Diagonals,n_start=0 turn_type=SRL
},
'direction=down,n_start=-1,x_start=0,y_start=0' =>
{ Right => 'A023531', # 1 at m(m+3)/2
# OEIS-Other: A023531 planepath=Diagonals,n_start=-1 turn_type=Right
},
'direction=up,n_start=0,x_start=0,y_start=0' =>
{ Right => 'A129184', # shift of triangle
SLR => 'A156319', # triangle 1, 2, 0, 0, 0, ... in each row OFFSET=1
# OEIS-Other: A129184 planepath=Diagonals,direction=up,n_start=0 turn_type=Right
# OEIS-Other: A156319 planepath=Diagonals,direction=up,n_start=0 turn_type=SLR
},
'direction=up,n_start=-1,x_start=0,y_start=0' =>
{ Left => 'A023531', # 1 at m(m+3)/2
# OEIS-Other: A023531 planepath=Diagonals,direction=up,n_start=-1
},
};
}
{ package Math::PlanePath::DiagonalsAlternating;
use constant _NumSeq_Turn_Turn4_max => 3.5;
}
{ package Math::PlanePath::DiagonalsOctant;
sub _NumSeq_Turn_Turn4_max {
my ($self) = @_;
return ($self->{'direction'} eq 'down'
? 2.5 # N=3
: 3); # N=2
}
# # down is left or straight, but also right at N=2,3,4
# # up is straight or right, but also left at N=2,3,4
# 'Math::PlanePath::DiagonalsOctant,direction=down' =>
# { Left => square or pronic starting from 1
# },
# 'Math::PlanePath::DiagonalsOctant,direction=up' =>
# { Left => square or pronic starting from 1
# },
}
{ package Math::PlanePath::Staircase;
use constant _NumSeq_Turn_Turn4_min => 1;
use constant _NumSeq_Turn_Turn4_max => 3;
}
# { package Math::PlanePath::StaircaseAlternating;
# }
{ package Math::PlanePath::MPeaks;
use constant _NumSeq_Turn_Turn4_max => 3;
}
{ package Math::PlanePath::Corner;
sub _NumSeq_Turn_Left_max {
my ($self) = @_;
return ($self->{'wider'} == 0 ? 0 # wider=0 right or straight always
: 1);
}
sub _NumSeq_Turn_Left_non_decreasing {
my ($self) = @_;
return ($self->{'wider'} == 0 ? 1 # wider=0 right or straight so left=0
: 0);
}
*_NumSeq_Turn_LSR_max = \&_NumSeq_Turn_Left_max;
use constant _NumSeq_Turn_Turn4_max => 3;
use constant _NumSeq_Turn_oeis_anum =>
{ 'wider=1,n_start=-1' =>
{ Left => 'A000007', # turn Left=1 at N=0 only
# catalogued only unless/until a better implementation
# OEIS-Catalogue: A000007 planepath=Corner,wider=1,n_start=-1
},
'wider=2,n_start=-1' =>
{ Left => 'A063524', # turn Left=1 at N=1 only
# catalogued only unless/until a better implementation
# OEIS-Catalogue: A063524 planepath=Corner,wider=2,n_start=-1
},
'wider=3,n_start=-1' =>
{ Left => 'A185012', # turn Left=1 at N=2 only
# catalogued only unless/until a better implementation
# OEIS-Catalogue: A185012 planepath=Corner,wider=3,n_start=-1
},
# A185013 Characteristic function of three.
# A185014 Characteristic function of four.
# A185015 Characteristic function of 5.
# A185016 Characteristic function of 6.
# A185017 Characteristic function of 7.
};
}
{ package Math::PlanePath::PyramidRows;
# if step==0 then always straight ahead
sub _NumSeq_Turn_Left_max {
my ($self) = @_;
return ($self->{'step'} > 0
? 1
: 0); # vertical only
}
sub _NumSeq_Turn_Left_non_decreasing {
my ($self) = @_;
return ($self->{'step'} > 0
? 0
: 1); # vertical only
}
*_NumSeq_Turn_Right_max = \&_NumSeq_Turn_Left_max;
*_NumSeq_Turn_Right_non_decreasing = \&_NumSeq_Turn_Left_non_decreasing;
sub _NumSeq_Turn_LSR_min {
my ($self) = @_;
return ($self->{'step'} > 0
? -1
: 0); # vertical only
}
sub _NumSeq_Turn_LSR_max {
my ($self) = @_;
return ($self->{'step'} > 0
? 1
: 0); # vertical only
}
sub _NumSeq_Turn_LSR_non_decreasing {
my ($self) = @_;
return ($self->{'step'} > 0
? 0
: 1); # vertical only
}
# *--* *---*
# | step=1 \ step=3
# * *
sub _NumSeq_Turn_Turn4_max {
my ($self) = @_;
return ($self->{'step'} == 0
? 0 # straight vertical only
# at N=2
: (Math::NumSeq::PlanePathTurn::_turn_func_Turn4
(- $self->{'left_slope'},1, 1,0)));
}
use constant _NumSeq_Turn_oeis_anum =>
{
# PyramidRows step=0 is trivial X=N,Y=0
do {
my $href= { Left => 'A000004', # all-zeros, OFFSET=0
LSR => 'A000004', # all zeros straight
};
('step=0,align=centre,n_start=1' => $href,
'step=0,align=right,n_start=1' => $href,
'step=0,align=left,n_start=1' => $href,
);
# OEIS-Other: A000004 planepath=PyramidRows,step=0
# OEIS-Other: A000004 planepath=PyramidRows,step=0 turn_type=LSR
# OEIS-Other: A000004 planepath=PyramidRows,step=0,align=right
# OEIS-Other: A000004 planepath=PyramidRows,step=0,align=left turn_type=LSR
},
# PyramidRows step=1
do {
my $href= { Left => 'A129184', # triangle 1s shift right
};
('step=1,align=centre,n_start=0' => $href,
'step=1,align=right,n_start=0' => $href,
'step=1,align=left,n_start=0' => $href,
);
# OEIS-Other: A129184 planepath=PyramidRows,step=1,n_start=0
# OEIS-Other: A129184 planepath=PyramidRows,step=1,align=right,n_start=0
# OEIS-Other: A129184 planepath=PyramidRows,step=1,align=left,n_start=0
},
do {
my $href= { Right => 'A023531', # 1 at n==m*(m+3)/2
};
('step=1,align=centre,n_start=-1' => $href,
'step=1,align=right,n_start=-1' => $href,
);
# OEIS-Other: A023531 planepath=PyramidRows,step=1,n_start=-1 turn_type=Right
# OEIS-Other: A023531 planepath=PyramidRows,step=1,align=right,n_start=-1 turn_type=Right
},
};
}
{ package Math::PlanePath::PyramidSides;
use constant _NumSeq_Turn_Left_max => 0; # right or straight
use constant _NumSeq_Turn_Left_non_decreasing => 1; # right or straight
use constant _NumSeq_Turn_LSR_max => 0; # right or straight
use constant _NumSeq_Turn_Turn4_max => 3; # at N=3
}
{ package Math::PlanePath::CellularRule;
sub _NumSeq_Turn_Left_increasing {
my ($self) = @_;
return (defined $self->{'rule'}
&& ($self->{'rule'} & 0x17) == 0 # single cell only
? 1
: 0);
}
*_NumSeq_Turn_Right_increasing = \&_NumSeq_Turn_Left_increasing;
sub _NumSeq_Turn_LSR_increasing {
my ($self) = @_;
return (defined $self->{'rule'}
&& ($self->{'rule'} & 0x17) == 0 # single cell only
? 1
: 0);
}
sub _NumSeq_Turn_SLR_min {
my ($self) = @_;
return (($self->{'rule'} & 0x5F) == 0x0E # left line 2
|| ($self->{'rule'} & 0x5F) == 0x54 # right line 2
? 1 # never straight
: 0);
}
*_NumSeq_Turn_SRL_min = \&_NumSeq_Turn_SLR_min;
}
{ package Math::PlanePath::CellularRule::Line;
use constant _NumSeq_Turn_Left_max => 0; # straight ahead only
use constant _NumSeq_Turn_Left_non_decreasing => 1; # straight ahead only
use constant _NumSeq_Turn_Right_max => 0; # straight ahead only
use constant _NumSeq_Turn_Right_non_decreasing => 1; # straight ahead only
use constant _NumSeq_Turn_LSR_max => 0;
use constant _NumSeq_Turn_LSR_min => 0;
use constant _NumSeq_Turn_LSR_non_decreasing => 1; # straight ahead only
use constant _NumSeq_Turn_Turn4_max => 0;
use constant _NumSeq_Turn_Turn4_integer => 1;
}
{ package Math::PlanePath::CellularRule::OneTwo;
use constant _NumSeq_Turn_SLR_min => 1; # never straight
use constant _NumSeq_Turn_SRL_min => 1; # never straight
# 5-6 5--6
# \ |
# 4 left rule=6 4 right rule=20
# ^----. N=2 turn4=2.5 / N=2 turn4=0.5
# 2--3 N=3 turn4=3.5 2--3 N=3 turn4=3
# \ N=4 turn4= |
# 1 1
sub _NumSeq_Turn_Turn4_min {
my ($self) = @_;
return ($self->{'align'} eq 'left'
? Math::NumSeq::PlanePathTurn::_turn_func_Turn4(1,0, -2,1) # N=3
: 0.5);
}
sub _NumSeq_Turn_Turn4_max {
my ($self) = @_;
return ($self->{'align'} eq 'left'
? Math::NumSeq::PlanePathTurn::_turn_func_Turn4(-2,1, -1,1) # N=4
: 3);
}
use constant _NumSeq_Turn_oeis_anum =>
{ 'align=left,n_start=-1' =>
{ SRL => 'A131534', # repeat 1,2,1, 1,2,1, ... OFFSET=0
# OEIS-Catalogue: A131534 planepath=CellularRule,rule=6,n_start=-1 turn_type=SRL
# OEIS-Other: A131534 planepath=CellularRule,rule=38,n_start=-1 turn_type=SRL
# OEIS-Other: A131534 planepath=CellularRule,rule=134,n_start=-1 turn_type=SRL
# OEIS-Other: A131534 planepath=CellularRule,rule=166,n_start=-1 turn_type=SRL
},
'align=right,n_start=-1' =>
{ SRL => 'A130196', # repeat 1,2,2, 1,2,2, ... OFFSET=0
# OEIS-Catalogue: A130196 planepath=CellularRule,rule=20,n_start=-1 turn_type=SRL
# OEIS-Other: A130196 planepath=CellularRule,rule=52,n_start=-1 turn_type=SRL
# OEIS-Other: A130196 planepath=CellularRule,rule=148,n_start=-1 turn_type=SRL
# OEIS-Other: A130196 planepath=CellularRule,rule=180,n_start=-1 turn_type=SRL
},
};
}
{ package Math::PlanePath::CellularRule::Two;
use constant _NumSeq_Turn_SLR_min => 1; # never straight
use constant _NumSeq_Turn_SRL_min => 1; # never straight
# 5--6 6--7
# ^---. |
# 4--5 left rule=6 4--5 right rule=84
# ^----. N=2 turn4=2.5 | N=2 turn4=1
# 2--3 N=3 turn4= 2--3 N=3 turn4=3
# \ |
# 1 1
sub _NumSeq_Turn_Turn4_min {
my ($self) = @_;
return ($self->{'align'} eq 'left'
? Math::NumSeq::PlanePathTurn::_turn_func_Turn4(1,0, -2,1) # N=3
: 1);
}
sub _NumSeq_Turn_Turn4_max {
my ($self) = @_;
return ($self->{'align'} eq 'left'
? 2.5
: 3);
}
use constant _NumSeq_Turn_oeis_anum =>
{ 'align=right,n_start=-1' =>
{
# right line 2, stair step
# |
# 3--1
# |
# 3--1 Turn4 amounts
# |
# *
Turn4 => 'A176040', # 3,1 repeating OFFSET=0
},
# OEIS-Catalogue: A176040 planepath=CellularRule,rule=84,n_start=-1 turn_type=Turn4
# OEIS-Other: A176040 planepath=CellularRule,rule=116,n_start=-1 turn_type=Turn4
# OEIS-Other: A176040 planepath=CellularRule,rule=212,n_start=-1 turn_type=Turn4
# OEIS-Other: A176040 planepath=CellularRule,rule=244,n_start=-1 turn_type=Turn4
};
}
{ package Math::PlanePath::CellularRule::OddSolid;
use constant _NumSeq_Turn_Turn4_max => 2.5; # at N=2
# R 0 0 L 1 0 0 2
# R 0 L 1 0 2
# R L 1 2
# . .
use constant _NumSeq_Turn_oeis_anum =>
{ 'n_start=0' =>
{ SRL => 'A156319', # triangle rows 1,2,0,0,0,0,...
# OEIS-Other: A156319 planepath=CellularRule,rule=50,n_start=0 turn_type=SRL
# OEIS-Other: A156319 planepath=CellularRule,rule=58,n_start=0 turn_type=SRL
# OEIS-Other: A156319 planepath=CellularRule,rule=250,n_start=0 turn_type=SRL
# OEIS-Other: A156319 planepath=CellularRule,rule=179,n_start=0 turn_type=SRL
},
};
}
{ package Math::PlanePath::CellularRule54;
use constant _NumSeq_Turn_Turn4_max => 2.5;
}
{ package Math::PlanePath::CellularRule57;
sub _NumSeq_Turn_Turn4_max {
my ($self) = @_;
return ($self->{'mirror'}
? 3.5
: Math::NumSeq::PlanePathTurn::_turn_func_Turn4(-2,1, 2,0)); # N=2
}
}
{ package Math::PlanePath::CellularRule190;
use constant _NumSeq_Turn_Turn4_max => 2.5;
}
# { package Math::PlanePath::CoprimeColumns;
# }
# { package Math::PlanePath::DivisibleColumns;
# }
# { package Math::PlanePath::File;
# # File points from a disk file
# # FIXME: analyze points for min/max etc
# }
{ package Math::PlanePath::QuintetCurve;
use constant _NumSeq_Turn_Turn4_max => 3;
}
{ package Math::PlanePath::QuintetCentres;
use constant _NumSeq_Turn_Turn4_max => 3.5;
}
# { package Math::PlanePath::QuintetSide;
# PlanePathTurn planepath=QuintetSide, turn_type=SLR
# match 1,2,1,1,2,2,1,2,1,1,2,1,1,2,2,1,2,2,1,2,1,1,2,2,1,2
# A060236 If n mod 3 = 0 then a(n)=a(n/3), otherwise a(n)=n mod 3.
# A060236 ,1,2,1,1,2,2,1,2,1,1,2,1,1,2,2,1,2,2,1,2,1,1,2,2,1,2,1,1,2,1,1,2,2,1,2,1,1,2,1,1,2,2,1,2,2,1,2,1,1,2,2,1,2,2,1,2,1,1,2,2,1,2,1,1,2,1,1,2,2,1,2,2,1,2,1,1,2,2,1,2,1,1,2,1,1,2,2,1,2,1,1,2,1,1,2,2,1,2,2,1,2,1,1,2,2,
# }
# { package Math::PlanePath::DekkingCurve;
# }
# { package Math::PlanePath::DekkingCentres;
# }
# { package Math::PlanePath::CincoCurve;
# }
{ package Math::PlanePath::CornerReplicate;
use constant _NumSeq_Turn_SLR_min => 1; # never straight
use constant _NumSeq_Turn_SRL_min => 1; # never straight
use constant _NumSeq_Turn_Turn4_min => # apparent minimum
Math::NumSeq::PlanePathTurn::_turn_func_Turn4(-1,0, -2,-1); # at N=11
use constant _NumSeq_Turn_Turn4_max => 3; # apparent maximum
}
{ package Math::PlanePath::SquareReplicate;
use constant _NumSeq_Turn_Turn4_max =>
Math::NumSeq::PlanePathTurn::_turn_func_Turn4(2,1, 0,1); # at N=9
}
{ package Math::PlanePath::DigitGroups;
# radix=3 "11110222222" len many 1s, 2*len-2 many 2s gives ever-increasing
# radix=4 "1303333...3333" ever-increasing
}
{ package Math::PlanePath::FibonacciWordFractal;
use constant _NumSeq_Turn_oeis_anum =>
{ '' =>
{ SRL => 'A156596', # turns 0=straight,1=right,2=left
# OEIS-Catalogue: A156596 planepath=FibonacciWordFractal turn_type=SRL
# Not quite, A003849 OFFSET=0 vs first turn N=1 here
# Straight => 'A003849'
},
};
}
{ package Math::PlanePath::LTiling;
{
my %_NumSeq_Turn_SLR_min = (middle => 1,
upper => 1,
);
sub _NumSeq_Turn_SLR_min {
my ($self) = @_;
return ($_NumSeq_Turn_SLR_min{$self->{'L_fill'}} || 0);
}
}
*_NumSeq_Turn_SRL_min = \&_NumSeq_Turn_SLR_min;
}
{ package Math::PlanePath::WythoffArray;
use constant _NumSeq_Turn_Turn4_max => # apparent maximum
Math::NumSeq::PlanePathTurn::_turn_func_Turn4(-2,3, 5,-4); # at N=12
}
{ package Math::PlanePath::WythoffPreliminaryTriangle;
# apparent maximum, searched through to N=10_000_000
# turn4 = 3.17777777777778
use constant _NumSeq_Turn_Turn4_max =>
Math::NumSeq::PlanePathTurn::_turn_func_Turn4(-54,-16, -8,13); # at N=1344
}
{ package Math::PlanePath::PowerArray;
sub _NumSeq_Turn_SLR_min {
my ($self) = @_;
return ($self->{'radix'} == 3
? 1 # radix=3 never straight
: 0);
}
*_NumSeq_Turn_SRL_min = \&_NumSeq_Turn_SLR_min;
# Turn4 ...
#
# radix=2
# min i=2[10] 1.50000 px=1,py=0 dx=-1,dy=1 -1.000
# max i=3[11] 2.20000 px=-1,py=1 dx=2,dy=-1[10,-1] -2.000
#
# radix=3
# min i=130[11211] 0.00000 px=-1,py=58 dx=0,dy=1 0.000
# max i=67[2111] 3.98889 px=-1,py=30 dx=0,dy=1[0,1] 0.000
#
# radix=4
# min i=2[2] 0.00000 px=0,py=1 dx=0,dy=1 0.000
# max i=53[311] 3.98889 px=-1,py=30 dx=0,dy=1[0,1] 0.000
#
# radix=5
# min i=2[2] 0.00000 px=0,py=1 dx=0,dy=1 0.000
# max i=46[141] 3.98889 px=-1,py=29 dx=0,dy=1[0,1] 0.000
#
# radix=6
# min i=2[2] 0.00000 px=0,py=1 dx=0,dy=1 0.000
# max i=43[111] 3.98889 px=-1,py=30 dx=0,dy=1[0,1] 0.000
#
# radix=7
# min i=2[2] 0.00000 px=0,py=1 dx=0,dy=1 0.000
# max i=43[61] 3.98889 px=-1,py=31 dx=0,dy=1[0,1] 0.000
#
# radix=8
# min i=2[2] 0.00000 px=0,py=1 dx=0,dy=1 0.000
# max i=41[51] 3.98889 px=-1,py=31 dx=0,dy=1[0,1] 0.000
#
# radix=9
# min i=2[2] 0.00000 px=0,py=1 dx=0,dy=1 0.000
# max i=37[41] 3.98889 px=-1,py=29 dx=0,dy=1[0,1] 0.000
#
# radix=10
# min i=2[2] 0.00000 px=0,py=1 dx=0,dy=1 0.000
# max i=41[41] 3.98889 px=-1,py=33 dx=0,dy=1[0,1] 0.000
#
# radix=16
# min i=2[2] 0.00000 px=0,py=1 dx=0,dy=1 0.000
# max i=33[21] 3.98889 px=-1,py=29 dx=0,dy=1[0,1] 0.000
#
# radix=29
# min i=2[2] 0.00000 px=0,py=1 dx=0,dy=1 0.000
# max i=59[21] 3.98889 px=-1,py=55 dx=0,dy=1[0,1] 0.000
# use constant _NumSeq_oeis_anum =>
# # Not quite, A011765 0,0,0,1 repeating has OFFSET=1
# # cf n_start=1 is first turn at N=2
# # Left => 'A011765',
# # Right => 'A011765',
#
# # Not quite, A131534 has OFFSET=1 vs first turn at N=2 here
# # 'radix=3' =>
# # { SRL => 'A131534', # repeat 1,2,1, OFFSET=0
# # }
#
# # Not quite, A007877 has OFFSET=1 vs first turn at N=2 here
# # 'radix=4' =>
# # { SRL => 'A007877', # repeat 0,1,2,1
# # }
#
# # Not quite, 0,0,2,1,2 here vs A053796 0,2,1,2,0
# # 'radix=5' =>
# # { SRL => 'A053796', # repeat 0,2,1,2,0
# # }
# };
}
{ package Math::PlanePath::ToothpickTree;
{
my %_NumSeq_Turn_Turn4_max
= (wedge => 3, # at N=1 turn right
);
sub _NumSeq_Turn_Turn4_max {
my ($self) = @_;
return $_NumSeq_Turn_Turn4_max{$self->{'parts'}} || 4;
}
}
{
my %_NumSeq_Turn4_max_is_supremum
= (wedge => 0,
);
sub _NumSeq_Turn4_max_is_supremum {
my ($self) = @_;
my $ret = $_NumSeq_Turn4_max_is_supremum{$self->{'parts'}};
return (defined $ret ? $ret : 1);
}
}
}
{ package Math::PlanePath::ToothpickReplicate;
{
my %_NumSeq_Turn_Turn4_max
= ( # at N=16
'3' => Math::NumSeq::PlanePathTurn::_turn_func_Turn4(-1,3, 0,1),
);
sub _NumSeq_Turn_Turn4_max {
my ($self) = @_;
return $_NumSeq_Turn_Turn4_max{$self->{'parts'}} || 3.5;
}
}
}
{ package Math::PlanePath::ToothpickUpist;
use constant _NumSeq_Turn_Turn4_max =>
Math::NumSeq::PlanePathTurn::_turn_func_Turn4(-2,1, 2,0); # at N=4
}
{ package Math::PlanePath::ToothpickSpiral;
use constant _NumSeq_Turn_SLR_min => 1; # never straight
use constant _NumSeq_Turn_SRL_min => 1; # never straight
use constant _NumSeq_Turn_Turn4_min => 1; # left or right always
}
{ package Math::PlanePath::LCornerReplicate;
# min i=63[333] 0.25556 px=1,py=0 dx=7,dy=3 2.333
use constant _NumSeq_Turn_Turn4_min =>
Math::NumSeq::PlanePathTurn::_turn_func_Turn4(1,0, 7,3); # at N=63
# higher N=1333..3333[base4] loop 3.8, 2.8, 1.7888, 0.7888
# max i=8191[1333333] 3.80000 px=-1,py=0 dx=-38,dy=13[-212,31] -2.923
use constant _NumSeq_Turn_Turn4_max =>
Math::NumSeq::PlanePathTurn::_turn_func_Turn4(-1,0, -38,13); # at N=8191
}
{ package Math::PlanePath::LCornerTree;
# parts=3 maybe maximum
# max i=3107[300203] 3.98889 px=1,py=0 dx=66,dy=-1[1002,-1] -66.000
# LCornerTree,parts=diagonal-1 Turn4 values_max=4 vs saw_values_max=3 at i=27 (to i_end=801)
{
my %_NumSeq_Turn_Turn4_max
= (wedge => 3, # at N=14
'wedge+1' => 3, # at N=19
diagonal => 3, # at N=21
'diagonal-1' => 3, # at N=27
);
sub _NumSeq_Turn_Turn4_max {
my ($self) = @_;
return $_NumSeq_Turn_Turn4_max{$self->{'parts'}} || 4;
}
}
{
my %_NumSeq_Turn4_max_is_supremum
= (4 => 0, # apparently
);
sub _NumSeq_Turn4_max_is_supremum {
my ($self) = @_;
return $_NumSeq_Turn4_max_is_supremum{$self->{'parts'}};
}
}
}
1;
__END__
=for stopwords Ryde Math-PlanePath NumSeq PlanePath SquareSpiral ie LSR dX,dY dx1,dy1 dx2,dy2 dx1 dx2 SRL dX
=head1 NAME
Math::NumSeq::PlanePathTurn -- turn sequence from PlanePath module
=head1 SYNOPSIS
use Math::NumSeq::PlanePathTurn;
my $seq = Math::NumSeq::PlanePathTurn->new (planepath => 'DragonCurve',
turn_type => 'Left');
my ($i, $value) = $seq->next;
=head1 DESCRIPTION
This is a tie-in to present turns from a C<Math::PlanePath> module in the
form of a NumSeq sequence.
The C<turn_type> choices are
"Left" 1=left 0=right or straight
"Right" 1=right 0=left or straight
"LSR" 1=left 0=straight -1=right
"SLR" 0=straight 1=left 2=right
"SRL" 0=straight 1=right 2=left
In each case the value at i is the turn which occurs at N=i,
i+1
^
|
|
i-1 ---> i turn at i
first turn at i = n_start + 1
For multiple "arms" the turn follows that particular arm so it's i-arms, i,
i+arms. i values start C<n_start()+arms_count()> so i-arms is C<n_start()>,
the first N on the path. A single arm path beginning N=0 has its first turn
at i=1.
For "LSR", "SLR" and "SRL", straight means either straight ahead or
180-degree reversal, ie. the direction N to N+1 is along the same line as
N-1 to N was.
"Left" means to the left side of the N-1 to N line, not straight or right.
Similarly "Right" means to the right side of the N-1 to N line, not straight
or left.
=head1 FUNCTIONS
See L<Math::NumSeq/FUNCTIONS> for behaviour common to all sequence classes.
=over 4
=item C<$seq = Math::NumSeq::PlanePathTurn-E<gt>new (key=E<gt>value,...)>
Create and return a new sequence object. The options are
planepath string, name of a PlanePath module
planepath_object PlanePath object
turn_type string, as described above
C<planepath> can be either the module part such as "SquareSpiral" or a
full class name "Math::PlanePath::SquareSpiral".
=item C<$value = $seq-E<gt>ith($i)>
Return the turn at N=$i in the PlanePath.
=item C<$bool = $seq-E<gt>pred($value)>
Return true if C<$value> occurs as a turn. Often this is merely the
possible turn values 1,0,-1, etc, but some spiral paths for example only go
left or straight in which case only 1 and 0 occur and C<pred()> reflects
that.
=item C<$i = $seq-E<gt>i_start()>
Return the first index C<$i> in the sequence. This is the position
C<rewind()> returns to.
This is C<$path-E<gt>n_start() - $path-E<gt>arms_count()> from the
PlanePath object.
=back
=head1 FORMULAS
=head2 Turn Left or Right
A turn left or right is identified by considering the dX,dY at N-1 and at N.
N+1 *
|
|
| dx2,dy2
|
N *
/
/
/ dx1,dy1
N-1 *
With the two vectors dx1,dy1 and dx2,dy2 at a common origin, if the dx2,dy2
is above the dx1,dy1 line then it's a turn to the left, or below is a turn
to the right
dx2,dy2
*
| * dx1,dy1
| /
| /
|/
o
At dx2 the Y value of the dx1,dy1 vector is
cmpY = dx2 * dy1/dx1 if dx1 != 0
left if dy2 > cmpY
dy2 > dx2 * dy1/dx1
so dy2 * dx1 > dx2 * dy1
This cross-product comparison dy2*dx1 E<gt> dx2*dy1 works when dx1=0 too,
ie. when dx1,dy1 is vertical
left if dy2 * 0 > dx2 * dy1
0 > dx2*dy1
good, left if dx2 and dy1 opposite signs
So
dy2*dx1 > dx2*dy1 left
dy2*dx1 < dx2*dy1 right
dy2*dx1 = dx2*dy1 straight, including 180 degree reverse
=head1 SEE ALSO
L<Math::NumSeq>,
L<Math::NumSeq::PlanePathCoord>,
L<Math::NumSeq::PlanePathDelta>,
L<Math::NumSeq::PlanePathN>
L<Math::NumberCruncher> has a C<Clockwise()> turn calculator
=head1 HOME PAGE
L<http://user42.tuxfamily.org/math-planepath/index.html>
=head1 LICENSE
Copyright 2011, 2012, 2013, 2014 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/>.
=cut