The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# $Id: 06-tree.t 1621 2011-03-19 15:25:38Z rvos $
use strict;
use Bio::Phylo::Util::CONSTANT 'looks_like_instance';
use Test::More 'no_plan';
use Bio::Phylo::IO qw(parse unparse);
use Bio::Phylo::Forest::Node;
use Bio::Phylo::Forest::Tree;
my $data;

while (<DATA>) {
    $data .= $_;
}
Bio::Phylo->VERBOSE( -level => 0 );
ok( 1, '1 init' );
ok(
    my $trees = parse(
        -string => $data,
        -format => 'newick'
    ),
    '2 parse'
);
ok( my $treeset = $trees->get_entities, '3 trees' );
my $tree       = $treeset->[0];
my $unresolved = $treeset->[2];
ok( my $root       = $tree->get_root,           '4 get root' );
ok( my $node       = $root->get_first_daughter, '5 get first daughter' );
ok( my $other_node = $root->get_last_daughter,  '6 get last daughter' );
ok( my $children   = $root->get_children,       '7 get children' );

# get
ok( $tree->get('calc_tree_length'), '8 get ctl' );
ok( $tree->get_entities,            '9 get n' );
ok( $tree->get_internals,           '10 get int' );
ok( $tree->get_terminals,           '11 get term' );

#ok($tree->get_by_name('cherry'),                                   '12 gbn');
ok(
    $tree->get_by_value(
        -value => 'get_branch_length',
        -lt    => 0.5
    ),
    '12 get lt'
);
ok(
    $tree->get_by_value(
        -value => 'get_branch_length',
        -eq    => 2
    ),
    '13 get eq'
);
ok(
    $tree->get_by_value(
        -value => 'get_branch_length',
        -le    => 0.4
    ),
    '14 get le'
);
ok(
    $tree->get_by_value(
        -value => 'get_branch_length',
        -ge    => 0.1
    ),
    '15 get ge'
);
ok(
    $tree->get_by_value(
        -value => 'get_branch_length',
        -gt    => 0.2
    ),
    '16 get gt'
);
ok( $tree->is_binary, '17 is binary' );

# methods on unresolved tree
ok( !$unresolved->is_binary,      '18 is binary' );
ok( !$unresolved->is_ultrametric, '19 is ultrametric' );
eval { $unresolved->calc_rohlf_stemminess };
ok( looks_like_instance( $@, 'Bio::Phylo::Util::Exceptions::ObjectMismatch' ),
    '20 calc rohlf stemminess: ' . ref($@) );
eval { $unresolved->calc_imbalance };
ok( looks_like_instance( $@, 'Bio::Phylo::Util::Exceptions::ObjectMismatch' ),
    '21 calc imbalance' );
eval { $unresolved->calc_branching_times };
ok( looks_like_instance( $@, 'Bio::Phylo::Util::Exceptions::ObjectMismatch' ),
    '22 calc branching times' );
eval { $unresolved->calc_ltt };
ok( looks_like_instance( $@, 'Bio::Phylo::Util::Exceptions::ObjectMismatch' ),
    '23 calc ltt' );
eval { $tree->insert('BAD!') };
ok( looks_like_instance( $@, 'Bio::Phylo::Util::Exceptions::ObjectMismatch' ),
    '24 insert bad obj' );

# tests
ok( !$tree->is_ultrametric(0.01), '25 is ultrametric' );
ok( !$tree->is_monophyletic( $children, $node ), '26 not monophyletic' );

# test for monophyly
my $poly = $unresolved->get_by_regular_expression(
    -value => 'get_name',
    -match => qr/^poly$/
);
my $e = $unresolved->get_by_regular_expression(
    -value => 'get_name',
    -match => qr/^E$/
);
my $desc = $poly->[0]->get_descendants;
ok( $tree->is_monophyletic( $desc, $e->[0] ), '27 is monophyletic' );

# calculations
ok( $tree->calc_tree_length,         '28 calc tree length' );
ok( $tree->calc_tree_height,         '29 calc tree height' );
ok( $tree->calc_number_of_nodes,     '30 calc num nodes' );
ok( $tree->calc_number_of_terminals, '31 calc num terminals' );
ok( $tree->calc_number_of_internals, '32 calc num internals' );
ok( $tree->calc_total_paths,         '33 calc total paths' );
ok( $tree->calc_redundancy,          '34 calc redundancy' );
ok( $tree->calc_imbalance,           '35 calc imbalance' );

# balance calculation
my $balanced = $treeset->[3];
ok( $tree->calc_imbalance, '36 calc imbalance' );

# ultrametric calculations
ok( $tree = $tree->ultrametricize, '37 ultrametricize' );
ok( $tree->calc_fiala_stemminess, '38 calc fiala stemminess' );
ok( $tree->calc_rohlf_stemminess, '39 calc rohlf stemminess' );
ok( $tree->calc_resolution,       '40 calc resolution' );
ok( $tree->calc_branching_times,  '41 calc branching times' );
ok( $tree->calc_ltt,              '42 calc ltt' );
ok( $tree->scale(10),             '43 scale' );

# testing on undef branch lengths
my $undef = $treeset->[3];
$root = $undef->get_root;
eval { $undef->calc_rohlf_stemminess };
ok( looks_like_instance( $@, 'Bio::Phylo::Util::Exceptions::ObjectMismatch' ),
    '44 calc rohlf stemminess: ' . ref($@) );
eval { $undef->get('BAD!') };
ok( looks_like_instance( $@, 'Bio::Phylo::Util::Exceptions::UnknownMethod' ),
    '45 bad arg get' );
ok( $undef->calc_imbalance, '46 calc imbalance' );

# trying to create a cyclical tree, no mas!
my $node1    = new Bio::Phylo::Forest::Node;
my $node2    = new Bio::Phylo::Forest::Node;
my $cyclical = new Bio::Phylo::Forest::Tree;
$node1->set_parent($node2);
$node2->set_parent($node1);
$cyclical->insert($node1);
$cyclical->insert($node2);
ok( $cyclical->get_root, '47 no root in cycle' );
ok( $tree->DESTROY,      '48 destroy' );
my $left   = '((((A,B),C),D),E);';
my $right  = '(E,(D,(C,(A,B))));';
my $ladder = parse( '-format' => 'newick', '-string' => $left )->first;
ok( $ladder->ladderize->to_newick eq $right, '49 ladderize' );
{
    my $n1 = '((C:0,(B:0,A:0):7):3,D:0):0;';
    my $n2 = '(((A:0,B:0):4,C:0):6,D:0):0;';
    my $n3 = '((A,B,C),D);';
    my $t1 = parse( '-format' => 'newick', '-string' => $n1 )->first;
    my $t2 = parse( '-format' => 'newick', '-string' => $n2 )->first;
    my $t3 = parse( '-format' => 'newick', '-string' => $n3 )->first;
    ok( $t1->calc_branch_length_score($t2) == 18, "50 branch length score" );
    ok( $t1->calc_symdiff($t2) == 0,              "51 calc symdiff" );
    ok( $t1->calc_symdiff($t3) == 1,              "52 calc symdiff" );
}
{
    my $newick =
'(((a:100,b:1)n1:1,c:1)n2:1,((((d:1,e:1)n3:1,f:1)n4:1,g:1)n5:1,h:1)n6:1)n7:0;';
    my $tree = parse( '-format' => 'newick', '-string' => $newick )->first;
    my $focal = $tree->get_by_name('b');
    my $farthest_nodal = $focal->get_farthest_node;
    my $fn_name        = $farthest_nodal->get_name;
    ok( ( $fn_name eq 'd' or $fn_name eq 'e' ), "53 farthest by nodal" );
    my $farthest_patristic = $focal->get_farthest_node(1);
    my $fp_name            = $farthest_patristic->get_name;
    ok( $fp_name                      eq 'a',  "54 farthest by patristic" );
    ok( $tree->get_midpoint->get_name eq 'n1', "55 gets midpoint node" );
}
{
    my $newick = '((a:1,b:1)n1:1,c:2)n2:0;';
    my $tree = parse( '-format' => 'newick', '-string' => $newick )->first;
    $tree->calc_node_ages;
    is( $tree->get_by_name('n1')->get_generic('age'), 1, "56 calc node age" );
    is( $tree->get_by_name('n2')->get_generic('age'), 2, "57 calc node age" );
}
{
    my $newick = '((a:1,b:1)n1:1,(c:2,d:2))n2:0;';
    my $tree = parse( '-format' => 'newick', '-string' => $newick )->first;
    is( $tree->calc_number_of_cherries, 2, "58 calc number of cherries" );
}
{
    my $newick = '((a:1,b:1)n1:1,(c:2,d:2)n2:1)n3:0;';
    my $tree = parse( '-format' => 'newick', '-string' => $newick )->first;
    $tree->chronompl;
    is( $tree->get_by_name('n1')->get_branch_length, 1.5, '59 chronompl' );
    is( $tree->get_by_name('n2')->get_branch_length, 0.5, '60 chronompl' );
}
{
    my $newick = '((((A,B),C),(D,F)),E);';
    my $tree = parse( '-format' => 'newick', '-string' => $newick )->first;
    ok( $tree->is_ultrametric, '61 grafen branch lengths' );
}
{
    my $newick = '((((((A:6,B:6):5,C:11):4,D:15):3,E:18):2,F:20):1,G:21):0;';
    my $tree   = parse( '-format' => 'newick', '-string' => $newick )->first;
    my $bt     = $tree->calc_waiting_times;
    for my $i ( 0 .. $#{$bt} ) {
        is( $bt->[$i]->[1], $i, '62 waiting times' );
    }
}
{
    my $newick = '(a,b,c,d,e,f);';
    my $tree = parse( '-format' => 'newick', '-string' => $newick )->first;
    $tree->sort_tips( [qw(a c b f d e)] );
    ok( $tree->to_newick eq '(a,c,b,f,d,e);', '63 star sort' );
}
{
    my $newick = '(a,b,(c,d),e,f);';
    my $tree = parse( '-format' => 'newick', '-string' => $newick )->first;
    $tree->sort_tips( [qw(a b d c e f)] );
    ok( $tree->to_newick eq '(a,b,(d,c),e,f);', '64 tip sort' );
}
{
    my $newick = '(a,b,((c,d),e),f);';
    my $tree = parse( '-format' => 'newick', '-string' => $newick )->first;
    $tree->sort_tips( [qw(a b e d c f)] );
    ok( $tree->to_newick eq '(a,b,(e,(d,c)),f);', '65 simple ladder sort' );
}
{
    my $newick = '((a,b),((c,d),e),f);';
    my $tree = parse( '-format' => 'newick', '-string' => $newick )->first;
    $tree->sort_tips( [qw(a e d c b f)] );
    ok( $tree->to_newick eq '((e,(d,c)),(a,b),f);', '66 conflict sort' );
}
{
    my $newick = '((a,b),((c,d),e),f);';
    my $tree = parse( '-format' => 'newick', '-string' => $newick )->first;
    my @cherries = @{ $tree->get_cherries };
    ok( scalar(@cherries) == 2, '67 get cherries' );
}

# Try pruning
{
    my $newick = '((A,(C,X)Int1)LUCA);';
    my $tree = parse( '-format' => 'newick', '-string' => $newick )->first;
    my $root = $tree->get_root->set_name('root');
    my @names = sort map {$_->get_name} @{$tree->get_entities};
    ok( $tree->keep_tips(\@names), 'pruning' );
    my @pruned_names = sort map {$_->get_name} @{$tree->get_entities};
    is_deeply(\@pruned_names, \@names);
    @names = ('A', 'C');
    ok( $tree->keep_tips(\@names) );
    @pruned_names = sort map {$_->get_name} @{$tree->get_entities};
    is_deeply( \@pruned_names, [@names, 'LUCA', 'root']);
}

__DATA__
((H:1,I:1):1,(G:1,(F:0.01,(E:0.3,(D:2,(C:0.1,(A:1,B:1)cherry:1):1):1):1):1):1):0;
(H:1,(G:1,(F:1,((C:1,(A:1,B:1):1):1,(D:1,E:1):1):1):1):1):0;
(H:1,(G:1,(F:1,((C:1,(A:1,I:1,B:1)poly:1):1,(D:1,E:1):1):1):1):1):0;
((((A,B),(C,D)),(E,F)),((G,H),(I,J)));