# Copyright 2015 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.  See the file COPYING.  If not, see
# <http://www.gnu.org/licenses/>.

package Graph::Maker::Keller;
use 5.004;
use strict;
use Graph::Maker;

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

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


sub _default_graph_maker {
  require Graph;
  Graph->new(@_);
}

sub _is_edge {
  my ($from,$to) = @_;
  my $diff = $from ^ $to;
  my $seen_two = 0;
  my $seen_any = 0;
  while ($diff) {
    my $digit = $diff & 3;
    if ($digit)      { $seen_any++; }
    if ($digit == 2) { $seen_two++; }
    if ($seen_any >= 2 && $seen_two) {
      return 1;
    }
    $diff >>= 2;
  }
  return 0;
}

sub init {
  my ($self, %params) = @_;

  my $N        = delete($params{'N'}) || 0;
  my $subgraph = delete($params{'subgraph'}) || 0;

  my $graph_maker = delete($params{'graph_maker'});
  $graph_maker ||= \&_default_graph_maker;
  my $graph = $graph_maker->(%params);

  my $directed = $graph->is_directed;

  if ($subgraph) {
    $graph->set_graph_attribute (name => "Keller Subgraph $N");

    my @vertices;
    foreach my $to (1 .. (1 << (2*$N))-1) {
      if (_is_edge(0,$to)) {
        $graph->add_vertex($to);
        push @vertices, $to;
      }
    }
    foreach my $i (0 .. $#vertices) {
      my $from = $vertices[$i];
      foreach my $j ($i+1 .. $#vertices) {
        my $to = $vertices[$j];
        if (_is_edge($from,$to)) {
          if ($directed) { $graph->add_edge($to, $from); }
          $graph->add_edge($from, $to);
        }
      }
    }

  } else {
    $graph->set_graph_attribute (name => "Keller $N");

    my $max_vertex = (1 << (2*$N)) - 1;
    for my $from (0 .. $max_vertex) {
      $graph->add_vertex($from);  # for $N < 2 where there are no edges

      for my $to ($from+1 .. $max_vertex) {
        if (_is_edge($from,$to)) {
          $graph->add_edge($from, $to);
          if ($directed) { $graph->add_edge($to, $from); }
        }
      }
    }
  }
  return $graph;
}

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

__END__

=for stopwords Ryde Keller cyclotomic

=head1 NAME

Graph::Maker::Keller - create Keller graphs

=for test_synopsis my ($graph)

=head1 SYNOPSIS

 use Graph::Maker::Keller;
 $graph = Graph::Maker->new ('Keller', N => 2);

=head1 DESCRIPTION

C<Graph::Maker::Keller> creates a C<Graph.pm> graph of a Keller graph.  This
is a graph form of Keller's conjecture on N-dimensional tilings by unit
hypercubes.

Keller graph N has 4^N vertices numbered 0 to 4^N-1.  These vertices are
treated as base-4 integers.  Edges are between vertices differing in two or
more digit positions at least one of which is difference 2 mod 4.

    N         0  1   2     3      4       5
    vertices  1, 4, 16,   64,   256,   1024, ...  4^N
    degree    0, 0,  5,   34,   171,    776, ...  4^N - 3^N - N
    edges     0, 0, 40, 1088, 21888, 397312, ... (4^N-3^N-N)*4^N / 2

The graph is vertex transitive since the digit differences can all be taken
mod 4.  Each vertex has degree 4^N - 3^N - N.  That's calculated by possible
edges to any of the 4^N vertices (including itself) but no edge to those
differing by only 0,1,3 in any or all digit positions, which is 3^N
combinations (including no differences at all which is the vertex itself),
and also no edge to vertices differing by 2 mod 4 at a single digit, which
is N possible digit positions.

=for GP-Test  vector(6,N,N--; 4^N-3^N-N) == [0, 0, 5, 34, 171, 776]

=for GP-Test  vector(6,N,N--; 1/2*4^N*(4^N-3^N-N)) == [0, 0, 40, 1088, 21888, 397312]

=for GP-Test  vector(6,N,N--; 1/2*4^N*(4^N-1) - 1/2*4^N*(4^N-3^N-N)) == [0, 6, 80, 928, 10752, 126464]

N=0 and N=1 have no edges since there are only 0 and 1 digits respectively,
so nothing differs in 2 digit positions.

N=2 (16 vertices and 40 edges) is the Clebsh graph and the 16-cyclotomic
graph.

=head2 Subgraph

C<subgraph =E<gt> 1> gives the subgraph of the Keller graph induced by
neighbours of vertex 0.  Vertex 0 itself is not included, only its
neighbours and the edges among those neighbours.

    subgraph => 1
    N         0  1   2     3      4       5
    vertices  0, 0,  5,   34,   171,    776, ... 4^N - 3^N - N
    edges     0, 0,  0,  261,  9435, 225990, ...

The number of vertices is the degree above.  This subgraph is of interest
since the Keller graph is vertex transitive so the problem of finding a
clique of size k in the Keller graph is reduced to finding a clique of size
k-1 in this subgraph.  The size of the maximum clique is related to Keller's
conjecture.

N=2 subgraph has no edges.  Its vertices are 12, 21, 22, 23, 32 in base-4
and any pair of them either differ in only 1 digit or differ in 2 digits but
without a difference 2 mod 4.

=head1 FUNCTIONS

=over

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

The key/value parameters are

    N         => integer
    subgraph  => 0 or 1, default 0

Other parameters are passed to C<Graph-E<gt>new()>.

If the graph is directed (the default) then edges are added both forward and
backward between vertices.  Option C<undirected =E<gt> 1> creates an
undirected graph and for it there is a single edge between vertices.

=back

=head1 OEIS

Entries in Sloane's Online Encyclopedia of Integer Sequences related to
these graphs include

=over

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

=back

    A202604    clique numbers (size of maximum clique)

=head1 SEE ALSO

L<Graph::Maker>

O. H. Keller, "Uber die luckenlose Einfullung des Raumes mit Wurfeln",
Journal fE<252>r die Reine und Angewandte Mathematik (Crelle's journal),
volume 163, 1930, pages 231-248.

=head1 LICENSE

Copyright 2015 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




    # for (my($i,$imask) = (0,1);
    #      $i < $N;
    #      $i++, $imask <<= 2) {
    #   foreach my $ixor ($imask, 2*$imask, 3*$imask) {
    #     my $in = $n ^ $ixor;
    #     
    #     for (my($j,$jmask) = (0,2);
    #          $j < $N;
    #          $j++, $jmask <<= 2) {
    #       next if $i == $j;
    #       
    #       my $n2 = $in ^ $jmask;
    #       if ($directed || $n2 > $n) {
    #         $graph->add_edge($n, $n2);
    #       }
    #     }
    #   }
    # }

# 171, 776, 3351, 14190
# not in OEIS, apparently
