package statalign.base;
import static java.util.Arrays.*;
/**
*
* An AlignColumn is an object that represents an observed or a hidden character
* and stores a double array storing the Felsenstein likelihoods.
*
* AlignColumns are double chained to form an observed or hidden sequence.
* There are also links to their parent. If the character that AlignColumn represents
* is aligned to an ancestral characters, then the alignColumn is not <tt>orphan</tt>
* otherwise it is, and then its <tt>parent</tt> represents the first ancestral character
* to the right in the alignment column.
*
* @author miklos, novak, herman
*
*/
public class AlignColumn {
/**
* The owner tells the tree vertex where this AlignColumn belongs to.
*/
public Vertex owner;
/**
* This reference points to the parent of this AlignColumn
*/
public AlignColumn parent;
/**
* <p>If true, then this AlignColumn is inserted, and its <tt>parent</tt> is the
* first ancestral character to the right in the alignment
*
* <p>If it is false, then this AlignColumn is aligned to its parent
*/
public boolean orphan; /* if false, column has a true parent
if true, parent shows parent vertex's
next column in alignment */
boolean selected; /* if true, it is selected as a homologous column that is not changed
during changing topology
*/
boolean emptyWindow; /* if true, then it is the end of an empty window */
/**
* This reference points to the previous AlignColumn in the alignment
*/
public AlignColumn prev; // previous column in alignment
/**
* This reference points to the next AlignColumn in the alignment
*/
public AlignColumn next; // next column in alignment
/**
* This reference points to the left child of this AlignColumn
*/
public AlignColumn left; // left child in alignment
/**
* This reference points to the right child of this Aligncolumn
*/
public AlignColumn right; // right child in alignment
/**
* This double array contains the Felsenstein Likelihoods
*/
public double seq[]; // Felsenstein likelihoods of the column
/**
* This double array contains the upper Likelihoods
*/
public double upp[]; // upper likelihoods of the column
/**
* Vector indicating which characters are aligned to this column at the leaves below.
*/
public int aligned[];
/**
* Vector indicating which characters are aligned to the parent of this column.
*/
public int alignedUpp[];
/**
* It constructs a new AlignColumn. Sets only the owner, other fields are filled in outside of the
* constructor
* @param owner The vertex where this AlignColumn belongs to.
*/
public AlignColumn(Vertex owner){
this.owner = owner;
parent = null;
orphan = true;
}
/**
* Creates a new column as a clone of the <code>this</code>,
* copying (or cloning) all fields except <code>prev</code> and <code>next</code>.
*/
public AlignColumn clone() {
AlignColumn c = new AlignColumn(owner);
c.parent = parent;
c.orphan = orphan;
c.left = left;
c.right = right;
c.selected = selected;
c.emptyWindow = emptyWindow;
if (seq==null) c.seq = null;
else c.seq = seq.clone();
if (upp==null) c.upp = null;
else c.upp = upp.clone();
if (aligned==null) c.aligned = null;
else c.aligned = aligned.clone();
if (alignedUpp==null) c.alignedUpp = null;
else c.alignedUpp = alignedUpp.clone();
return c;
}
/**
* Copies all fields from column <code>c</code> into <code>this</code>,
* except for <code>prev</code> and <code>next</code>.
* @param c The column from which the data will be copied.
*/
void copy(AlignColumn c) {
owner = c.owner;
parent = c.parent;
orphan = c.orphan;
seq = c.seq;
upp = c.upp;
aligned = c.aligned;
aligned = c.aligned;
left = c.left;
right = c.right;
parent = c.parent;
selected = c.selected;
emptyWindow = c.emptyWindow;
}
/**
* It creates a new AlignColumn, chains it to the next column (namely, it is used to
* generate a new ancestral sequence built in a traceback phase of a dynamic programming).
* the owner is set to the same vertex as the owner of the next AlignColumn.
*
* @param next The next AlignColumn in the sequence that is being generated
* @param newSeq If true, then the constructor allocates memory for
* the seq[] array that contains the Felsenstein likelihoods. If false, then
* the seq[] array of other AlignColumn is associated to this Aligncolumn and reused.
* For speeding-up purposes.
*/
public AlignColumn(AlignColumn next, boolean newSeq) {
owner = next.owner;
parent = null;
orphan = true;
this.next = next;
next.prev = this;
if(newSeq) {
seq = new double[owner.owner.substitutionModel.e.length];
upp = new double[owner.owner.substitutionModel.e.length];
if(Utils.USE_MODEXT_EM) {
aligned = new int[owner.owner.vertex.length/2 + 1];
fill(aligned,-1);
}
if(Utils.USE_MODEXT_UPP) {
alignedUpp = new int[owner.owner.vertex.length/2 + 1];
fill(alignedUpp,-1);
}
}
}
public AlignColumn(AlignColumn next) {
owner = next.owner;
parent = next.parent;
left = null;
right = null;
orphan = true;
this.next = next;
prev = next.prev;
if (prev!=null) prev.next = this;
else owner.first = this;
next.prev = this;
seq = new double[owner.owner.substitutionModel.e.length];
upp = new double[owner.owner.substitutionModel.e.length];
if(Utils.USE_MODEXT_EM) {
aligned = new int[owner.owner.vertex.length/2 + 1];
fill(aligned,-1);
}
if(Utils.USE_MODEXT_UPP) {
alignedUpp = new int[owner.owner.vertex.length/2 + 1];
fill(alignedUpp,-1);
}
owner.length++;
}
/**
* Remove all non-parent references to a column, <code>p</code>.
* Any adopted children must be updated separately.
* @param p The column to be deleted.
*/
static boolean delete(AlignColumn p) {
// if (Utils.DEBUG) {
// if (p.left!=null && p.right!=null) {
// throw new IllegalArgumentException("A column cannot be deleted if it has two descendants.");
// }
// }
//if (Utils.DEBUG) {
if (p.owner.length<=Utils.MIN_SEQ_LENGTH) {
return false; // Don't allow it to delete if we have so few columns already
//throw new RuntimeException("All columns in sequence "+p.owner.index+" have been deleted.");
}
// System.out.println("Deleting column "+p.toString()+" at Vertex "+p.owner.index);
// System.out.println("p.prev = "+p.prev);
//}
if (!p.orphan) {
if (p==p.parent.left) p.parent.left = null;
else p.parent.right = null;
}
// Now skip column p in the parent sequence
if (p.prev != null) p.prev.next = p.next; else p.owner.first = p.next;
if (p.next != null) p.next.prev = p.prev;
if (p.left != null) { p.left.orphan = true; p.left.parent = null; }
if (p.right != null) { p.right.orphan = true; p.right.parent = null; }
// NB the reason for making the new orphan's parent field null
// is to allow the memory associated with p to be freed
// as soon as possible, rather than waiting for p.left and p.right
// to be adopted by another parent.
p.owner.length--;
return true;
}
/**
* Updates the parent/child pointers with a specified parent.
* @param p The parent column.
* @param isLeft Whether <code>this</code> is to be the left descendant of <code>p</code>.
*/
void updateParent(AlignColumn p, boolean isLeft) {
// Remove this node from the list of children of its old parent
if (p != parent) {
if (parent!=null) {
if (this==parent.left) parent.left = null;
else if (this==parent.right) parent.right = null;
else ; // the original parent had already taken a new child
}
parent = p;
}
//if (orphan) parent = p.next;
//else {
if (!orphan) {
if (isLeft) p.left = this;
else p.right = this;
}
}
void setWinFirst(AlignColumn prev) {
owner.winFirst = this;
this.prev = prev;
if(prev != null)
prev.next = this;
else
owner.first = this;
}
void saveLeft(AlignColumn copy) {
if(copy.left != null) {
left = copy.left;
}
selected = copy.selected;
emptyWindow = copy.emptyWindow;
}
void saveRight(AlignColumn copy) {
if(copy.right != null) {
right = copy.right;
}
selected = copy.selected;
emptyWindow = copy.emptyWindow;
}
void saveBoth(AlignColumn copy) {
saveLeft(copy);
saveRight(copy);
seq = copy.seq;
upp = copy.upp;
aligned = copy.aligned;
alignedUpp = copy.alignedUpp;
}
void saveParent(AlignColumn copy) {
if(copy.parent != null) {
parent = copy.parent;
orphan = copy.orphan;
if(!orphan) {
if(parent.left == copy)
parent.left = this;
else
parent.right = this;
}
}
}
AlignColumn brother() {
if(parent != null && !orphan) {
return parent.left == this ? parent.right : parent.left;
}
return null;
}
char mostLikely(){
// double max = 0.0;
// char character = '*';
// for(int i = 0; i < seq.length; i++){
// if(seq[i] > max){
// character = owner.owner.substitutionModel.alphabet[i];
// max = seq[i];
// s[1] += owner.substitutionModel.alphabet[i];
//found = true;
// }
// }
// return character;
return owner.owner.substitutionModel.mostLikely(seq);
}
/**
This function tells if a character is homolgous in all selected vertices
*/
boolean isHomologous(){
boolean x = true;
if(owner.left == null){
return true;
}
if(owner.left.selected && left != null){
x = x && left.isHomologous();
}
if(owner.left.selected && left == null){
return false;
}
if(owner.right.selected && right != null){
x = x && right.isHomologous();
}
if(owner.right.selected && right == null){
return false;
}
return x;
}
/**
This function tells if it is an empty window in all selected vertices
Works only for selected AlignColumns, since we dom't check if left and right exist
*/
boolean isEmptyWindow(){
boolean x = (prev == null || prev.selected);
if(owner.left != null && owner.left.selected){
x = x && left.isEmptyWindow();
}
if(owner.right != null && owner.right.selected){
x = x && right.isEmptyWindow();
}
return x;
}
void selectDown(){
selected = true;
if(owner.left != null && owner.left.selected){
left.selectDown();
}
if(owner.right != null && owner.right.selected){
right.selectDown();
}
}
void setEmptyWindowDown(boolean value){
emptyWindow = value;
if(owner.left != null && owner.left.selected){
left.setEmptyWindowDown(value);
}
if(owner.right != null && owner.right.selected){
right.setEmptyWindowDown(value);
}
}
/**
This function finds the beginning of the window in fast topology changing
*/
AlignColumn findWindowStart(){
AlignColumn a = this;
int length = 0;
// System.out.println("a: "+a+" selected: "+a.prev.selected);
while(a.prev != null && !(a.prev.selected && a.prev.emptyWindow)){
a = a.prev;
length ++;
}
owner.winLength = length;
return a;
}
}