private boolean eligibleForMove(NodeRef node, TreeModel tree, BranchMapModel branchMap) {
    // to be eligible for this move, the node's parent and grandparent, or parent and other child,
    // must be in the same partition (so removing the parent has no effect on the remaining links of
    // the TT),
    // and the node and its parent must be in different partitions (such that the move does not
    // disconnect anything)

    return ((tree.getParent(tree.getParent(node)) != null
                && branchMap.get(tree.getParent(node).getNumber())
                    == branchMap.get(tree.getParent(tree.getParent(node)).getNumber()))
            || branchMap.get(tree.getParent(node).getNumber())
                == branchMap.get(getOtherChild(tree, tree.getParent(node), node).getNumber()))
        && branchMap.get(tree.getParent(node).getNumber()) != branchMap.get(node.getNumber());
  }
  public void proposeTree() throws OperatorFailedException {
    TreeModel tree = c2cLikelihood.getTreeModel();
    BranchMapModel branchMap = c2cLikelihood.getBranchMap();
    NodeRef i;
    double oldMinAge, newMinAge, newRange, oldRange, newAge, q;
    // choose a random node avoiding root, and nodes that are ineligible for this move because they
    // have nowhere to
    // go
    final int nodeCount = tree.getNodeCount();
    do {
      i = tree.getNode(MathUtils.nextInt(nodeCount));
    } while (tree.getRoot() == i || !eligibleForMove(i, tree, branchMap));
    final NodeRef iP = tree.getParent(i);

    // this one can go anywhere

    NodeRef j = tree.getNode(MathUtils.nextInt(tree.getNodeCount()));
    NodeRef k = tree.getParent(j);

    while ((k != null && tree.getNodeHeight(k) <= tree.getNodeHeight(i)) || (i == j)) {
      j = tree.getNode(MathUtils.nextInt(tree.getNodeCount()));
      k = tree.getParent(j);
    }

    if (iP == tree.getRoot() || j == tree.getRoot()) {
      throw new OperatorFailedException("Root changes not allowed!");
    }

    if (k == iP || j == iP || k == i) throw new OperatorFailedException("move failed");

    final NodeRef CiP = getOtherChild(tree, iP, i);
    NodeRef PiP = tree.getParent(iP);

    newMinAge = Math.max(tree.getNodeHeight(i), tree.getNodeHeight(j));
    newRange = tree.getNodeHeight(k) - newMinAge;
    newAge = newMinAge + (MathUtils.nextDouble() * newRange);
    oldMinAge = Math.max(tree.getNodeHeight(i), tree.getNodeHeight(CiP));
    oldRange = tree.getNodeHeight(PiP) - oldMinAge;
    q = newRange / Math.abs(oldRange);

    // need to account for the random repainting of iP

    if (branchMap.get(PiP.getNumber()) != branchMap.get(CiP.getNumber())) {
      q *= 0.5;
    }

    if (branchMap.get(k.getNumber()) != branchMap.get(j.getNumber())) {
      q *= 2;
    }

    tree.beginTreeEdit();

    if (j == tree.getRoot()) {

      // 1. remove edges <iP, CiP>
      tree.removeChild(iP, CiP);
      tree.removeChild(PiP, iP);

      // 2. add edges <k, iP>, <iP, j>, <PiP, CiP>
      tree.addChild(iP, j);
      tree.addChild(PiP, CiP);

      // iP is the new root
      tree.setRoot(iP);

    } else if (iP == tree.getRoot()) {

      // 1. remove edges <k, j>, <iP, CiP>, <PiP, iP>
      tree.removeChild(k, j);
      tree.removeChild(iP, CiP);

      // 2. add edges <k, iP>, <iP, j>, <PiP, CiP>
      tree.addChild(iP, j);
      tree.addChild(k, iP);

      // CiP is the new root
      tree.setRoot(CiP);

    } else {
      // 1. remove edges <k, j>, <iP, CiP>, <PiP, iP>
      tree.removeChild(k, j);
      tree.removeChild(iP, CiP);
      tree.removeChild(PiP, iP);

      // 2. add edges <k, iP>, <iP, j>, <PiP, CiP>
      tree.addChild(iP, j);
      tree.addChild(k, iP);
      tree.addChild(PiP, CiP);
    }

    tree.setNodeHeight(iP, newAge);

    tree.endTreeEdit();

    //
    logq = Math.log(q);

    // repaint the parent to match either its new parent or its new child (50% chance of each).

    if (MathUtils.nextInt(2) == 0) {
      branchMap.set(iP.getNumber(), branchMap.get(k.getNumber()), true);
    } else {
      branchMap.set(iP.getNumber(), branchMap.get(j.getNumber()), true);
    }

    if (DEBUG) {
      c2cLikelihood.checkPartitions();
    }
  }