package statalign.postprocess.plugins.contree;
import java.util.ArrayList;
import java.util.BitSet;
/**
* Splits in the consensus network
* Includes several functions to check degrees of compatibility with another split
*
* @author wood
*
*/
public class CNetworkSplit {
// Edges that represent this split
public ArrayList<CNetworkEdge> edges;
// The split in binary format
public BitSet split;
// The length of this split
public double edgelength;
// The weight based on the proportion of times that this edge occurs in the tree samples.
public int noOfOccurences;
// Direction of split in angle, is negative if unset.
public double direction = -1.0;
/**
* Constructs a new split.
*/
public CNetworkSplit(){
edges = new ArrayList<CNetworkEdge>();
}
/**
* Compares an input split to see if it is compatible with this one. i.e. is one of the sides of one split entirely within one side of the other.
*
* @param comp The split to compare this one with
* @param noOfTaxa The number of taxa as the count function on size of a BitSet is strange
*/
public boolean isCompatible(CNetworkSplit comp, int noOfTaxa){
BitSet split1 = this.split;
BitSet split2 = comp.split;
// See if any side can be considered a subset of another. If so, then is compatible.
// Do this by seeing if a 1 in split1 is always either a 1 or zero in split2 OR
// a 0 in split1 is always either a 1 or zero in split2
boolean[] combinations = new boolean[]{false,false,false,false};
for(int i=0; i<noOfTaxa;i++){
combinations[((split1.get(i)==true)?1:0)*2+((split2.get(i)==true)?1:0)] = true;
}
if (combinations[0] == false || combinations[1] == false || combinations[2] == false || combinations[3] == false){
return true;
}
else{
return false;
}
}
/**
* Compares an input split to see if the input split occurs on the zero side of this split.
*
* @param comp The split to try and find in the zero side of this split
* @param noOfTaxa The number of taxa as the count function on size of a BitSet is strange
*/
public boolean OnZeroSide(CNetworkSplit comp, int noOfTaxa){
BitSet split1 = this.split;
BitSet split2 = comp.split;
// We only want to return TRUE if comparison split is a subset of the zero side of the split calling the function (This split)#
// We can test for this by seeing if all the "1"s in this split match with a single value (of either "0" or "1" in the comparison
boolean[] found = new boolean[]{false,false};
for(int i=0; i<noOfTaxa;i++){
if(split1.get(i)==true){
found[(split2.get(i)==true)?1:0] = true;
}
}
if(found[0] == true && found[1] == true) return false;
else return true;
}
/**
* Compares an input split to see if the argument split is a subset of the specified side of this split?
* NOTE: assumes that splits are COMPATIBLE to begin with i.e. all combinations CANNOT be true!!
*
* @param comp The split we want to find as a subset
* @param zeroSide If we want the split on the zeroside of this split then set as true, if on the other side then as false...
* @param noOfTaxa The number of taxa as the count function on size of a BitSet is strange
*/
public boolean isSubset(CNetworkSplit comp, boolean zeroSide, int noOfTaxa){
BitSet split1 = this.split;
BitSet split2 = comp.split;
// shamelessly copy code from the isCompatible function...
boolean[] combinations = new boolean[]{false,false,false,false};
for(int i=0; i<noOfTaxa;i++){
combinations[((split1.get(i)==true)?1:0)*2+((split2.get(i)==true)?1:0)] = true;
}
// for the case that we have all the splits on one side
if(zeroSide == true && combinations[0] == true && combinations[1] == true){
return true;
}
else if (zeroSide == false && combinations[2] == true && combinations[3] == true){
return true;
}
else return false;
}
/**
* Takes an input edge and finds the side of this split it occurs on and then returns the number of taxa on that side of this split.
* NOTE: assumes that splits are COMPATIBLE to begin with i.e. all combinations CANNOT be true!
*
* @param currentEdge An edge from this node to compare
* @param noOfTaxa The number of taxa as the count function on size of a BitSet is strange
*/
// Assumed that splits input are compatible...
public double getSubsetJoins(CNetworkEdge currentEdge,int noOfTaxa){
CNetworkSplit splitToCompare = currentEdge.split;
// if is a subset on ZERO side, then return number of zeros...
if(isSubset(splitToCompare,true,noOfTaxa)==true) {
return (double)(noOfTaxa-split.cardinality());
}
// if on 1 side, return the number of 1s....
else if (isSubset(splitToCompare,false,noOfTaxa)==true){
return (double)split.cardinality();
}
// shouldn't be returned... but will be if, for example, a node in the middle of a line is output.
else{
System.out.println("Potential problem in drawing network: No subset found for splits "+splitToCompare.split.toString()+" and "+split.toString());
return currentEdge.weighting;
}
}
}