# 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::BestOfTree;
use 5.004;
use strict;
use base 'Graph::Maker';

use vars '$VERSION';
$VERSION = 4;

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

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

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

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

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

  $graph->set_graph_attribute (name => "Best-Of Tree, height $height");
  if ($height > 0) {
    my $directed = $graph->is_directed;
    my $half = int(($height+1)/2);

    my @v = (1);
    my @score = (0);
    my $upto = 1;
    $graph->add_vertex(1);

    foreach my $remaining (reverse 0 .. $height-2) {
      my @new_v;
      my @new_score;
      while (@v) {
        my $parent = shift @v;
        my $score  = shift @score;
        foreach my $win (0, 1) {
          my $child = ++$upto;
          $graph->add_edge($parent, $child);
          if ($directed) { $graph->add_edge($child, $parent); }

          $score += $win;
          unless ($score > $half
                  || $score+$remaining < $half) {
            push @new_v, $child;
            push @new_score, $score;
          }
        }
      }
      @v = @new_v;
      @score = @new_score;
    }
  }
  return $graph;
}

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

__END__

=for stopwords Ryde

=head1 NAME

Graph::Maker::BestOfTree - create BestOf tree graph

=for test_synopsis my ($graph)

=head1 SYNOPSIS

 use Graph::Maker::BestOfTree;
 $graph = Graph::Maker->new ('best_of_tree', N => 32);

=head1 DESCRIPTION

C<Graph::Maker::BestOfTree> creates C<Graph.pm> graphs of BestOf trees.
A BestOf tree has N nodes numbered from the root at 0 up to N-1, inclusive.

The parent of a given node is its node number n with the lowest 1-bit
cleared to 0.  Conversely the children of a node n=xxx000 are xxx001 xxx010
xxx100, so each trailing 0 bit changed to a 1.  At the root the children are
single bit, so powers-of-2, up to the highest bit of the highest vertex N-1.

          __0___
         /  |   \        N => 8
        1   2    4
            |   / \
            3  5   6
                   |
                   7

BestOf tree C<N =E<gt> 32> appears on the cover of Knuth volume 1,
"Fundamental Algorithms", second edition.

=head1 FUNCTIONS

=over

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

The key/value parameters are

    N          =>  integer
    level      =>  integer

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

C<N> is the number of vertices, 0 to N-1.  Or instead C<level> gives 2^level
many vertices.

Like C<Graph::Maker::BalancedTree>, if the graph is directed (the default)
then edges are added both up and down between each parent and child.  Option
C<undirected =E<gt> 1> creates an undirected graph and for it there is a
single edge from parent to child.

=back

=head1 SEE ALSO

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

=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
