/* * DefaultBranchColouring.java * * Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard * * This file is part of BEAST. * See the NOTICE file distributed with this work for additional * information regarding copyright ownership and licensing. * * BEAST is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * BEAST is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with BEAST; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA */ package dr.evolution.colouring; import dr.evolution.tree.ColourChange; import java.util.ArrayList; import java.util.List; /** * BranchColouring.java * * Represents a colouring on a single branch, both backward and forward in time. * * @author Gerton Lunter * @author Andrew Rambaut * * @version $Id: BranchColouring.java,v 1.6 2006/08/12 12:55:44 gerton Exp $ */ public class DefaultBranchColouring implements BranchColouring { public final static boolean checkSanity = true; public DefaultBranchColouring() { this.parentColour = -1; } public DefaultBranchColouring( int parentColour, int childColour ) { this.parentColour = parentColour; this.childColour = childColour; } public DefaultBranchColouring( DefaultBranchColouring history ) { parentColour = history.parentColour; childColour = history.childColour; colourChanges.addAll( history.colourChanges ); changeHeights.addAll( history.changeHeights ); finalSanityCheck(); } public DefaultBranchColouring getCopy() { return new DefaultBranchColouring( this ); } public void clear() { colourChanges.clear(); changeHeights.clear(); } public void reset( int parentColour, int childColour ) { this.parentColour = parentColour; this.childColour = childColour; colourChanges.clear(); changeHeights.clear(); } public int getParentColour() { return parentColour; } public int getChildColour() { return childColour; } /** * Add a colour change event, going in the parent->child direction. * * @param colour * @param time */ public void addEvent( int colour, double time ) { colourChanges.add(colour); changeHeights.add(time); sanityCheck(); } /** * Add a number of change events below the current child */ public void addHistory( DefaultBranchColouring history ) { // require that my child colour is the new history's parent colour if (childColour != history.parentColour) { throw new Error("My child colour and the added parent colour don't match"); } colourChanges.addAll( history.colourChanges ); changeHeights.addAll( history.changeHeights ); childColour = history.childColour; fullSanityCheck(); } /** * * @return number of events */ public int getNumEvents() { return colourChanges.size(); } /** * * @param i event, 0..getNumEvents inclusive. * @return Colour below event. If i==0, returns parent colour */ public int getForwardColourBelow( int i ) { finalSanityCheck(); if (i==0) return parentColour; return colourChanges.get(i - 1); } /** * * @param i event, 1..getNumEvents inclusive * @return time of event */ public double getForwardTime( int i ) { finalSanityCheck(); return changeHeights.get(i - 1); } /** * * @param i event, 0..getNumEvents inclusive * @return Colour above event. If i==0, returns child colour */ public int getBackwardColourAbove( int i ) { finalSanityCheck(); int total = colourChanges.size(); if (i == total) return parentColour; return colourChanges.get(total - 1 - i); } /** * * @param i event, 1..getNumEvents inclusive * @return time of event */ public double getBackwardTime( int i ) { finalSanityCheck(); int total = colourChanges.size(); return changeHeights.get(total - i); } /** * Returns event index corresponding to first event after time, going forward in time * @param height * @return 1 if first event is after time; getNumEvents+1 if time is after last event; 1 if no events exist */ public int getNextForwardEvent( double height ) { int i; for (i=0; i < changeHeights.size() && changeHeights.get(i) > height; i += 1 ) {} return i+1; } public double getTimeInColour(int colour, double parentHeight, double childHeight) { double totalTime = 0.0; int currentColour = getForwardColourBelow(0); // parent colour double previousTime = parentHeight; for (int i = 1; i <= getNumEvents(); i++) { double currentTime = getForwardTime( i ); if (currentColour == colour) { totalTime += previousTime - currentTime; } currentColour = getForwardColourBelow( i ); previousTime = currentTime; } if (currentColour == colour) { totalTime += previousTime - childHeight; } return totalTime; } /** * for backward compatibility (evolution.tree.ColouredTreePainter) * * @return list of colour changes */ public List<ColourChange> getColourChanges() { List<ColourChange> cc = new ArrayList<ColourChange>(0); for (int i=1; i<=getNumEvents(); i++) { cc.add( new ColourChange( getBackwardTime(i), getBackwardColourAbove(i) ) ); } return cc; } private void sanityCheck() { if (checkSanity) { int total = colourChanges.size(); if (total > 0) { if ( colourChanges.get(0) == parentColour) { throw new Error("First event does not change colour"); } } if (total > 1) { if ((colourChanges.get(total-1)).intValue() == (colourChanges.get(total-2)).intValue()) { throw new Error("Last event does not change colour"); } if ( changeHeights.get(total - 1) > changeHeights.get(total - 2) ) { throw new Error("Child event occurs before parent event"); } } } } private void fullSanityCheck() { if (checkSanity) { sanityCheck(); int total = colourChanges.size(); for (int i=1; i < total; i++) { if ((colourChanges.get(i)).intValue() == (colourChanges.get(i-1)).intValue()) { throw new Error("Event "+i+" does not change colour"); } if ( changeHeights.get(i) > changeHeights.get(i - 1) ) { throw new Error("Event "+i+" jumps back in time"); } } } } private void finalSanityCheck() { if (checkSanity) { if (parentColour == -1) { throw new Error("Parent colour has not been set"); } int total = colourChanges.size(); if (total > 0) { if ( colourChanges.get(total - 1) != childColour) { throw new Error("Last event does not change colour into child's"); } } } } private final List<Integer> colourChanges = new ArrayList<Integer>(0); // new colours, forward in time private final List<Double> changeHeights = new ArrayList<Double>(0); // change heights, forward in time private int parentColour; private int childColour; }