#!/usr/bin/perl -w # Copyright 2015, 2016, 2017, 2018 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 . use strict; use Test; use lib 't'; use MyTestHelpers; BEGIN { MyTestHelpers::nowarnings() } # uncomment this to run the ### lines # use Smart::Comments; plan tests => 117; require Graph::Graph6; my $filename = 'Graph-Graph6-t.tmp'; #------------------------------------------------------------------------------ { my $want_version = 9; ok ($Graph::Graph6::VERSION, $want_version, 'VERSION variable'); ok (Graph::Graph6->VERSION, $want_version, 'VERSION class method'); ok (eval { Graph::Graph6->VERSION($want_version); 1 }, 1, "VERSION class check $want_version"); my $check_version = $want_version + 1000; ok (! eval { Graph::Graph6->VERSION($check_version); 1 }, 1, "VERSION class check $check_version"); } #------------------------------------------------------------------------------ # _number_to_string() ok (Graph::Graph6::_number_to_string(30), chr(93)); ok (Graph::Graph6::_number_to_string(12345), chr(126).chr(66).chr(63).chr(120)); { # 460175067 = octal 3333333333 < 2^29 my $want = chr(126).chr(126) . chr(63).chr(90).chr(90) . chr(90).chr(90).chr(90); ok (Graph::Graph6::_number_to_string(460175067), $want); require Math::BigInt; ok (Graph::Graph6::_number_to_string(Math::BigInt->new('460175067')), $want); } { # 2147483647 = 2^31 - 1 my $want = chr(126).chr(126) . chr(63+1).chr(126).chr(126) . chr(126).chr(126).chr(126); ok (Graph::Graph6::_number_to_string(2147483647), $want); require Math::BigInt; ok (Graph::Graph6::_number_to_string(Math::BigInt->new('2147483647')), $want); } { # 2^36 - 1 my $power = 1; $power <<= 36; $power -= 1; my $have_36bit_UV = ($power >= 3 * 2.0**34); my $want = chr(126).chr(126) . chr(126).chr(126).chr(126) . chr(126).chr(126).chr(126); my $got = Graph::Graph6::_number_to_string($power); skip ($have_36bit_UV ? 0 : 'due to UV less than 36 bits', $got, $want, '2^36-1'); ok (Graph::Graph6::_number_to_string(Math::BigInt->new('68719476735')), $want, '2^36-1'); $power = Math::BigInt->new(1)->blsft(36) - 1; $got = Graph::Graph6::_number_to_string($power); # Math::BigInt which came with Perl 5.6.0 didn't have an >> operator so # beware of it being a 32-bit truncation my $have_bigint_good_rshift = (($power >> 32) == 15); skip ($have_bigint_good_rshift ? 0 : 'due to Math::BigInt >>32 not right', $got, $want, '2^36-1'); } #------------------------------------------------------------------------------ # read_graph() sub aref_stringize { my ($aref) = @_; if (ref $aref eq 'ARRAY') { return '['.join(',', map {aref_stringize($_)} @$aref).']'; } else { return "$aref"; } } { # formats.txt digraph6 example my $str = chr(38).chr(68).chr(73).chr(63).chr(65).chr(79).chr(63)."\n"; my @edges; my $num_vertices; my $ret = Graph::Graph6::read_graph(str => $str, num_vertices_func => sub { $num_vertices = $_[0]; }, edge_func => sub { push @edges, [@_]; }); ok ($ret, 1); ok ($num_vertices, 5); ok (aref_stringize(\@edges), '[[0,2],[0,4],[3,1],[3,4]]'); } { # formats.txt graph6 example my @edges; my $num_vertices; my $ret = Graph::Graph6::read_graph(str => chr(68).chr(81).chr(99)."\n", num_vertices_func => sub { $num_vertices = $_[0]; }, edge_func => sub { push @edges, [@_]; }); ok ($ret, 1); ok ($num_vertices, 5); ok (aref_stringize(\@edges), '[[0,2],[1,3],[0,4],[3,4]]'); } { # formats.txt sparse6 example my @edges; my $num_vertices; my $ret = Graph::Graph6::read_graph(str => ':Fa@x^', num_vertices_ref => \$num_vertices, edge_aref => \@edges); ok ($ret, 1); ok ($num_vertices, 7); ok (aref_stringize(\@edges), '[[0,1],[0,2],[1,2],[5,6]]'); } { # bad, croaking my $returned = 0; eval { Graph::Graph6::read_graph(str => '0'); $returned = 1; }; ok ($returned, 0); } # bad, with error_func returning foreach my $elem (['0', '0'], ['&&', '&'], # doubled digraph6 "&" ['&&DI?AO?', '&'], # doubled digraph6 "&" otherwise valid [':&Fa@x^', '&'], # bad digraph6 "&" after sparse6 [':Fa&@x^', '&'], # bad digraph6 "&" in middle of sparse6 ) { my ($str, $bad_char) = @$elem; my $error_func_called = 0; my $error_message = ''; my $ret = Graph::Graph6::read_graph(str => $str, error_func => sub { $error_message = join('',@_); $error_func_called = 1; }); ok ($ret, undef, "str \"$str\""); ok ($error_func_called, 1); ok ($error_message, "Unrecognised character: $bad_char"); } { # no such filename my $error_func_called = 0; my $error_message = ''; my $ret = Graph::Graph6::read_graph(filename => 'nosuchfilename', error_func => sub { $error_message = join('',@_); $error_func_called = 1; }); ok ($ret, undef); ok ($error_func_called, 1); ok ($error_message =~ /^Cannot open/, 1); } { # unexpected eof ~ my $error_func_called = 0; my $error_message = ''; my $ret = Graph::Graph6::read_graph(str => '~', error_func => sub { $error_message = join('',@_); $error_func_called = 1; }); ok ($ret, undef); ok ($error_func_called, 1); ok ($error_message, "Unexpected EOF"); } { # graph6 dodgy padding, doesn't become edges my $str = chr(63+2) . chr(63+63); my @edges; my $num_vertices; my $ret = Graph::Graph6::read_graph(str => $str, num_vertices_ref => \$num_vertices, edge_aref => \@edges); ok ($ret, 1); ok ($num_vertices, 2); ok (aref_stringize(\@edges), '[[0,1]]'); } { # skip blank lines before graph my $str = "\n\n\n".chr(63+0); my @edges; my $num_vertices; my $ret = Graph::Graph6::read_graph(str => $str, num_vertices_ref => \$num_vertices, edge_aref => \@edges); ok ($ret, 1); ok ($num_vertices, 0); ok (aref_stringize(\@edges), '[]'); } { # trailng \r my $str = "\n\n\n".chr(63+0)."\r\n"; my @edges; my $num_vertices; my $ret = Graph::Graph6::read_graph(str => $str, num_vertices_ref => \$num_vertices, edge_aref => \@edges); ok ($ret, 1); ok ($num_vertices, 0); ok (aref_stringize(\@edges), '[]'); } { # some newlines then eof my $str = "\n\n\n"; my @edges; my $num_vertices; my $ret = Graph::Graph6::read_graph(str => $str, num_vertices_ref => \$num_vertices, edge_aref => \@edges); ok ($ret, 0); } { # bad header my $str = ">>grXYZ"; my $error_message; my $ret = Graph::Graph6::read_graph(str => $str, error_func => sub { $error_message = join('',@_); }); ok ($ret, undef); ok ($error_message, "Incomplete header: >>grX"); } { # bad header my $str = ">#"; my $error_message; my $ret = Graph::Graph6::read_graph(str => $str, error_func => sub { $error_message = join('',@_); }); ok ($ret, undef); ok ($error_message, "Incomplete header: >#"); } { # \my per POD my $ret = Graph::Graph6::read_graph(str => ':Fa@x^', num_vertices_ref => \my $num_vertices, edge_aref => \my @edges); ok ($ret, 1); ok ($num_vertices, 7); ok (aref_stringize(\@edges), '[[0,1],[0,2],[1,2],[5,6]]'); } { # from filename { my $fh; (open $fh, '>', $filename and print $fh ':Fa@x^',"\n" and close $fh) or die "Cannot write $filename; $!"; } my $ret = Graph::Graph6::read_graph(filename => $filename, num_vertices_ref => \my $num_vertices, edge_aref => \my @edges); ok ($ret, 1); ok ($num_vertices, 7); ok (aref_stringize(\@edges), '[[0,1],[0,2],[1,2],[5,6]]'); } { # from fh { my $fh; (open $fh, '>', $filename and print $fh ':Fa@x^',"\n" and close $fh) or die "Cannot write $filename; $!"; } open my $fh, '<', $filename or die "Cannot open $filename: $!"; my $ret = Graph::Graph6::read_graph(fh => $fh, num_vertices_ref => \my $num_vertices, edge_aref => \my @edges); ok ($ret, 1); ok ($num_vertices, 7); ok (aref_stringize(\@edges), '[[0,1],[0,2],[1,2],[5,6]]'); } { # sparse6 # b[i]=1 or b[i]=0 same for setting v when to>=v+2 # foreach my $str (":CoJ\n", ":COJ\n") { my @edges; my $num_vertices; my $ret = Graph::Graph6::read_graph(str => $str, num_vertices_ref => \$num_vertices, edge_aref => \@edges); ok ($ret, 1); ok ($num_vertices, 4); ok (aref_stringize(\@edges), '[[0,2],[1,2]]'); } } { # sparse6 my $str = ":GoBN_\n"; my @edges; my $num_vertices; my $ret = Graph::Graph6::read_graph(str => $str, num_vertices_ref => \$num_vertices, edge_aref => \@edges); ok ($ret, 1); ok ($num_vertices, 8); ok (aref_stringize(\@edges), '[[0,4],[3,4],[3,4],[0,6]]'); } { # :GoBN_ # [length 7] # 001000 110000 000011 001111 100000 # round trip: # # [length 6] # 001000 110000 001110 000011 # sparse6 my $str = ":GoMB\n"; my @edges; my $num_vertices; my $ret = Graph::Graph6::read_graph(str => $str, num_vertices_ref => \$num_vertices, edge_aref => \@edges); ok ($ret, 1); ok ($num_vertices, 8); ok (aref_stringize(\@edges), '[[0,4],[0,6]]'); } #------------------------------------------------------------------------------ # write_graph() -- graph6 { # formats.txt graph6 example my $str; my $ret = Graph::Graph6::write_graph(str_ref => \$str, num_vertices => 5, edge_aref => [[0,2],[4,0],[1,3],[3,4]]); ok ($ret, 1); ok ($str, chr(68).chr(81).chr(99)."\n"); } { # with header my $str; my $ret = Graph::Graph6::write_graph(header => 1, str_ref => \$str, num_vertices => 2); ok ($ret, 1); ok ($str, ">>graph6< \$str, num_vertices => 6, edge_predicate => sub { my ($from, $to) = @_; return $to <= 3; }); ok ($ret, 1); ok ($str, "E~??\n"); } { # no edges my $str; my $ret = Graph::Graph6::write_graph(str_ref => \$str, num_vertices => 0, ); ok ($ret, 1); ok ($str, "?\n"); } { # \my per POD my $ret = Graph::Graph6::write_graph(str_ref => \my $str, num_vertices => 1, ); ok ($ret, 1); ok ($str, chr(63+1)."\n"); } { # to filename my $ret = Graph::Graph6::write_graph(filename => $filename, edge_aref => [[0,1]]); ok ($ret, 1); my $str; { my $fh; (open $fh, '<', $filename and read $fh, $str, 1000 and close $fh ) or die "Cannot read $filename: $!"; } unlink($filename); ok ($str, chr(63+2).chr(63+32)."\n"); } { # to fh { open my $fh, '>', $filename or die "Cannot write $filename: $!"; my $ret = Graph::Graph6::write_graph(filename => $filename, edge_aref => [[0,1]]); ok ($ret, 1); } my $str; { my $fh; (open $fh, '<', $filename and read $fh, $str, 1000 and close $fh ) or die "Cannot read $filename: $!"; } unlink($filename); ok ($str, chr(63+2).chr(63+32)."\n"); } #------------------------------------------------------------------------------ # write_graph() -- sparse6 { # formats.txt sparse6 example my $str; my $ret = Graph::Graph6::write_graph(format => 'sparse6', str_ref => \$str, num_vertices => 7, edge_aref => [[0,1],[0,2],[1,2],[5,6]]); ok ($ret, 1); ok ($str, ":Fa\@x^\n"); } { # with header my $str; my $ret = Graph::Graph6::write_graph(format => 'sparse6', header => 1, str_ref => \$str, num_vertices => 8); ok ($ret, 1); ok ($str, ">>sparse6<<:G\n"); } { # with edge_predicate my $str; my $ret = Graph::Graph6::write_graph(format => 'sparse6', str_ref => \$str, num_vertices => 31, # width = 5 bits edge_predicate => sub { my ($from, $to) = @_; return ($from == 0 && $to == 0); }); ok ($ret, 1); ok ($str, ':'.chr(31+63).chr(63)."\n"); } { # no edges my $str; my $ret = Graph::Graph6::write_graph(format => 'sparse6', str_ref => \$str, num_vertices => 0, ); ok ($ret, 1); ok ($str, ":?\n"); } #------------------------------------------------------------------------------ # write_graph() provoke some sparse6 padding stuff { my $num_vertices; my $edges; my $try = sub { my ($str) = @_; undef $num_vertices; $edges = ''; Graph::Graph6::read_graph(str => $str, num_vertices_func => sub { my ($n) = @_; $num_vertices = $n; }, edge_func => sub { my ($from, $to) = @_; $edges .= "$from-$to,"; }); }; { # n=1 with 0-0 self loop # : N(1) 0 11111 $try->(':'.chr(63+1).chr(63+31)); ok ($num_vertices, 1); ok ($edges, '0-0,'); } { # n=2 with 0-0 self loop # : N(2) 0-0 0-1 1-1 # set v=1 + to v=2 $try->(':'.chr(63+2).chr(63+7)); ok ($num_vertices, 2); ok ($edges, '0-0,'); } { # n=2 with 0-0 and 1-1 self loops # : N(2) 0-0 1-1 1-1 # x=0 + x=1 + to v=2 $try->(':'.chr(63+2).chr(63+15)); ok ($num_vertices, 2); ok ($edges, '0-0,1-1,'); } { # n=4 edge 0-1 # : N(4) 1-00 1-11 # + x=0 + set v=3 $try->(':'.chr(63+4).chr(63+32+7)); ok ($num_vertices, 4); ok ($edges, '0-1,'); } { # n=4 edges 0-2, 1-2 # : N(4) 0-10 0-00 0-01 0-11 # set v=2 edge edge pad # :COJ # :CoJ $try->(':' . chr(63+4) . chr(63+16) . chr(63+11)); ok ($num_vertices, 4); ok ($edges, '0-2,1-2,'); } } #------------------------------------------------------------------------------ # write_graph() -- digraph6 { # formats.txt digraph6 example my $str; my $ret = Graph::Graph6::write_graph (format => 'digraph6', str_ref => \$str, num_vertices => 5, edge_aref => [[0,2], [0,4], [3,1],[3,4]]); ok ($ret, 1); ok ($str, "&DI?AO?\n"); } { # edge both ways my $str; my $ret = Graph::Graph6::write_graph (format => 'digraph6', str_ref => \$str, num_vertices => 3, edge_aref => [[0,2], [2,0]]); ok ($ret, 1); ok ($str, "&BG_\n"); } { # edge back only my $str; my $ret = Graph::Graph6::write_graph (format => 'digraph6', str_ref => \$str, num_vertices => 3, edge_aref => [ [2,0]]); ok ($ret, 1); ok ($str, "&B?_\n"); } #------------------------------------------------------------------------------ unlink $filename; exit 0;