The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# -*-Perl-*- Test Harness script for Bioperl
# $Id: TreeIO.t 14580 2008-03-01 17:01:30Z cjfields $

use strict;

BEGIN {
  use lib '.';
  use Bio::Root::Test;
    
  test_begin(-tests => 49);

  use_ok('Bio::TreeIO');
}

my $verbose = test_debug();

ok my $treeio = Bio::TreeIO->new(-verbose => $verbose,
                                 -format => 'newick',
                                 -file => test_input_file('cysprot1b.newick'));

my $tree = $treeio->next_tree;
isa_ok($tree, 'Bio::Tree::TreeI');

my @nodes = $tree->get_nodes;
is(@nodes, 6);
my ($rat) = $tree->find_node('CATL_RAT');
ok($rat);
is($rat->branch_length, '0.12788');
# move the id to the bootstap
is($rat->ancestor->bootstrap($rat->ancestor->id), '95');
$rat->ancestor->id('');
# maybe this can be auto-detected, but then can't distinguish
# between internal node labels and bootstraps...
is($rat->ancestor->bootstrap, '95');
is($rat->ancestor->branch_length, '0.18794');
is($rat->ancestor->id, '');

if ($verbose) {
	foreach my $node ( $tree->get_root_node()->each_Descendent() ) {
		print "node: ", $node->to_string(), "\n";
		my @ch = $node->each_Descendent();
		if( @ch ) {
			print "\tchildren are: \n";
			foreach my $node ( $node->each_Descendent() ) {
				print "\t\t ", $node->to_string(), "\n";
			}
		}
	}
}

my $FILE1 = test_output_file();
$treeio = Bio::TreeIO->new(-verbose => $verbose,
			  -format => 'newick',
			  -file   => ">$FILE1");
$treeio->write_tree($tree);
undef $treeio;
ok( -s $FILE1 );
$treeio = Bio::TreeIO->new(-verbose => $verbose,
			  -format => 'newick',
			  -file   => test_input_file('LOAD_Ccd1.dnd'));
ok($treeio);
$tree = $treeio->next_tree;

isa_ok($tree,'Bio::Tree::TreeI');

@nodes = $tree->get_nodes;
is(@nodes, 52);

if( $verbose ) { 
	foreach my $node ( @nodes ) {
		print "node: ", $node->to_string(), "\n";
		my @ch = $node->each_Descendent();
		if( @ch ) {
			print "\tchildren are: \n";
			foreach my $node ( $node->each_Descendent() ) {
				print "\t\t ", $node->to_string(), "\n";
			}
		}
	}
}

is($tree->total_branch_length, 7.12148);
my $FILE2 = test_output_file();
$treeio = Bio::TreeIO->new(-verbose => $verbose,
			  -format => 'newick', 
			  -file   => ">$FILE2");
$treeio->write_tree($tree);
undef $treeio;
ok(-s $FILE2);
$treeio = Bio::TreeIO->new(-verbose => $verbose,
			  -format  => 'newick',
			  -file    => test_input_file('hs_fugu.newick'));
$tree = $treeio->next_tree();
@nodes = $tree->get_nodes();
is(@nodes, 5);
# no relable order for the bottom nodes because they have no branchlen
my @vals = qw(SINFRUP0000006110);
my $saw = 0;
foreach my $node ( $tree->get_root_node()->each_Descendent() ) {
	foreach my $v ( @vals ) {
	   if( defined $node->id && 
	       $node->id eq $v ){ $saw = 1; last; }
	}
	last if $saw;
}
is($saw, 1, "Saw $vals[0] as expected");
if( $verbose ) {
	foreach my $node ( @nodes ) {
		print "\t", $node->id, "\n" if $node->id;
	}
}

# parse trees with scores

$treeio = Bio::TreeIO->new(-format => 'newick',
			   -file   => test_input_file('puzzle.tre'));
$tree = $treeio->next_tree;
ok($tree);
is($tree->score, '-2673.059726');


# no semi-colon

$treeio = Bio::TreeIO->new(-format => 'newick', 
			   -file=> test_input_file('semicolon.newick'));
$tree = $treeio->next_tree;
ok($tree);
is($tree->get_nodes, 15);

$treeio = Bio::TreeIO->new(-format => 'newick', 
			   -file=> test_input_file('no_semicolon.newick'));
$tree = $treeio->next_tree;
ok($tree);
is($tree->get_nodes, 15);


test_roundtrip('((a,b),c);','Round trip: simple newick');
test_roundtrip('(a:1,b:2,c:3,d:4)TEST:1.2345;','Round trip: Root node branch length');
test_roundtrip('(a:1,b:2,c:3,d:4):1.2345;','Round trip: Root node branch length');
test_roundtrip('(A:0.1,B:0.2,(C:0.3,D:0.4)E:0.5)F;','Round trip: from Wikipedia');
test_roundtrip('(a:1,b:2):0.0;','Branch length on root');
test_roundtrip('(a:1,b:2):0.001;','Tiny branch length on root');
test_roundtrip('(a:0,b:00):0.0;','Zero branch lenghts');

# From Wikipedia:
test_roundtrip('(,,(,));','wkp blank tree');
test_roundtrip('(A,B,(C,D));','wkp only leaves labeled');
test_roundtrip('(A,B,(C,D)E)F;','wkp all nodes labeled');
test_roundtrip('(:0.1,:0.2,(:0.3,:0.4):0.5);','wkp branch lengths, no labels');
test_roundtrip('(:0.1,:0.2,(:0.3,:0.4):0.5):0.0;','wkp branch lengths, including root');
test_roundtrip('(A:0.1,B:0.2,(C:0.3,D:0.4):0.5);','wkp distances and leaf names');
test_roundtrip('(A:0.1,B:0.2,(C:0.3,D:0.4)E:0.5)F;','wkp distances and all names');
test_roundtrip('((B:0.2,(C:0.3,D:0.4)E:0.5)F:0.1)A;','wkp rooted on leaf node');

# From the PHYLIP site:
test_roundtrip('(B,(A,C,E),D);','phylip simple tree');
test_roundtrip('(,(,,),);','phylip no labels');
test_roundtrip('(B:6.0,(A:5.0,C:3.0,E:4.0):5.0,D:11.0);','phylip w/ branch lengths');
test_roundtrip('(B:6.0,(A:5.0,C:3.0,E:4.0)Ancestor1:5.0,D:11.0);','phylip w/ internal label');
test_roundtrip('((raccoon:19.19959,bear:6.80041):0.84600,((sea_lion:11.99700,seal:12.00300):7.52973,((monkey:100.85930,cat:47.14069):20.59201,weasel:18.87953):2.09460):3.87382,dog:25.46154);','phylip raccoon tree');
test_roundtrip('(Bovine:0.69395,(Gibbon:0.36079,(Orang:0.33636,(Gorilla:0.17147,(Chimp:0.19268,Human:0.11927):0.08386):0.06124):0.15057):0.54939,Mouse:1.21460):0.10;','phylip mammal tree');
test_roundtrip('(Bovine:0.69395,(Hylobates:0.36079,(Pongo:0.33636,(G._Gorilla:0.17147,(P._paniscus:0.19268,H._sapiens:0.11927):0.08386):0.06124):0.15057):0.54939,Rodent:1.21460);','phylip mammal tree w/ underbars');
test_roundtrip('A;','phylip single node');
test_roundtrip('((A,B),(C,D));','phylip_quartet');
test_roundtrip('(Alpha,Beta,Gamma,Delta,,Epsilon,,,);','phylip greek');

sub test_roundtrip {
  my $string = shift;
  my $desc = shift;

  my $in = Bio::TreeIO->new(-format => 'newick',
                            -string => $string,
                            -verbose => $verbose
                            );
  my $out = '';
  eval {
    my $t = $in->next_tree;
    $out = $t->as_text('newick');
  };
  return is($out,$string,$desc);
}

sub read_file {
  my $file = shift;
  open(IN,"<$file");
  my (@lines) = <IN>;
  @lines = map {$_ =~ s/\\n//g} @lines;
  return join("",@lines);
}