#!/usr/bin/perl -w

# Copyright 2015 Kevin Ryde
#
# This file is part of Graph-Graph6.
#
# Graph-Graph6 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.
#
# Graph-Graph6 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-Graph6.  If not, see <http://www.gnu.org/licenses/>.

use strict;
use 5.010;
use File::Slurp;
use Graph;
use Graph::Writer::Graph6;
use Graph::Easy;
use Graph::Easy::As_graph6;
use Graph::Easy::Parser::Graph6;
use Graph::Graph6;
use IPC::Run;

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



{
  # Konigsberg bridges
  #
  #     ---0---1-----2---
  #               |
  #               3
  #               |
  #     ---4---5-----6---
  #
  # plain https://hog.grinvin.org/ViewGraphInfo.action?id=28
  # dual not
  #
  my $filename = '/tmp/2.g6';
  my $edge_aref = [ [0,1],[0,2],  [0,3], [0,4],[0,5],
                    [1,2],[1,3], [1,4],[1,5],
                    [2,3],[2,6],
                    [3,4],[3,5],[3,6],
                    [4,5],[4,6],
                    [5,6] ];
  Graph::Graph6::write_graph(filename => $filename,
                             edge_aref => $edge_aref)
      or die $!;
  my $parser = Graph::Easy::Parser::Graph6->new;
  my $easy = $parser->from_file($filename);
  my $graphviz_str = $easy->as_graphviz;
  File::Slurp::write_file('/tmp/2.dot', $graphviz_str) or die;
  IPC::Run::run(['dot', '-Tpng', '/tmp/2.dot', '-o', '/tmp/2.png']);
  # if ($ENV{'DISPLAY'}) {
  #   IPC::Run::run(['xdot', '/tmp/2.dot'], '&');
  # }

  $easy = Graph::Easy->new;
  $easy->add_edge('A','C', '0');
  $easy->add_edge('A','C', '1');
  $easy->add_edge('C','D', '2');
  $easy->add_edge('A','D', '3');
  $easy->add_edge('A','B', '4');
  $easy->add_edge('A','B', '5');
  $easy->add_edge('B','D', '6');
  my $g6_land_str = $easy->as_graph6;
  File::Slurp::write_file('/tmp/3.g6', $g6_land_str) or die;
  $graphviz_str = $easy->as_graphviz;
  File::Slurp::write_file('/tmp/3.dot', $graphviz_str) or die;
  IPC::Run::run(['dot', '-Tpng', '/tmp/3.dot', '-o', '/tmp/3.png']);
  print $easy->as_ascii;

  my $dual = Graph::Easy->new;
  foreach my $from_edge ($easy->edges) {
    my $from_name = $from_edge->name;
    foreach my $to_edge ($from_edge->from->edges,
                         $from_edge->to->edges) {
      my $to_name = $to_edge->name;
      if ($from_name ne $to_name) {
        $dual->add_edge($from_name, $to_name);
      }
    }
  }

  Graph::Graph6::write_graph(str_ref => \my $g6_str,
                             edge_aref => $edge_aref)
      or die $!;
  my $dual_g6 = $dual->as_graph6;
  print $g6_str;
  print $dual_g6;
  print g6_str_to_canonical($g6_str);
  print g6_str_to_canonical($dual_g6);

  exit 0;

  sub Graph_Easy_Edge_name {
    my ($edge) = @_;
    return $edge->from->name . $edge->to->name;
  }
}

{
  # count trees no square grid layout
  # A000055 trees n nodes
  # A144520 trees n nodes degree <= n-2, so skip star-n A000055-1
  # A144527 trees n nodes degree <= n-3,  A000055-2
  # not     trees n nodes degree <= n-4
  # A000602 trees n nodes degree 1,4  2     5     8    11    14    17    20
  #                       sans zeros  1,0,0,1,0,0,1,0,0,1,0,0,2,0,0,3,0,0,5
  # A000672 trees degree 1,2,3
  # not     trees degree 1,2,4
  #         trees degree 1,2,3,4 not square grid n=12  1,4,14,43,136
  #         trees degree 1,2,  4 not square grid n=14  1,2,5,13,30,69,162
  #         trees degree 1,    4 not square grid n=14 1of2  n=17 2of3
  # 1,2,3 always square grid?

  my @values;
  foreach my $num_vertices (3..17) {
    my $iterator_func = make_tree_iterator_edge_aref
      (num_vertices_min => $num_vertices,
       num_vertices_max => $num_vertices,
       # degree_max => $num_vertices-4,
       degree_list => [
                       1,4,
                       # ,3,4
                      ],
      );
    my $count = 0;
    GRAPH: while (my $edge_aref = $iterator_func->()) {
      my $parent_aref = edge_aref_to_parent_aref($edge_aref);

      {
        my $layout = grid_layout($parent_aref);
        if ($layout) {
          # grid_layout_check ($edge_aref, $parent_aref, $layout);
          next GRAPH;
        } else {
          # print "Cannot layout\n";
        }
      }

      $count++;
    }
    push @values, $count;
    print "n=$num_vertices  $count\n";
  }
  require Math::OEIS::Grep;
  Math::OEIS::Grep->search(array => \@values, verbose=>1);
  exit 0;
}
{
  # 12 vertices no square grid layout
  # https://hog.grinvin.org/ViewGraphInfo.action?id=604
  # is K???????{XMC
  # but picture K?????H@b_OF

  my $orig_g6_str;
  my %seen;
  my $try_tree = sub {
    my ($parent_aref, $edge_aref) = @_;
    # print "$num_vertices vertices   p=",@$parent[1 .. $num_vertices-1],
    #   " nc=",@num_children[0 .. $num_vertices-1],"\n";

    my $num_vertices = scalar(@$parent_aref);
    my $layout = grid_layout($parent_aref);
    if ($layout) {
      grid_layout_check ($edge_aref, $parent_aref, $layout);
    } else {
      print "Cannot layout\n";
    }
    if ($num_vertices < 5 && $layout) {
      print "can layout\n";
    }
    my $easy = parent_aref_to_Graph_Easy($parent_aref);
    my $g6_str = $easy->as_graph6;

    my $canonical = g6_str_to_canonical($g6_str);
    # if ($seen{$canonical}++) {
    #   print "duplicate $canonical\n";
    #   return;
    # }
    # print "new $canonical\n";

    if ($num_vertices <= 0 || ! $layout) {
      $easy->layout(timeout => 999);
      print "num_vertices $num_vertices\n";
      print $easy->as_ascii,"\n";
    }

    if (! $layout) {
      {
        print "original\n";
        print $orig_g6_str;
        Graph::Graph6::read_graph(str => $orig_g6_str,
                                  edge_func => sub { print "  ",join('-', @_) });
        print "\n";
        print "\n";

        print "easy $easy  ",($easy->is_directed ? 'directed' : 'undirected'), "\n";
        print " ", Graph_Easy_edges_string($easy),"\n";
        Graph_Easy_print_adjacency_matrix($easy);

        print "via parent_aref\n";
        print $g6_str;
        Graph::Graph6::read_graph(str => $g6_str,
                                  edge_func => sub { print "  ",join('-', @_) });
        print "\n";
        print "  parents  ", join(' ', map {defined $_ ? $_ : 'undef'} @$parent_aref), "\n";
        print "\n";

        print "canonical\n";
        print $canonical;
        Graph::Graph6::read_graph(str => $canonical,
                                  edge_func => sub { print "  ",join('-', @_) });
        print "\n";
        print g6_str_to_canonical($orig_g6_str);
        print "\n";

        open my $fh, '>', '/tmp/1.g6' or die;
        print $fh $g6_str or die;
        close $fh or die;
      }
      my $graphviz_str = $easy->as_graphviz;
      File::Slurp::write_file('/tmp/cannot.dot', $graphviz_str) or die;

      IPC::Run::run(['dot', '-Tpng', '/tmp/cannot.dot', '-o', '/tmp/1.png']);
      if ($ENV{'DISPLAY'}) {
        IPC::Run::run(['xdot', '/tmp/cannot.dot']);
      }
    }
  };

  {
    my $iterator_func = make_tree_iterator_edge_aref(num_vertices_min => 12,
                                                     num_vertices_max => 12,
                                                     degree_max => 4);
    my $count = 0;
    while (my $edge_aref = $iterator_func->()) {
      $count++;
      Graph::Graph6::write_graph(str_ref => \$orig_g6_str,
                                 edge_aref => $edge_aref);
      my $parent_aref = edge_aref_to_parent_aref($edge_aref);
      $try_tree->($parent_aref, $edge_aref);
    }
    print "count $count trees\n";
    exit 0;
  }

  {
    $orig_g6_str = 'K??????wCwO['."\n";
    my @edges;
    Graph::Graph6::read_graph(str => $orig_g6_str,
                              edge_aref => \@edges);
    my @parent = (undef, 0,0,0,0, 1,1,1, 2,2,2, 3,3,3, 4);
    @parent = (undef, 0,0,0, 1,1,1, 2,2,2, 3,3);
    $try_tree->(\@parent, \@edges);
    exit 0;
  }

  exit 0;
}



{
  # trees no layout on square grid
  #
  # 3,3,3,0 https://hog.grinvin.org/ViewGraphInfo.action?id=582
  # 3,3,2,1 not
  # 3,2,2,2 not
  my $n = 1;
  foreach my $list ([3, 3, 3, 0],
                    [3, 3, 2, 1],
                    [3, 2, 2, 2]) {
    $list->[0]+$list->[1]+$list->[2]+$list->[3] == 9 or die;
    print @$list,"  n=$n\n";

    my $graph = Graph->new (undirected => 1);
    $graph->add_vertex(0);
    $graph->add_edge(0,1);
    $graph->add_edge(0,2);
    $graph->add_edge(0,3);
    $graph->add_edge(0,4);
    my $v = 5;
    foreach my $i (0 .. $#$list) {
      my $from = $i+1;
      foreach (1 .. $list->[$i]) {
        $graph->add_edge($from, $v++);
      }
    }
    my $writer = Graph::Writer::Graph6->new;
    $writer->write_graph($graph, "/tmp/$n.g6");

    print "  centre: ",join(' ',$graph->centre_vertices),"\n";

    {
      require Graph::Convert;
      my $easy = Graph::Convert->as_graph_easy($graph);
      my $graphviz_str = $easy->as_graphviz;
      # print $graphviz_str;
      my $png_filename = "/tmp/$n.png";
      require IPC::Run;
      IPC::Run::run(['dot','-Tpng'], '<',\$graphviz_str, '>',$png_filename);
      # print $easy->as_ascii;
    }

    $n++;
  }
  exit 0;
}

#------------------------------------------------------------------------------

BEGIN {
  my @dir4_to_dx = (1,0,-1,0);
  my @dir4_to_dy = (0,1,0,-1);

  sub grid_layout {
    my ($parent_aref) = @_;
    ### grid_layout() ...

    my $num_vertices = scalar(@$parent_aref);
    my $origin = $#$parent_aref + 5;
    my @layout = ([$origin, $origin]);  # v=0
    my @occupied;

    $occupied[$origin][$origin] = 0;
    my @directions = (3, -1);   # direction of $v from its parent

    my $v = 1;
    for (;;) {
      my $dir = ++$directions[$v];
      ### at: "$v consider direction $dir  parent $parent_aref->[$v]"
      if ($dir > $#dir4_to_dx) {
        ### backtrack ...
        $v--;
        if ($v < 1) {
          return undef;
        }
        # unplace $v
        my ($x,$y) = @{$layout[$v]};
        undef $occupied[$x][$y];
        next;
      }

      my ($x,$y) = @{$layout[$parent_aref->[$v]]};
      $x += $dir4_to_dx[$dir];
      $y += $dir4_to_dy[$dir];
      if (defined $occupied[$x][$y]) {
        next;
      }

      # place and go to next vertex
      $occupied[$x][$y] = $v;
      @{$layout[$v]} = ($x, $y);
      $v++;
      if ($v >= $num_vertices) {
        ### successful layout ...
        ### @layout
        return \@layout;
      }
      $directions[$v] = -1;
    }
  }
}

sub grid_layout_check {
  my ($edge_aref, $parent_aref, $layout_aref) = @_;
  # my $num_vertices = edge_aref_num_vertices($edge_aref);
  my $bad;
  foreach my $edge (@$edge_aref) {
    my ($from, $to) = @$edge;
    if (! defined $layout_aref->[$from]) {
      $bad = "missing layout for vertex $from";
      last;
    }
    if (! defined $layout_aref->[$to]) {
      $bad = "missing layout for vertex $to";
      last;
    }

    my ($x_from,$y_from) = @{$layout_aref->[$from]};
    my ($x_to,$y_to)     = @{$layout_aref->[$to]};
    if ($x_from < 0 || $y_from < 0) {
      $bad = "negative coordinate at $from";
      last;
    }
    if ($x_to < 0 || $y_to < 0) {
      $bad = "negative coordinates at $to";
      last;
    }
    if (abs($x_from - $x_to) + abs($y_from - $y_to) != 1) {   # dx+dy
      $bad = "layout not a unit step $from to $to";
      last;
    }
  }
  if ($bad) {
    print "layout length ", scalar(@$layout_aref),"\n";
    foreach my $v (0 .. $#$layout_aref) {
      print "$v at ",join(', ', @{$layout_aref->[$v]}),"\n";
    }
    print "edges";
    foreach my $edge (@$edge_aref) {
      print "  ",join('-', @$edge);
    }
    print "\n";
    print "parents  ", join(' ', map {defined $_ ? $_ : 'undef'} @$parent_aref), "\n";
    die $bad
  }
}
sub edge_aref_num_vertices {
  my ($edge_aref) = @_;
  if (! @$edge_aref) { return 0; }
  return max(map {@$_} @$edge_aref) + 1;
}

sub parent_aref_to_Graph_Easy {
  my ($parent_aref) = @_;
  my $graph = Graph::Easy->new(undirected => 1);
  if (@$parent_aref) {
    $graph->add_vertex(0);
    foreach my $v (1 .. $#$parent_aref) {
      $graph->add_edge($v,$parent_aref->[$v]);
    }
  }
  return $graph;
}

sub make_tree_iterator_edge_aref {
  my %option = @_;

  my $degree_predicate_aref;
  if (defined (my $degree_list = $option{'degree_list'})) {
    my @degree_predicate_array;
    foreach my $degree (@$degree_list) {
      $degree_predicate_array[$degree] = 1;
    }
    $degree_predicate_aref = \@degree_predicate_array;
  } elsif (defined (my $degree_max = $option{'degree_max'})) {
    my @degree_predicate_array;
    foreach my $degree (1 .. $degree_max) {
      $degree_predicate_array[$degree] = 1;
    }
    $degree_predicate_aref = \@degree_predicate_array;
  }

  my $num_vertices = ($option{'num_vertices_min'} || 1) - 1;
  my $num_vertices_max = $option{'num_vertices_max'};

  my $fh;
  return sub {
    for (;;) {
      if (! $fh) {
        if (defined $num_vertices_max && $num_vertices >= $num_vertices_max) {
          return;
        }
        $num_vertices++;
        ### open: $num_vertices
        my $filename = sprintf '/so/hog/trees%02d.g6', $num_vertices;
        open $fh, '<', $filename
          or die "Cannot open $filename: $!";
      }

      my @edges;
      unless (Graph::Graph6::read_graph(fh => $fh,
                                        num_vertices_ref => \my $file_num_vertices,
                                        edge_aref => \@edges)) {
        ### EOF ...
        close $fh or die;
        undef $fh;
        next;
      }
      my $edge_aref = \@edges;
      if ($degree_predicate_aref
          && ! edge_aref_degree_check($edge_aref, $degree_predicate_aref)) {
        ### skip for degree_max ...
        next;
      }
      return $edge_aref;
    }
  }
}

sub edge_aref_degree_check {
  my ($edge_aref, $degree_predicate_aref) = @_;
  my @vertex_degree;
  foreach my $edge (@$edge_aref) {
    my ($from, $to) = @$edge;
    $vertex_degree[$from]++;
    $vertex_degree[$to]++;
  }
  foreach my $degree (@vertex_degree) {
    if (! $degree_predicate_aref->[$degree]) {
      return 0;
    }
  }
  return 1;
}

sub edge_aref_to_parent_aref {
  my ($edge_aref) = @_;
  ### edge_aref_to_parent_aref() ...

  my @neighbours;
  foreach my $edge (@$edge_aref) {
    my ($from, $to) = @$edge;
    push @{$neighbours[$from]}, $to;
    push @{$neighbours[$to]}, $from;
  }

  my @parent;
  my @n_to_v = (0);
  my @v_to_n = (0);
  my $upto_v = 1;
  for (my $v = 0; $v < $upto_v; $v++) {
    ### neighbours: "$v=n$v_to_n[$v] to n=".join(',',@{$neighbours[$v_to_n[$v]]})
    foreach my $n (@{$neighbours[$v_to_n[$v]]}) {
      if (! defined $n_to_v[$n]) {
        $n_to_v[$n] = $upto_v;
        $v_to_n[$upto_v] = $n;
        $parent[$upto_v] = $v;
        $upto_v++;
      }
    }
  }
  foreach my $edge (@$edge_aref) {
    foreach my $n (@$edge) {
      $n = $n_to_v[$n]; # mutate array
    }
  }

  ### @parent
  ### num_vertices: scalar(@parent)
  return \@parent;
}
sub parent_aref_to_edge_aref {
  my ($parent_aref) = @_;
  return [ map {[$parent_aref->[$_] => $_]} 1 .. $#$parent_aref ];
}

sub g6_str_to_canonical {
  my ($g6_str) = @_;
  my $canonical;
  my $err;
  if (! IPC::Run::run(['nauty-labelg','-i2'],
                      '<',\$g6_str,
                      '>',\$canonical,
                      '2>',\$err)) {
    die $err;
  }
  return $canonical;
}

sub Graph_Easy_edges_string {
  my ($easy) = @_;
  my @edges = map { [ $_->from->name, $_->to->name ] } $easy->edges;
  @edges = sort { $a->[0] cmp $b->[0] || $a->[1] cmp $b->[1] } @edges;
  return join(' ',map {join('-',@$_)} @edges);
}

# Trees by search.
# {
#   my @parent = (undef, -1);
#   my $v = 1;
#   for (;;) {
#     my $p = ++$parent[$v];
#     ### at: "$v consider new parent $p"
#     if ($p >= $v) {
#       ### backtrack ...
#       $v--;
#       if ($v < 1) { last; }
#       $p = $parent[$v];  # unparent this preceding v
#       $num_children[$p]--;
#       next;
#     }
#
#     if ($num_children[$p] >= ($p==0 ? 4 : 3)) {
#       next;
#     }
#
#     $num_vertices = $v;
#     $process_tree->();
#
#     if ($v < $num_vertices_limit) {
#       # descend
#       $num_children[$p]++;
#       # $num_children[$p] == grep {$_==$p} @parent[1..$v] or die;
#       $num_children[$v] = 0;
#       $v++;
#       $parent[$v] = -1;
#       $num_vertices = $v;
#     }
#   }
# }

# Tree iterator by parent.
# {
#   @parent = (undef);
#   @num_children = (0);
#   my $v = 0;
#   for (;;) {
#     $num_children[$v]++;
#     my $new_v = $v + $num_children[$v];
#     ### at: "$v consider new children $num_children[$v]"
#
#     if ($num_children[$v] > ($v==0 ? 4 : 3)
#         || $new_v > $num_vertices_limit) {
#       ### backtrack ...
#       $v = $parent[$v] // last;
#       next;
#     }
#
#     # add children
#     foreach my $i (1 .. $num_children[$v]) {
#       my $c = $v + $i;
#       $parent[$c] = $v;
#       $num_children[$c] = 0;
#     }
#     $v = $new_v-1;
#     $num_vertices = $v;
#     $process_tree->();
#   }
# }


sub Graph_Easy_print_adjacency_matrix {
  my ($easy) = @_;
  my $has_edge_either = ($easy->is_directed
                         ? \&Graph::Easy::As_graph6::_has_edge_either_directed
                         : 'has_edge');
  my @vertices = $easy->sorted_nodes('name');
  foreach my $from (0 .. $#vertices) {
    foreach my $to (0 .. $#vertices) {
      print $easy->$has_edge_either($vertices[$from], $vertices[$to]) ? ' 1' : ' 0';
    }
    print "\n";
  }
}
