# Copyright 2017 Kevin Ryde
#
# This file is part of Graph-Maker-Other.
#
# This file 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.
#
# This file 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 Graph-Maker-Other.  See the file COPYING.  If not, see
# <http://www.gnu.org/licenses/>.

package Graph::Maker::TwinAlternateAreaTree;
use 5.004;
use strict;
use Carp 'croak';
use Graph::Maker;

use vars '$VERSION','@ISA';
$VERSION = 8;
@ISA = ('Graph::Maker');

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


sub _default_graph_maker {
  require Graph;
  Graph->new(@_);
}
sub _vertex_name_func_decimal {
  my ($v) = @_;
  return $v;
}
sub _vertex_name_func_binary {
  my ($v) = @_;
  return sprintf '%b', $v;
}
sub _vertex_name_func_xy {
  my ($v) = @_;
  my $x = 0;
  my $y = 0;
  my $bit = 1;
  while ($v) {
    if ($v & 1) { $x |= $bit; }
    $v >>= 1;
    if ($v & 1) { $y |= $bit; }
    $v >>= 1;
    $bit <<= 1;
  }
  $x = $y-$x;
  return "$x,$y";
}
sub init {
  my ($self, %params) = @_;

  my $level = delete($params{level}) || 0;
  my $graph_maker = delete($params{'graph_maker'}) || \&_default_graph_maker;

  # undocumented parameter
  my $vertex_name_type = delete($params{'vertex_name_type'}) || 'decimal';
  my $vertex_names_func = $self->can("_vertex_name_func_$vertex_name_type")
    || croak 'Unrecognised vertex_name_type: ',$vertex_name_type;

  ### $level
  ### $vertex_name_type

  my $graph = $graph_maker->(%params);
  $graph->set_graph_attribute(name => "Twin Alternate Area Tree $level");
  $graph->set_graph_attribute(vertex_name_type => $vertex_name_type);
  $graph->add_vertex($vertex_names_func->(0));
  my $directed = $graph->is_directed;

 V: foreach my $v (0 .. 2**$level-1) {
    ### $v

    # ...1 edge to ...0
    if ($v & 1) {
      ### horizontal ...
      my $to = $v ^ 1;
      my $v_name = $vertex_names_func->($v);
      my $to_name = $vertex_names_func->($to);
      $graph->add_edge($v_name, $to_name);
      if ($directed) { $graph->add_edge($to_name, $v_name); }
    }

    #  ...00 11...11
    #        \-----/ zero or more low 1 bits
    # edge to
    #  ...11 00...00
    #
    my $bit  = 1;
    for (my $pos = 1; ; $pos++, $bit<<=1) {
      $pos < $level or next V;
      next if $v & $bit;  # still all 1s
      unless ($v & ($bit<<1)) {  # 00 11..11
        my $to = $v+1+($bit<<1);

        my $v_name = $vertex_names_func->($v);
        my $to_name = $vertex_names_func->($to);
        $graph->add_edge($v_name, $to_name);
        if ($directed) { $graph->add_edge($to_name, $v_name); }
      }
      last;
    }
  }
  return $graph;
}

Graph::Maker->add_factory_type('twin_alternate_area_tree' => __PACKAGE__);
1;
__END__


# base i+1
#         ...0   <-->   ...1            toggle low bit
#
# ...01 00..00   <-->   ...10 11..11
#       \----/                \----/
#
# base i-1  bit flip every second starting from second lowest
#
# ...11 10..10    <-->   ...00 01..01    even low run
# ...00 010..10   <-->   ...11 101..01   odd low run
#    flip lowest triplet 000 or 111 and all below
#
# base i-1  bit flip every second starting from lowest
#
# ...00 01..01   <-->   ...11 10..10     even low run
# ...11 101..01  <-->   ...00 010..10     odd low run
#    flip lowest triplet 000 or 111 and all below



=for stopwords Ryde chamfered undirected McWorter Tazelaar

=head1 NAME

Graph::Maker::TwinAlternateAreaTree - create twin alternate area tree graph

=for test_synopsis my ($graph)

=head1 SYNOPSIS

 use Graph::Maker::TwinAlternateAreaTree;
 $graph = Graph::Maker->new ('twin_alternate_area_tree', level => 6);

=head1 DESCRIPTION

C<Graph::Maker::TwinAlternateAreaTree> creates a C<Graph.pm> graph of a
twin alternate area tree.

=cut

# this picture generated by code in devel/twin-alternate-area-tree.pl

=pod

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

Graph vertices are unit squares inside a twin alternate curve formed by two
alternate paperfolding curves back-to-back, or equivalently a cycle of four
such curves.  For level=k the twin alternate is four level k paperfolding
curves curves.  It has 2^k squares inside which are vertices.  Edges are
between squares with segments consecutive in the curve.  Or equivalently if
the curves are drawn with corners chamfered off to leave little gaps at
corners then edges connect squares through those gaps.  See the author's
mathematical write-up for more

=over

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

=back

Vertices are numbered 0 to 2^level-1.  These are Z-order point numbers
corresponding to the unit squares in the twin alternate.  The layout above
is Y-X + Y*i, so Y coordinate unchanged and horizontally Y-X shear and
negate.

With this vertex numbering, edges are between bit patterns

    xxx0  --- xxx1       horizontal, toggle low bit

    xxx 11 00..00
        \-------/        vertical, toggle low run + 2
            |            when respective bit pattern
        /-------\
    xxx 00 11..11

Level=0 is a single vertex 0 and no edges.

Level=1 is two vertices 0 and 1 and an edge between them by the horizontal
toggle low bit rule.

            0---1          level => 1

Level=2 has the vertical rule connecting vertices 0 = 00 binary and 3 = 11
binary.  These are 11 with no trailing 0s, connected to 00 with no trailing
1s.

                3---2
                |             level => 2
            1---0

Level=3 is also still a path.  Its middle vertical is 1 = 001 connected to 6
= 110 so a low run of one bit before the 00 - 11.

        7---6   3---2
        |   |   |              level => 3
    5---4   1---0

Level=4 is the first not simply a path.  The new edge is 3 = binary 0011 up
to 12 = binary 1100.

               15--14  11--10
                |   |   |
           13--12   9---8      level => 4
                |
        7---6   3---2
        |   |   |
    5---4   1---0

Vertices are degree 1, 2 or 3.  Vertex 0 can be considered as the root.
Level k+1 is formed by taking level k and extending with a second copy of
level k connected by a middle edge.  This is between bit patterns 0011..11
in the first copy and 1100..00 in the second, per the level=4 above.  In the
big level=6 example above this is vertices 15 to 48.

=for GP-Test  binary(15) == [    1,1,1,1]

=for GP-Test  binary(48) == [1,1,0,0,0,0]

Each level is the previous plus a copy of that previous attached at the
biggest bit pattern edge.  The effect is to make two spines continuing
infinitely.  One spine is the verticals 0,3,12,15,etc, the other is the
North West stair-step 0,1,6,7,24,etc.  The vertical and vertices branching
off from it are N with an even length in binary.  The NW and vertices
branching off from it are N with an odd length in binary.

=cut

# =head2 Coordinates
#
# Vertex numbers can be converted to X,Y coordinates using
# C<Math::PlanePath::ZOrderCurve>.
#
#     use Math::PlanePath::ZOrderCurve;
#     my $path = Math::PlanePath::ZOrderCurve->new;
#     my $graph = Graph::Maker->new ('twin_alternate_area_tree', level=>5);
#     foreach my $edge ($graph->edges) {
#       my ($v1,$v2) = @$edge;
#       my ($x1,$y1) = $path->n_to_xy($v1);  $x1 = $y1 - $x1;
#       my ($x2,$y2) = $path->n_to_xy($v2);  $x2 = $y2 - $x2;
#       # draw an edge from ($x1,$y1) to ($x2,$y2) ...
#     }
#
# This is the layout of the samples above.  Edges are unit lengths horizontal
# or vertical and do not cross.  Of course there's many other layouts
# possible.  Mirror image horizontally to X = Y-X is just a shear of the Z
# order for example.

=pod

=head1 FUNCTIONS

=over

=item C<$graph = Graph::Maker-E<gt>new('twin_alternate_area_tree', key =E<gt> value, ...)>

The key/value parameters are

    level       => integer >= 0
    graph_maker => subr(key=>value) constructor, default Graph->new

Other parameters are passed to the constructor, either C<graph_maker> or
C<Graph-E<gt>new()>.

Like C<Graph::Maker::BalancedTree>, if the graph is directed (the default)
then edges are added in both directions between nodes.  Option C<undirected
=E<gt> 1> creates an undirected graph and for it there is a single edge
between vertices.

=back

=head1 HOUSE OF GRAPHS

House of Graphs entries for the graphs here include

=over

=item level=0, L<https://hog.grinvin.org/ViewGraphInfo.action?id=1310>  (single vertex)

=item level=1, L<https://hog.grinvin.org/ViewGraphInfo.action?id=19655>  (path-2)

=item level=2, L<https://hog.grinvin.org/ViewGraphInfo.action?id=594>  (path-4)

=item level=3, L<https://hog.grinvin.org/ViewGraphInfo.action?id=260>  (path-8)

=item level=4, L<https://hog.grinvin.org/ViewGraphInfo.action?id=27042>

=item level=5, L<https://hog.grinvin.org/ViewGraphInfo.action?id=27044>

=item level=6, L<https://hog.grinvin.org/ViewGraphInfo.action?id=27046>

=back

=head1 OEIS

Entries in Sloane's Online Encyclopedia of Integer Sequences related to
this tree include

=over

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

=back

    A053599    diameter of level
    A077866    height of level
    A001196    vertices of vertical spine
    A053754    vertices of vertical spine and branches from it,
                 being N binary even length
    A053738    vertices of NW spine and branches from it,
                 being N binary odd length

=head1 SEE ALSO

L<Graph::Maker>,
L<Graph::Maker::TwindragonAreaTree>,
L<Graph::Maker::BalancedTree>

L<Math::PlanePath::AlternatePaper>

=head1 LICENSE

Copyright 2017 Kevin Ryde

This file 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.

This file 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
This file.  If not, see L<http://www.gnu.org/licenses/>.

=cut
