<commit_before>=head1 NAME

ProteinTree - DESCRIPTION of Object

=head1 SYNOPSIS

=head1 DESCRIPTION

Specific subclass of NestedSet to add functionality when the nodes of this tree
are GenomicAlign objects and the tree is a representation of a Protein derived
Phylogenetic tree

=head1 CONTACT

  Contact <NAME> on implemetation/design detail: <EMAIL>
  Contact <NAME> on EnsEMBL compara project: <EMAIL>
  Contact <NAME> on EnsEMBL in general: <EMAIL>

=head1 APPENDIX

The rest of the documentation details each of the object methods. Internal methods are usually preceded with a _

=cut



package Bio::EnsEMBL::Compara::GenomicAlignTree;

use strict;
use Bio::EnsEMBL::Compara::BaseRelation;
use Bio::EnsEMBL::Utils::Argument;
use Bio::EnsEMBL::Utils::Exception;
use Bio::SimpleAlign;
use IO::File;

use Bio::EnsEMBL::Compara::NestedSet;
use Bio::EnsEMBL::Compara::GenomicAlignBlock;
our @ISA = qw(Bio::EnsEMBL::Compara::NestedSet Bio::EnsEMBL::Compara::GenomicAlignBlock);
# our @ISA = qw(Bio::EnsEMBL::Compara::NestedSet);


=head2 left_node_id

  Arg [1]     : (optional) $left_node_id
  Example     : $object->left_node_id($left_node_id);
  Example     : $left_node_id = $object->left_node_id();
  Description : Getter/setter for the left_node_id attribute
  Returntype  : integer
  Exceptions  : none
  Caller      : general
  Status      : Stable

=cut

sub left_node_id {
  my $self = shift;
  if (@_) {
    $self->{_left_node_id} = shift;
  }
  return $self->{_left_node_id};
}


=head2 right_node_id

  Arg [1]     : (optional) $right_node_id
  Example     : $object->right_node_id($right_node_id);
  Example     : $right_node_id = $object->right_node_id();
  Description : Getter/setter for the right_node_id attribute
  Returntype  : integer
  Exceptions  : none
  Caller      : general
  Status      : Stable

=cut

sub right_node_id {
  my $self = shift;
  if (@_) {
    $self->{_right_node_id} = shift;
  }
  return $self->{_right_node_id};
}


=head2 left_node

  Arg [1]     : -none-
  Example     : $left_node = $object->left_node();
  Description : Get the left_node object from the database.
  Returntype  : Bio::EnsEMBL::Compara::GenomicAlignTree object
  Exceptions  : none
  Caller      : general
  Status      : Stable

=cut

sub left_node {
  my $self = shift;
  if ($self->{_left_node_id} and $self->adaptor) {
    return $self->adaptor->fetch_node_by_node_id($self->{_left_node_id});
  }
  return undef;
}


=head2 right_node

  Arg [1]     : -none-
  Example     : $left_node = $object->right_node();
  Description : Get the right_node object from the database.
  Returntype  : Bio::EnsEMBL::Compara::GenomicAlignTree object
  Exceptions  : none
  Caller      : general
  Status      : Stable

=cut

sub right_node {
  my $self = shift;
  if ($self->{_right_node_id} and $self->adaptor) {
    return $self->adaptor->fetch_node_by_node_id($self->{_right_node_id});
  }
  return undef;
}


=head2 get_original_strand

  Args       : -none-
  Example    : if (!$genomic_align_tree->get_original_strand()) {
                 # original GenomicAlignTree has been reverse-complemented
               }
  Description: getter for the _orignal_strand attribute
  Returntype : none
  Exceptions : none
  Caller     : general
  Status     : At risk

=cut

sub get_original_strand {
  my ($self) = @_;

  if (!defined($self->{_original_strand})) {
    $self->{_original_strand} = 1;
  }

  return $self->{_original_strand};
}


=head2 ancestral_genomic_align_block_id

  Arg [1]     : (optional) $ancestral_genomic_align_block_id
  Example     : $object->ancestral_genomic_align_block_id($ancestral_genomic_align_block_id);
  Example     : $ancestral_genomic_align_block_id = $object->ancestral_genomic_align_block_id();
  Description : Getter/setter for the ancestral_genomic_align_block_id attribute
                This attribute is intended for the root of the tree only!
  Returntype  : int
  Exceptions  : none
  Caller      : general
  Status      : Stable

=cut

sub ancestral_genomic_align_block_id {
  my $self = shift;
  if (@_) {
    $self->{_ancestral_genomic_align_block_id} = shift;
  }
  return $self->{_ancestral_genomic_align_block_id};
}


=head2 modern_genomic_align_block_id

  Arg [1]     : (optional) $modern_genomic_align_block_id
  Example     : $object->modern_genomic_align_block_id($modern_genomic_align_block_id);
  Example     : $modern_genomic_align_block_id = $object->modern_genomic_align_block_id();
  Description : Getter/setter for the modern_genomic_align_block_id attribute
  Returntype  : 
  Exceptions  : none
  Caller      : general
  Status      : Stable

=cut

sub modern_genomic_align_block_id {
  my $self = shift;
  if (@_) {
    $self->{_modern_genomic_align_block_id} = shift;
  }
  return $self->{_modern_genomic_align_block_id};
}


=head2 genomic_align_group

  Arg [1]     : (optional) $genomic_align_group
  Example     : $object->genomic_align_group($genomic_align_group);
  Example     : $genomic_align_group = $object->genomic_align_group();
  Description : Getter/setter for the genomic_align_group attribute
  Returntype  : Bio::EnsEMBL::Compara::GenomicAlignGroup object
  Exceptions  : none
  Caller      : general
  Status      : Stable

=cut

sub genomic_align_group {
  my $self = shift;
  if (@_) {
    $self->{_genomic_align_group} = shift;
  }
  return $self->{_genomic_align_group};
}


=head2 get_all_GenomicAligns

  Arg [1]     : -none-
  Example     : $genomic_aligns = $object->get_all_GenomicAligns
  Description : Getter for all the GenomicAligns contained in the
                genomic_align_group object. This method is a short
                cut for $object->genomic_align_group->get_all_GenomicAligns()
  Returntype  : listref of Bio::EnsEMBL::Compara::GenomicAlign objects
  Exceptions  : none
  Caller      : general
  Status      : Stable

=cut

sub get_all_GenomicAligns {
  my $self = shift(@_);
  return [] if (!$self->genomic_align_group);
  return $self->genomic_align_group->get_all_GenomicAligns;
}


=head2 reference_genomic_align

  Arg [1]     : (optional) $reference_genomic_align
  Example     : $object->reference_genomic_align($reference_genomic_align);
  Example     : $reference_genomic_align = $object->reference_genomic_align();
  Description : Getter/setter for the reference_genomic_align attribute
  Returntype  : Bio::EnsEMBL::Compara::GenomicAlign object
  Exceptions  : none
  Caller      : general
  Status      : Stable

=cut

sub reference_genomic_align {
  my $self = shift;

  if (@_) {
    $self->{reference_genomic_align} = shift;
  }

  return $self->{reference_genomic_align};
}


=head2 reference_genomic_align_node

  Arg [1]     : (optional) $reference_genomic_align_node
  Example     : $object->reference_genomic_align_node($reference_genomic_align_node);
  Example     : $reference_genomic_align_node = $object->reference_genomic_align_node();
  Description : Getter/setter for the reference_genomic_align_node attribute
  Returntype  : Bio::EnsEMBL::Compara::GenomicAlignTree object
  Exceptions  : none
  Caller      : general
  Status      : Stable

=cut

sub reference_genomic_align_node {
  my $self = shift;

  if (@_) {
    $self->{reference_genomic_align_node} = shift;
  }

  return $self->{reference_genomic_align_node};
}


=head2 aligned_sequence

  Arg [1]     : -none-
  Example     : $aligned_sequence = $object->aligned_sequence();
  Description : Get the aligned sequence for this node. When the node
                contains one single sequence, it returns its aligned sequence.
                For composite segments, it returns the combined aligned seq.
  Returntype  : string
  Exceptions  : none
  Caller      : general
  Status      : Stable

=cut

sub aligned_sequence {
  my $self = shift;
  return $self->genomic_align_group->aligned_sequence(@_);
}


=head2 group_id

  Arg [1]    : integer $group_id
  Example    : my $group_id = $genomic_align_tree->group_id;
  Example    : $genomic_align_tree->group_id(1234);
  Description: get/set for attribute group_id of the underlying
               GenomicAlignBlock objects
  Returntype : integer
  Exceptions : A GenomicAlignTree is made of two GenomicAlignBlock
               object. The method fail when gettign the value if the
               two group_ids do not match
  Caller     : general

=cut

sub group_id {
    my ($self, $group_id) = @_;
    
    if (defined($group_id)) {
        $self->{'group_id'} = $group_id;
        # Set the group_id on the genomic_align_blocks...
        my %genomic_align_blocks;
        #foreach my $this_genomic_align_node (@{$self->get_all_sorted_genomic_align_nodes()}) {
        foreach my $this_genomic_align_node (@{$self->get_all_nodes()}) {
	    next if (!defined $this_genomic_align_node->genomic_align_group);
	    foreach my $genomic_align (@{$this_genomic_align_node->genomic_align_group->get_all_GenomicAligns}) {
		my $this_genomic_align_block = $genomic_align->genomic_align_block;
		if ($this_genomic_align_block and !defined($genomic_align_blocks{$this_genomic_align_block})) {
		    $this_genomic_align_block->group_id($group_id);
		    $genomic_align_blocks{$this_genomic_align_block} = 1;
		}
	    }
	}
    } elsif (!defined($self->{'group_id'}) and defined($self->{adaptor})) {
        # Try to get the ID from other sources...
        my %group_ids;
        my $genomic_align_block_adaptor = $self->adaptor->dba->get_GenomicAlignBlockAdaptor;
        foreach my $this_genomic_align_node (@{$self->get_all_nodes()}) {
	    next if (!defined $this_genomic_align_node->genomic_align_group);
	    foreach my $genomic_align (@{$this_genomic_align_node->genomic_align_group->get_all_GenomicAligns}) {
		my $this_genomic_align_block_id = $genomic_align->genomic_align_block_id;
		my $this_genomic_align_block = $genomic_align_block_adaptor->fetch_by_dbID($this_genomic_align_block_id);
		if ($this_genomic_align_block->group_id) {
		    $group_ids{$this_genomic_align_block->group_id} = 1;
		} else {
		    $group_ids{"undef"} = 1;
		}
	    }
        }
        if (keys %group_ids == 1) {
	    if (!defined($group_ids{"undef"})) {
		$self->{'group_id'} = (keys %group_ids)[0];
	    }
        } else {
	    warning("Different group_ids found for this GenomicAlignTree\n");
        }
    }
    return $self->{'group_id'};
}


=head2 name

  Arg [1]     : (optional) string $name
  Example     : $object->name($name);
  Example     : $name = $object->name();
  Description : Getter/setter for the name attribute.
  Returntype  : string
  Exceptions  : none
  Caller      : general
  Status      : At risk

=cut

sub name {
  my $self = shift;

  if (@_) {
    $self->{_name} = shift;
  } elsif (!$self->{_name}) {
    my $genomic_align_group = $self->genomic_align_group;
    if (defined($self->SUPER::name()) and $self->SUPER::name() ne "") {
      ## Uses the name defined before blessing this object as a
      ## Bio::EnsEMBL::Compara::GenomicAlignTree in the Ortheus pipeline
      $self->{_name} = $self->SUPER::name();
    } elsif ($self->is_leaf) {
    	my $gdb_name;
      if($genomic_align_group->genome_db->name =~ /(.)[^ ]+ (.{3})/) {
      	$gdb_name = "${1}${2}";
      }
      else {
      	$gdb_name = $genomic_align_group->genome_db->name();
      	$gdb_name =~ tr/_//;
      }
      $self->{_name} = $gdb_name.'_'.$genomic_align_group->dnafrag->name."_".
          $genomic_align_group->dnafrag_start."_".$genomic_align_group->dnafrag_end."[".
          (($genomic_align_group->dnafrag_strand eq "-1")?"-":"+")."]";
    } else {
      $self->{_name} = join("-", map {
      	my $name = $_->genomic_align_group->genome_db->name;
      	if($name =~ /(.)[^ ]+ (.{3})/) {
      		$name = "$1$2";
      	}
      	else {
      		$name =~ tr/_//; 	
      	} 
      	$name;
      } @{$self->get_all_leaves})."[".scalar(@{$self->get_all_leaves})."]";
    }
  }

  return $self->{_name};
}


=head2 get_all_sorted_genomic_align_nodes

  Arg [1]     : (optional) Bio::EnsEMBL::Compara::GenomicAlignTree $reference_genomic_align_node
  Example     : $object->get_all_sorted_genomic_align_nodes($ref_genomic_align_node);
  Example     : $nodes = $object->get_all_sorted_genomic_align_nodes();
  Description : If ref_genomic_align_node is set, sorts the tree based on the
                reference_genomic_align_node
                If ref_genomic_align_node is not set, sorts the tree based on
                the species name
  Returntype  : Bio::EnsEMBL::Compara::GenomicAlignTree
  Exceptions  : none
  Caller      : general
  Status      : At risk

=cut

sub get_all_sorted_genomic_align_nodes {
  my ($self, $reference_genomic_align_node) = @_;
  my $sorted_genomic_align_nodes = [];

  if (!$reference_genomic_align_node and $self->reference_genomic_align_node) {
    $reference_genomic_align_node = $self->reference_genomic_align_node;
  }

  my $children = $self->children;
  if (@$children >= 1) {
    $children = [sort _sort_children @$children];
    push(@$sorted_genomic_align_nodes, @{$children->[0]->get_all_sorted_genomic_align_nodes(
            $reference_genomic_align_node)});
    push(@$sorted_genomic_align_nodes, $self);
    for (my $i = 1; $i < @$children; $i++) {
      push(@$sorted_genomic_align_nodes, @{$children->[$i]->get_all_sorted_genomic_align_nodes(
              $reference_genomic_align_node)});
    }
  } elsif (@$children == 0) {
    push(@$sorted_genomic_align_nodes, $self);
  }

  return $sorted_genomic_align_nodes;
}


=head2 restrict_between_alignment_positions

  Arg[1]     : [optional] int $start, refers to the start of the alignment
  Arg[2]     : [optional] int $end, refers to the start of the alignment
  Arg[3]     : [optional] boolean $skip_empty_GenomicAligns
  Example    : none
  Description: restrict this GenomicAlignBlock. It returns a new object unless no
               restriction is needed. In that case, it returns the original unchanged
               object.
               This method uses coordinates relative to the alignment itself.
               For instance if you have an alignment like:
                            1    1    2    2    3
                   1   5    0    5    0    5    0
                   AAC--CTTGTGGTA-CTACTT-----ACTTT
                   AACCCCTT-TGGTATCTACTTACCTAACTTT
               and you restrict it between 5 and 25, you will get back a
               object containing the following alignment:
                            1    1
                   1   5    0    5
                   CTTGTGGTA-CTACTT----
                   CTT-TGGTATCTACTTACCT

               See restrict_between_reference_positions() elsewhere in this document
               for an alternative method using absolute genomic coordinates.

               NB: This method works only for GenomicAlignBlock which have been
               fetched from the DB as it is adjusting the dnafrag coordinates
               and the cigar_line only and not the actual sequences stored in the
               object if any. If you want to restrict an object with no coordinates
               a simple substr() will do!

  Returntype : Bio::EnsEMBL::Compara::GenomicAlignBlock object
  Exceptions : none
  Caller     : general
  Status     : At risk

=cut

sub restrict_between_alignment_positions {
  my ($self, $start, $end, $skip_empty_GenomicAligns, $reference_genomic_align) = @_;
  my $genomic_align_tree;
  $genomic_align_tree = $self->copy();
  $genomic_align_tree->adaptor($self->adaptor);

  foreach my $this_node (@{$genomic_align_tree->get_all_nodes}) {
    my $genomic_align_group = $this_node->genomic_align_group;
    next if (!$genomic_align_group);
    my $new_genomic_aligns = [];
    my $length = $this_node->length;
    foreach my $this_genomic_align (@{$genomic_align_group->get_all_GenomicAligns}) {
      my $restricted_genomic_align = $this_genomic_align->restrict($start, $end, $length);
      if ($genomic_align_tree->reference_genomic_align eq $this_genomic_align) {
        ## Update the reference_genomic_align
        $genomic_align_tree->reference_genomic_align($restricted_genomic_align);
        $genomic_align_tree->reference_genomic_align_node($this_node);
      }
      if (!$skip_empty_GenomicAligns or
          $restricted_genomic_align->dnafrag_start <= $restricted_genomic_align->dnafrag_end
          ) {
        ## Always skip composite segments outside of the range of restriction
        ## The cigar_line will contain only X's
        next if ($restricted_genomic_align->cigar_line =~ /^\d*X$/);

        $restricted_genomic_align->genomic_align_block($genomic_align_tree);
        push(@$new_genomic_aligns, $restricted_genomic_align);
      }
    }
    if (@$new_genomic_aligns) {
      $genomic_align_group->{genomic_align_array} = undef;
      foreach my $this_genomic_align (@$new_genomic_aligns) {
        $genomic_align_group->add_GenomicAlign($this_genomic_align);
      }
    } else {
      $this_node->disavow_parent();
      my $reference_genomic_align = $genomic_align_tree->reference_genomic_align;
      my $reference_genomic_align_node = $genomic_align_tree->reference_genomic_align_node;
      $genomic_align_tree = $genomic_align_tree->minimize_tree();
      ## Make sure links are not broken after tree minimization
      $genomic_align_tree->reference_genomic_align($reference_genomic_align);
      $genomic_align_tree->reference_genomic_align->genomic_align_block($genomic_align_tree);
      $genomic_align_tree->reference_genomic_align_node($reference_genomic_align_node);
    }
  }

  return $genomic_align_tree;
}


=head2 restrict_between_reference_positions

  Arg[1]     : [optional] int $start, refers to the reference_dnafrag
  Arg[2]     : [optional] int $end, refers to the reference_dnafrag
  Arg[3]     : [optional] Bio::EnsEMBL::Compara::GenomicAlign $reference_GenomicAlign
  Arg[4]     : [optional] boolean $skip_empty_GenomicAligns [ALWAYS FALSE]
  Example    : none
  Description: restrict this GenomicAlignBlock. It returns a new object unless no
               restriction is needed. In that case, it returns the original unchanged
               object
               It might be the case that the restricted region coincide with a gap
               in one or several GenomicAligns. By default these GenomicAligns are
               returned with a dnafrag_end equals to its dnafrag_start + 1. For instance,
               a GenomicAlign with dnafrag_start = 12345 and dnafrag_end = 12344
               correspond to a block which goes on this region from before 12345 to
               after 12344, ie just between 12344 and 12345. You can choose to remove
               these empty GenomicAligns by setting $skip_empty_GenomicAligns to any
               true value.
  Returntype : Bio::EnsEMBL::Compara::GenomicAlignBlock object in scalar context. In<commit_msg>Added check if  exists before trying to use it.<commit_after>=head1 NAME

ProteinTree - DESCRIPTION of Object

=head1 SYNOPSIS

=head1 DESCRIPTION

Specific subclass of NestedSet to add functionality when the nodes of this tree
are GenomicAlign objects and the tree is a representation of a Protein derived
Phylogenetic tree

=head1 CONTACT

  Contact <NAME> on implemetation/design detail: <EMAIL>
  Contact <NAME> on EnsEMBL compara project: <EMAIL>
  Contact <NAME> on EnsEMBL in general: <EMAIL>

=head1 APPENDIX

The rest of the documentation details each of the object methods. Internal methods are usually preceded with a _

=cut



package Bio::EnsEMBL::Compara::GenomicAlignTree;

use strict;
use Bio::EnsEMBL::Compara::BaseRelation;
use Bio::EnsEMBL::Utils::Argument;
use Bio::EnsEMBL::Utils::Exception;
use Bio::SimpleAlign;
use IO::File;

use Bio::EnsEMBL::Compara::NestedSet;
use Bio::EnsEMBL::Compara::GenomicAlignBlock;
our @ISA = qw(Bio::EnsEMBL::Compara::NestedSet Bio::EnsEMBL::Compara::GenomicAlignBlock);
# our @ISA = qw(Bio::EnsEMBL::Compara::NestedSet);


=head2 left_node_id

  Arg [1]     : (optional) $left_node_id
  Example     : $object->left_node_id($left_node_id);
  Example     : $left_node_id = $object->left_node_id();
  Description : Getter/setter for the left_node_id attribute
  Returntype  : integer
  Exceptions  : none
  Caller      : general
  Status      : Stable

=cut

sub left_node_id {
  my $self = shift;
  if (@_) {
    $self->{_left_node_id} = shift;
  }
  return $self->{_left_node_id};
}


=head2 right_node_id

  Arg [1]     : (optional) $right_node_id
  Example     : $object->right_node_id($right_node_id);
  Example     : $right_node_id = $object->right_node_id();
  Description : Getter/setter for the right_node_id attribute
  Returntype  : integer
  Exceptions  : none
  Caller      : general
  Status      : Stable

=cut

sub right_node_id {
  my $self = shift;
  if (@_) {
    $self->{_right_node_id} = shift;
  }
  return $self->{_right_node_id};
}


=head2 left_node

  Arg [1]     : -none-
  Example     : $left_node = $object->left_node();
  Description : Get the left_node object from the database.
  Returntype  : Bio::EnsEMBL::Compara::GenomicAlignTree object
  Exceptions  : none
  Caller      : general
  Status      : Stable

=cut

sub left_node {
  my $self = shift;
  if ($self->{_left_node_id} and $self->adaptor) {
    return $self->adaptor->fetch_node_by_node_id($self->{_left_node_id});
  }
  return undef;
}


=head2 right_node

  Arg [1]     : -none-
  Example     : $left_node = $object->right_node();
  Description : Get the right_node object from the database.
  Returntype  : Bio::EnsEMBL::Compara::GenomicAlignTree object
  Exceptions  : none
  Caller      : general
  Status      : Stable

=cut

sub right_node {
  my $self = shift;
  if ($self->{_right_node_id} and $self->adaptor) {
    return $self->adaptor->fetch_node_by_node_id($self->{_right_node_id});
  }
  return undef;
}


=head2 get_original_strand

  Args       : -none-
  Example    : if (!$genomic_align_tree->get_original_strand()) {
                 # original GenomicAlignTree has been reverse-complemented
               }
  Description: getter for the _orignal_strand attribute
  Returntype : none
  Exceptions : none
  Caller     : general
  Status     : At risk

=cut

sub get_original_strand {
  my ($self) = @_;

  if (!defined($self->{_original_strand})) {
    $self->{_original_strand} = 1;
  }

  return $self->{_original_strand};
}


=head2 ancestral_genomic_align_block_id

  Arg [1]     : (optional) $ancestral_genomic_align_block_id
  Example     : $object->ancestral_genomic_align_block_id($ancestral_genomic_align_block_id);
  Example     : $ancestral_genomic_align_block_id = $object->ancestral_genomic_align_block_id();
  Description : Getter/setter for the ancestral_genomic_align_block_id attribute
                This attribute is intended for the root of the tree only!
  Returntype  : int
  Exceptions  : none
  Caller      : general
  Status      : Stable

=cut

sub ancestral_genomic_align_block_id {
  my $self = shift;
  if (@_) {
    $self->{_ancestral_genomic_align_block_id} = shift;
  }
  return $self->{_ancestral_genomic_align_block_id};
}


=head2 modern_genomic_align_block_id

  Arg [1]     : (optional) $modern_genomic_align_block_id
  Example     : $object->modern_genomic_align_block_id($modern_genomic_align_block_id);
  Example     : $modern_genomic_align_block_id = $object->modern_genomic_align_block_id();
  Description : Getter/setter for the modern_genomic_align_block_id attribute
  Returntype  : 
  Exceptions  : none
  Caller      : general
  Status      : Stable

=cut

sub modern_genomic_align_block_id {
  my $self = shift;
  if (@_) {
    $self->{_modern_genomic_align_block_id} = shift;
  }
  return $self->{_modern_genomic_align_block_id};
}


=head2 genomic_align_group

  Arg [1]     : (optional) $genomic_align_group
  Example     : $object->genomic_align_group($genomic_align_group);
  Example     : $genomic_align_group = $object->genomic_align_group();
  Description : Getter/setter for the genomic_align_group attribute
  Returntype  : Bio::EnsEMBL::Compara::GenomicAlignGroup object
  Exceptions  : none
  Caller      : general
  Status      : Stable

=cut

sub genomic_align_group {
  my $self = shift;
  if (@_) {
    $self->{_genomic_align_group} = shift;
  }
  return $self->{_genomic_align_group};
}


=head2 get_all_GenomicAligns

  Arg [1]     : -none-
  Example     : $genomic_aligns = $object->get_all_GenomicAligns
  Description : Getter for all the GenomicAligns contained in the
                genomic_align_group object. This method is a short
                cut for $object->genomic_align_group->get_all_GenomicAligns()
  Returntype  : listref of Bio::EnsEMBL::Compara::GenomicAlign objects
  Exceptions  : none
  Caller      : general
  Status      : Stable

=cut

sub get_all_GenomicAligns {
  my $self = shift(@_);
  return [] if (!$self->genomic_align_group);
  return $self->genomic_align_group->get_all_GenomicAligns;
}


=head2 reference_genomic_align

  Arg [1]     : (optional) $reference_genomic_align
  Example     : $object->reference_genomic_align($reference_genomic_align);
  Example     : $reference_genomic_align = $object->reference_genomic_align();
  Description : Getter/setter for the reference_genomic_align attribute
  Returntype  : Bio::EnsEMBL::Compara::GenomicAlign object
  Exceptions  : none
  Caller      : general
  Status      : Stable

=cut

sub reference_genomic_align {
  my $self = shift;

  if (@_) {
    $self->{reference_genomic_align} = shift;
  }

  return $self->{reference_genomic_align};
}


=head2 reference_genomic_align_node

  Arg [1]     : (optional) $reference_genomic_align_node
  Example     : $object->reference_genomic_align_node($reference_genomic_align_node);
  Example     : $reference_genomic_align_node = $object->reference_genomic_align_node();
  Description : Getter/setter for the reference_genomic_align_node attribute
  Returntype  : Bio::EnsEMBL::Compara::GenomicAlignTree object
  Exceptions  : none
  Caller      : general
  Status      : Stable

=cut

sub reference_genomic_align_node {
  my $self = shift;

  if (@_) {
    $self->{reference_genomic_align_node} = shift;
  }

  return $self->{reference_genomic_align_node};
}


=head2 aligned_sequence

  Arg [1]     : -none-
  Example     : $aligned_sequence = $object->aligned_sequence();
  Description : Get the aligned sequence for this node. When the node
                contains one single sequence, it returns its aligned sequence.
                For composite segments, it returns the combined aligned seq.
  Returntype  : string
  Exceptions  : none
  Caller      : general
  Status      : Stable

=cut

sub aligned_sequence {
  my $self = shift;
  return $self->genomic_align_group->aligned_sequence(@_);
}


=head2 group_id

  Arg [1]    : integer $group_id
  Example    : my $group_id = $genomic_align_tree->group_id;
  Example    : $genomic_align_tree->group_id(1234);
  Description: get/set for attribute group_id of the underlying
               GenomicAlignBlock objects
  Returntype : integer
  Exceptions : A GenomicAlignTree is made of two GenomicAlignBlock
               object. The method fail when gettign the value if the
               two group_ids do not match
  Caller     : general

=cut

sub group_id {
    my ($self, $group_id) = @_;
    
    if (defined($group_id)) {
        $self->{'group_id'} = $group_id;
        # Set the group_id on the genomic_align_blocks...
        my %genomic_align_blocks;
        #foreach my $this_genomic_align_node (@{$self->get_all_sorted_genomic_align_nodes()}) {
        foreach my $this_genomic_align_node (@{$self->get_all_nodes()}) {
	    next if (!defined $this_genomic_align_node->genomic_align_group);
	    foreach my $genomic_align (@{$this_genomic_align_node->genomic_align_group->get_all_GenomicAligns}) {
		my $this_genomic_align_block = $genomic_align->genomic_align_block;
		if ($this_genomic_align_block and !defined($genomic_align_blocks{$this_genomic_align_block})) {
		    $this_genomic_align_block->group_id($group_id);
		    $genomic_align_blocks{$this_genomic_align_block} = 1;
		}
	    }
	}
    } elsif (!defined($self->{'group_id'}) and defined($self->{adaptor})) {
        # Try to get the ID from other sources...
        my %group_ids;
        my $genomic_align_block_adaptor = $self->adaptor->dba->get_GenomicAlignBlockAdaptor;
        foreach my $this_genomic_align_node (@{$self->get_all_nodes()}) {
	    next if (!defined $this_genomic_align_node->genomic_align_group);
	    foreach my $genomic_align (@{$this_genomic_align_node->genomic_align_group->get_all_GenomicAligns}) {
		my $this_genomic_align_block_id = $genomic_align->genomic_align_block_id;
		my $this_genomic_align_block = $genomic_align_block_adaptor->fetch_by_dbID($this_genomic_align_block_id);
		if ($this_genomic_align_block->group_id) {
		    $group_ids{$this_genomic_align_block->group_id} = 1;
		} else {
		    $group_ids{"undef"} = 1;
		}
	    }
        }
        if (keys %group_ids == 1) {
	    if (!defined($group_ids{"undef"})) {
		$self->{'group_id'} = (keys %group_ids)[0];
	    }
        } else {
	    warning("Different group_ids found for this GenomicAlignTree\n");
        }
    }
    return $self->{'group_id'};
}


=head2 name

  Arg [1]     : (optional) string $name
  Example     : $object->name($name);
  Example     : $name = $object->name();
  Description : Getter/setter for the name attribute.
  Returntype  : string
  Exceptions  : none
  Caller      : general
  Status      : At risk

=cut

sub name {
  my $self = shift;

  if (@_) {
    $self->{_name} = shift;
  } elsif (!$self->{_name}) {
    my $genomic_align_group = $self->genomic_align_group;
    if (defined($self->SUPER::name()) and $self->SUPER::name() ne "") {
      ## Uses the name defined before blessing this object as a
      ## Bio::EnsEMBL::Compara::GenomicAlignTree in the Ortheus pipeline
      $self->{_name} = $self->SUPER::name();
    } elsif ($self->is_leaf) {
    	my $gdb_name;
      if($genomic_align_group->genome_db->name =~ /(.)[^ ]+ (.{3})/) {
      	$gdb_name = "${1}${2}";
      }
      else {
      	$gdb_name = $genomic_align_group->genome_db->name();
      	$gdb_name =~ tr/_//;
      }
      $self->{_name} = $gdb_name.'_'.$genomic_align_group->dnafrag->name."_".
          $genomic_align_group->dnafrag_start."_".$genomic_align_group->dnafrag_end."[".
          (($genomic_align_group->dnafrag_strand eq "-1")?"-":"+")."]";
    } else {
      $self->{_name} = join("-", map {
      	my $name = $_->genomic_align_group->genome_db->name;
      	if($name =~ /(.)[^ ]+ (.{3})/) {
      		$name = "$1$2";
      	}
      	else {
      		$name =~ tr/_//; 	
      	} 
      	$name;
      } @{$self->get_all_leaves})."[".scalar(@{$self->get_all_leaves})."]";
    }
  }

  return $self->{_name};
}


=head2 get_all_sorted_genomic_align_nodes

  Arg [1]     : (optional) Bio::EnsEMBL::Compara::GenomicAlignTree $reference_genomic_align_node
  Example     : $object->get_all_sorted_genomic_align_nodes($ref_genomic_align_node);
  Example     : $nodes = $object->get_all_sorted_genomic_align_nodes();
  Description : If ref_genomic_align_node is set, sorts the tree based on the
                reference_genomic_align_node
                If ref_genomic_align_node is not set, sorts the tree based on
                the species name
  Returntype  : Bio::EnsEMBL::Compara::GenomicAlignTree
  Exceptions  : none
  Caller      : general
  Status      : At risk

=cut

sub get_all_sorted_genomic_align_nodes {
  my ($self, $reference_genomic_align_node) = @_;
  my $sorted_genomic_align_nodes = [];

  if (!$reference_genomic_align_node and $self->reference_genomic_align_node) {
    $reference_genomic_align_node = $self->reference_genomic_align_node;
  }

  my $children = $self->children;
  if (@$children >= 1) {
    $children = [sort _sort_children @$children];
    push(@$sorted_genomic_align_nodes, @{$children->[0]->get_all_sorted_genomic_align_nodes(
            $reference_genomic_align_node)});
    push(@$sorted_genomic_align_nodes, $self);
    for (my $i = 1; $i < @$children; $i++) {
      push(@$sorted_genomic_align_nodes, @{$children->[$i]->get_all_sorted_genomic_align_nodes(
              $reference_genomic_align_node)});
    }
  } elsif (@$children == 0) {
    push(@$sorted_genomic_align_nodes, $self);
  }

  return $sorted_genomic_align_nodes;
}


=head2 restrict_between_alignment_positions

  Arg[1]     : [optional] int $start, refers to the start of the alignment
  Arg[2]     : [optional] int $end, refers to the start of the alignment
  Arg[3]     : [optional] boolean $skip_empty_GenomicAligns
  Example    : none
  Description: restrict this GenomicAlignBlock. It returns a new object unless no
               restriction is needed. In that case, it returns the original unchanged
               object.
               This method uses coordinates relative to the alignment itself.
               For instance if you have an alignment like:
                            1    1    2    2    3
                   1   5    0    5    0    5    0
                   AAC--CTTGTGGTA-CTACTT-----ACTTT
                   AACCCCTT-TGGTATCTACTTACCTAACTTT
               and you restrict it between 5 and 25, you will get back a
               object containing the following alignment:
                            1    1
                   1   5    0    5
                   CTTGTGGTA-CTACTT----
                   CTT-TGGTATCTACTTACCT

               See restrict_between_reference_positions() elsewhere in this document
               for an alternative method using absolute genomic coordinates.

               NB: This method works only for GenomicAlignBlock which have been
               fetched from the DB as it is adjusting the dnafrag coordinates
               and the cigar_line only and not the actual sequences stored in the
               object if any. If you want to restrict an object with no coordinates
               a simple substr() will do!

  Returntype : Bio::EnsEMBL::Compara::GenomicAlignBlock object
  Exceptions : none
  Caller     : general
  Status     : At risk

=cut

sub restrict_between_alignment_positions {
  my ($self, $start, $end, $skip_empty_GenomicAligns, $reference_genomic_align) = @_;
  my $genomic_align_tree;
  $genomic_align_tree = $self->copy();
  $genomic_align_tree->adaptor($self->adaptor);

  foreach my $this_node (@{$genomic_align_tree->get_all_nodes}) {
    my $genomic_align_group = $this_node->genomic_align_group;
    next if (!$genomic_align_group);
    my $new_genomic_aligns = [];
    my $length = $this_node->length;
    foreach my $this_genomic_align (@{$genomic_align_group->get_all_GenomicAligns}) {
      my $restricted_genomic_align = $this_genomic_align->restrict($start, $end, $length);
      if ($genomic_align_tree->reference_genomic_align eq $this_genomic_align) {
        ## Update the reference_genomic_align
        $genomic_align_tree->reference_genomic_align($restricted_genomic_align);
        $genomic_align_tree->reference_genomic_align_node($this_node);
      }
      if (!$skip_empty_GenomicAligns or
          $restricted_genomic_align->dnafrag_start <= $restricted_genomic_align->dnafrag_end
          ) {
        ## Always skip composite segments outside of the range of restriction
        ## The cigar_line will contain only X's
        next if ($restricted_genomic_align->cigar_line =~ /^\d*X$/);

        $restricted_genomic_align->genomic_align_block($genomic_align_tree);
        push(@$new_genomic_aligns, $restricted_genomic_align);
      }
    }
    if (@$new_genomic_aligns) {
      $genomic_align_group->{genomic_align_array} = undef;
      foreach my $this_genomic_align (@$new_genomic_aligns) {
        $genomic_align_group->add_GenomicAlign($this_genomic_align);
      }
    } else {
      $this_node->disavow_parent();
      my $reference_genomic_align = $genomic_align_tree->reference_genomic_align;
      if ($reference_genomic_align) {
	  my $reference_genomic_align_node = $genomic_align_tree->reference_genomic_align_node;
	  $genomic_align_tree = $genomic_align_tree->minimize_tree();
	  ## Make sure links are not broken after tree minimization
	  $genomic_align_tree->reference_genomic_align($reference_genomic_align);
	  $genomic_align_tree->reference_genomic_align->genomic_align_block($genomic_align_tree);
	  $genomic_align_tree->reference_genomic_align_node($reference_genomic_align_node);
      }
    }
  }

  return $genomic_align_tree;
}


=head2 restrict_between_reference_positions

  Arg[1]     : [optional] int $start, refers to the reference_dnafrag
  Arg[2]     : [optional] int $end, refers to the reference_dnafrag
  Arg[3]     : [optional] Bio::EnsEMBL::Compara::GenomicAlign $reference_GenomicAlign
  Arg[4]     : [optional] boolean $skip_empty_GenomicAligns [ALWAYS FALSE]
  Example    : none
  Description: restrict this GenomicAlignBlock. It returns a new object unless no
               restriction is needed. In that case, it returns the original unchanged
               object
               It might be the case that the restricted region coincide with a gap
               in one or several GenomicAligns. By default these GenomicAligns are
               returned with a dnafrag_end equals to its dnafrag_start + 1. For instance,
               a GenomicAlign with dnafrag_start = 12345 and dnafrag_end = 12344
               correspond to a block which goes on this region from before 12345 to
               after 12344, ie just between 12344 and 12345. You can choose to remove
               these empty GenomicAligns by setting $skip_empty_GenomicAligns to any
               true value.
  Returntype : Bio::EnsEMBL::Compara::GenomicAlignBlock object in scalar context. In
{'fraction_non_alphanumeric': 0.10296071865199596, 'fraction_numerical': 0.006165949077765375, 'mean_word_length': 3.3314147576838953, 'pattern_counts': {'":': 2, '<': 19, '<?xml version=': 0, '>': 295, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4256206', 'n_tokens_mistral': 12863, 'n_tokens_neox': 12298, 'n_words': 2739}