gt:         storedRootNumber = root.getNumber();
context: /*
   * TreeModel.java
   *
   * Copyright (c) 2002-2017 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.evomodel.tree;
  
  import dr.evolution.tree.*;
  import dr.evolution.util.MutableTaxonListListener;
  import dr.evolution.util.Taxon;
  import dr.inference.model.*;
  import dr.util.*;
  import org.w3c.dom.Document;
  import org.w3c.dom.Element;
  
  import java.util.*;
  
  /**
   * A model component for trees.
   *
   * @author Andrew Rambaut
   * @author Alexei Drummond
   * @version $Id: TreeModel.java,v 1.129 2006/01/05 17:55:47 rambaut Exp $
   */
  public class TreeModel extends AbstractModel implements MutableTreeModel, Keywordable, Citable {
  
      //
      // Public stuff
      //
  
      public static final String TREE_MODEL = "treeModel";
  
      private static final boolean TEST_NODE_BOUNDS = false;
  
      public TreeModel(String name) {
          super(name);
          nodeCount = 0;
          externalNodeCount = 0;
          internalNodeCount = 0;
          isTreeRandom = true;
      }
  
      public TreeModel(Tree tree) {
          this(TREE_MODEL, tree, false, false, false);
      }
  
      public TreeModel(String id, Tree tree) { this(id, tree, false, false); }
  
      public TreeModel(String id, Tree tree, boolean fixHeights, boolean fixTree) {
  
          this(id, tree, false, fixHeights, fixTree);
          setId(id);
      }
  
      /* New constructor that copies the attributes of Tree tree into the new TreeModel
        * Useful for constructing a TreeModel from a NEXUS file entry
        */
  
      public TreeModel(String name, Tree tree, boolean copyAttributes, boolean fixHeights, boolean fixTree) {
  
          super(name);
  
          // get a rooted version of the tree to clone
          FlexibleTree binaryTree = new FlexibleTree(tree, copyAttributes);
          binaryTree.resolveTree();
  
          // adjust the heights to be compatible with the tip dates and perturb
          // any zero branches.
          if (!fixHeights) {
              MutableTree.Utils.correctHeightsForTips(binaryTree);
          }
  
          this.isTreeRandom = !fixTree;
  
          // clone the node structure (this will create the individual parameters)
          Node node = new Node(binaryTree, binaryTree.getRoot());
  
          internalNodeCount = binaryTree.getInternalNodeCount();
          externalNodeCount = binaryTree.getExternalNodeCount();
  
          nodeCount = internalNodeCount + externalNodeCount;
  
          nodes = new Node[nodeCount];
          storedNodes = new Node[nodeCount];
  
          int i = 0;
          int j = externalNodeCount;
  
          root = node;
  
          do {
              node = (Node) TreeUtils.postorderSuccessor(this, node);
  
              if (node.isExternal()) {
                  node.number = i;
  
                  nodes[i] = node;
                  storedNodes[i] = new Node();
                  storedNodes[i].taxon = node.taxon;
                  storedNodes[i].number = i;
  
                  i++;
              } else {
                  node.number = j;
  
                  nodes[j] = node;
                  storedNodes[j] = new Node();
                  storedNodes[j].number = j;
  
                  j++;
              }
          } while (node != root);
  
          // must be done here to allow programmatic running of BEAST
          setupHeightBounds();
      }
  
  
      boolean heightBoundsSetup = false;
  
      public void setupHeightBounds() {
  
          if (heightBoundsSetup) {
              throw new IllegalArgumentException("Node height bounds set up twice");
          }
  
          for (int i = 0; i < nodeCount; i++) {
              nodes[i].setupHeightBounds();
          }
  
          heightBoundsSetup = true;
      }
  
      /**
       * Push a tree changed event into the event stack.
       */
      public void pushTreeChangedEvent() {
          pushTreeChangedEvent(new TreeChangedEvent());
      }
  
      /**
       * Push a tree changed event into the event stack.
       */
      public void pushTreeChangedEvent(NodeRef nodeRef) {
          pushTreeChangedEvent(new TreeChangedEvent((Node) nodeRef));
      }
  
      /**
       * Push a tree changed event into the event stack.
       */
      public void pushTreeChangedEvent(Node node, Parameter parameter, int index) {
          pushTreeChangedEvent(new TreeChangedEvent(node, parameter, index));
      }
  
      /**
       * Push a tree changed event into the event stack.
       */
      public void pushTreeChangedEvent(dr.evomodel.tree.TreeChangedEvent event) {
  
          if (!isTreeRandom) throw new IllegalStateException("Attempting state change in fixed tree");
  
          if (inEdit) {
              treeChangedEvents.add(event);
          } else {
              listenerHelper.fireModelChanged(this, event);
          }
      }
  
  
      protected void handleModelChangedEvent(Model model, Object object, int index) {
          // no submodels so nothing to do
      }
  
      /**
       * Called when a parameter changes.
       */
      public void handleVariableChangedEvent(Variable variable, int index, Parameter.ChangeType type) {
          final Node node = getNodeOfParameter((Parameter) variable);
          if (type == Parameter.ChangeType.ALL_VALUES_CHANGED) {
              //this signals events where values in all dimensions of a parameter is changed.
              pushTreeChangedEvent(new TreeChangedEvent(node, (Parameter) variable, TreeChangedEvent.CHANGE_IN_ALL_INTERNAL_NODES));
          } else {
              pushTreeChangedEvent(node, (Parameter) variable, index);
          }
      }
  
  
      private final List<dr.evomodel.tree.TreeChangedEvent> treeChangedEvents = new ArrayList<dr.evomodel.tree.TreeChangedEvent>();
  
      public boolean hasRates() {
          return hasRates;
      }
  
      public boolean inTreeEdit() {
          return inEdit;
      }
  
      public class TreeChangedEvent implements dr.evomodel.tree.TreeChangedEvent {
          static final int CHANGE_IN_ALL_INTERNAL_NODES = -2;
  
          final Node node;
          final Parameter parameter;
          final int index;
  
          public TreeChangedEvent() {
              this(null, null, -1);
          }
  
          public TreeChangedEvent(Node node) {
              this(node, null, -1);
          }
  
          public TreeChangedEvent(Node node, Parameter parameter, int index) {
              this.node = node;
              this.parameter = parameter;
              this.index = index;
          }
  
          @Override
          public int getIndex() {
              return index;
          }
  
          @Override
          public Node getNode() {
              return node;
          }
  
          public Parameter getParameter() {
              return parameter;
          }
  
          public boolean isTreeChanged() {
              return parameter == null;
          }
  
          @Override
          public boolean isNodeChanged() {
              return node != null;
          }
  
          @Override
          public boolean isNodeParameterChanged() {
              return parameter != null;
          }
  
          public boolean isHeightChanged() {
              return parameter == node.heightParameter;
          }
  
          public boolean isRateChanged() {
              return parameter == node.rateParameter;
          }
  
          public boolean isTraitChanged(String name) {
              return parameter == node.traitParameters.get(name);
          }
  
          public boolean areAllInternalHeightsChanged() {
              if (parameter != null) {
                  return parameter == node.heightParameter && index == CHANGE_IN_ALL_INTERNAL_NODES;
              }
              return false;
          }
  
      }
  
      // *****************************************************************
      // Interface Tree
      // *****************************************************************
  
      /**
       * Return the units that this tree is expressed in.
       */
      public Type getUnits() {
          return units;
      }
  
      /**
       * Sets the units that this tree is expressed in.
       */
      public void setUnits(Type units) {
          this.units = units;
      }
  
      /**
       * @return a count of the number of nodes (internal + external) in this
       *         tree.
       */
      public int getNodeCount() {
          return nodeCount;
      }
  
      public boolean hasNodeHeights() {
          return true;
      }
  
      public double getNodeHeight(NodeRef node) {
          return ((Node) node).getHeight();
      }
  
      public final double getNodeHeightUpper(NodeRef node) {
          return ((Node) node).heightParameter.getBounds().getUpperLimit(0);
      }
  
      public final double getNodeHeightLower(NodeRef node) {
          return ((Node) node).heightParameter.getBounds().getLowerLimit(0);
      }
  
  
      /**
       * @param node
       * @return the rate parameter associated with this node.
       */
      public double getNodeRate(NodeRef node) {
          if (!hasRates) {
              return 1.0;
          }
          return ((Node) node).getRate();
      }
  
      public Object getNodeAttribute(NodeRef node, String name) {
  
          if (name.equals("rate")) {
              return getNodeRate(node);
          }
  
          return null;
      }
  
      public Iterator getNodeAttributeNames(NodeRef node) {
          return new Iterator() {
  
              int i = 0;
              String[] attributes = {"rate"};
  
              public boolean hasNext() {
                  return i < attributes.length;
              }
  
              public Object next() {
                  return attributes[i++];
              }
  
              public void remove() {
                  throw new UnsupportedOperationException("can't remove from this iterator!");
              }
          };
      }
  
      public boolean hasNodeTraits() {
          return hasTraits;
      }
  
      public Map<String, Parameter> getTraitMap(NodeRef node) {
          if (!hasTraits) throw new IllegalArgumentException("Trait parameters have not been created");
          return ((Node) node).getTraitMap();
      }
  
      public double getNodeTrait(NodeRef node, String name) {
          if (!hasTraits) throw new IllegalArgumentException("Trait parameters have not been created");
          return ((Node) node).getTrait(name);
      }
  
      public Parameter getNodeTraitParameter(NodeRef node, String name) {
          if (!hasTraits) throw new IllegalArgumentException("Trait parameters have not been created");
          return ((Node) node).getTraitParameter(name);
      }
  
      public double[] getMultivariateNodeTrait(NodeRef node, String name) {
          if (!hasTraits) throw new IllegalArgumentException("Trait parameters have not been created");
          return ((Node) node).getMultivariateTrait(name);
      }
  
      public final void swapAllTraits(NodeRef node1, NodeRef node2) {
          if (!hasTraits) throw new IllegalArgumentException("Trait parameters have not been created");
          swapAllTraits((Node) node1, (Node) node2);
      }
  
      public Taxon getNodeTaxon(NodeRef node) {
          return ((Node) node).taxon;
      }
  
      public void setNodeTaxon(NodeRef node, Taxon taxon) {
          ((Node) node).taxon = taxon;
      }
  
      public boolean isExternal(NodeRef node) {
          return ((Node) node).isExternal();
      }
  
      public boolean isRoot(NodeRef node) {
          return (node == root);
      }
  
      public int getChildCount(NodeRef node) {
          return ((Node) node).getChildCount();
      }
  
      public NodeRef getChild(NodeRef node, int i) {
          return ((Node) node).getChild(i);
      }
  
      public NodeRef getParent(NodeRef node) {
          return ((Node) node).parent;
      }
  
      public boolean hasBranchLengths() {
          return true;
      }
  
      public double getBranchLength(NodeRef node) {
          NodeRef parent = getParent(node);
          if (parent == null) {
              return 0.0;
          }
  
          return getNodeHeight(parent) - getNodeHeight(node);
      }
  
      public NodeRef getExternalNode(int i) {
          return nodes[i];
      }
  
      public NodeRef getInternalNode(int i) {
          return nodes[i + externalNodeCount];
      }
  
      public NodeRef getNode(int i) {
          return nodes[i];
      }
  
      public NodeRef[] getNodes() {
          return nodes;
      }
  
      /**
       * Returns the number of external nodes.
       */
      public int getExternalNodeCount() {
          return externalNodeCount;
      }
  
      /**
       * Returns the ith internal node.
       */
      public int getInternalNodeCount() {
          return internalNodeCount;
      }
  
      /**
       * Returns the root node of this tree.
       */
      public NodeRef getRoot() {
          return root;
      }
  
      // *****************************************************************
      // Interface MutableTree
      // *****************************************************************
  
      /**
       * Set a new node as root node.
       */
      public void setRoot(NodeRef newRoot) {
  
          if (!inEdit) throw new RuntimeException("Must be in edit transaction to call this method!");
  
          root = (Node) newRoot;
  
          // We shouldn't need this because the addChild will already have fired appropriate events.
          pushTreeChangedEvent(root);
      }
  
      public void addChild(NodeRef p, NodeRef c) {
  
          if (!inEdit) throw new RuntimeException("Must be in edit transaction to call this method!");
  
          Node parent = (Node) p;
          Node child = (Node) c;
          if (parent.hasChild(child)) throw new IllegalArgumentException("Child already exists in parent");
  
          parent.addChild(child);
          pushTreeChangedEvent(parent);
      }
  
      public void removeChild(NodeRef p, NodeRef c) {
  
          if (!inEdit) throw new RuntimeException("Must be in edit transaction to call this method!");
  
          Node parent = (Node) p;
          Node child = (Node) c;
  
          parent.removeChild(child);
      }
  
      public void replaceChild(NodeRef node, NodeRef child, NodeRef newChild) {
          throw new RuntimeException("Unimplemented");
      }
  
      private Node oldRoot;
  
      public boolean beginTreeEdit() {
          if (inEdit) throw new RuntimeException("Already in edit transaction mode!");
  
          oldRoot = root;
  
          inEdit = true;
  
          return false;
      }
  
      public void endTreeEdit() {
          if (!inEdit) throw new RuntimeException("Not in edit transaction mode!");
  
          inEdit = false;
  
          if (root != oldRoot) {
              swapParameterObjects(oldRoot, root);
          }
  
          if (TEST_NODE_BOUNDS) {
              try {
                  checkTreeIsValid();
              } catch (InvalidTreeException ite) {
                  throw new RuntimeException(ite.getMessage());
              }
          }
  
          for (dr.evomodel.tree.TreeChangedEvent treeChangedEvent : treeChangedEvents) {
              listenerHelper.fireModelChanged(this, treeChangedEvent);
          }
          treeChangedEvents.clear();
      }
  
      public void checkTreeIsValid() throws MutableTree.InvalidTreeException {
          for (Node node : nodes) {
              if (!node.heightParameter.isWithinBounds()) {
                  throw new InvalidTreeException("height parameter out of bounds");
              }
          }
      }
  
      public void setNodeHeight(NodeRef n, double height) {
          ((Node) n).setHeight(height);
      }
  
      public void setNodeHeightQuietly(NodeRef n, double height) {
          ((Node) n).setHeightQuietly(height);
      }
  
      public void setNodeRate(NodeRef n, double rate) {
          if (!hasRates) throw new IllegalArgumentException("Rate parameters have not been created");
          ((Node) n).setRate(rate);
  
      }
  
      public void setNodeTrait(NodeRef n, String name, double value) {
          if (!hasTraits) throw new IllegalArgumentException("Trait parameters have not been created");
          ((Node) n).setTrait(name, value);
      }
  
      public void setMultivariateTrait(NodeRef n, String name, double[] value) {
          if (!hasTraits) throw new IllegalArgumentException("Trait parameters have not been created");
          ((Node) n).setMultivariateTrait(name, value);
      }
  
      public void setBranchLength(NodeRef node, double length) {
          throw new UnsupportedOperationException("TreeModel cannot have branch lengths set");
      }
  
      public void setNodeAttribute(NodeRef node, String name, Object value) {
          throw new UnsupportedOperationException("TreeModel does not use NodeAttributes");
      }
  
      // *****************************************************************
      // Interface ModelComponent
      // *****************************************************************
  
      /**
       * Store current state
       */
      protected void storeState() {
  
          copyNodeStructure(storedNodes);
  