public String asText(int indentlen) {
   StringBuilder s = new StringBuilder();
   Formatter formatter = new Formatter(s, Locale.US);
   if (lft < 0) {
     formatter.format("%s ", taxon.getId());
   } else {
     formatter.format("%s ", "+");
   }
   while (s.length() < 20 - indentlen) {
     formatter.format("%s", " ");
   }
   formatter.format("%s ", AlloppMisc.nonnegIn8Chars(height));
   formatter.format("%20s ", AlloppMisc.FixedBitSetasText(union));
   formatter.format("%3d  ", nlineages);
   for (int c = 0; c < coalheights.size(); c++) {
     formatter.format(AlloppMisc.nonnegIn8Chars(coalheights.get(c)) + ",");
   }
   return s.toString();
 }
  public PopsIOSpeciesTreeModel(
      PopsIOSpeciesBindings piosb, Parameter popPriorScale, PriorComponent[] priorComponents) {
    super(PopsIOSpeciesTreeModelParser.PIO_SPECIES_TREE);
    this.piosb = piosb;

    this.popPriorScale = popPriorScale;
    addVariable(popPriorScale);
    popPriorScale.addBounds(new Parameter.DefaultBounds(Double.POSITIVE_INFINITY, 0.0, 1));

    this.priorComponents = priorComponents;

    PopsIOSpeciesBindings.SpInfo[] species = piosb.getSpecies();
    int nTaxa = species.length;
    int nNodes = 2 * nTaxa - 1;
    pionodes = new PopsIONode[nNodes];
    for (int n = 0; n < nNodes; n++) {
      pionodes[n] = new PopsIONode(n);
    }
    ArrayList<Integer> tojoin = new ArrayList<Integer>(nTaxa);
    for (int n = 0; n < nTaxa; n++) {
      pionodes[n].setTaxon(species[n].name);
      pionodes[n].setHeight(0.0);
      pionodes[n].setUnion(piosb.tipUnionFromTaxon(pionodes[n].getTaxon()));
      tojoin.add(n);
    }
    double rate = 1.0;
    double treeheight = 0.0;
    for (int i = 0; i < nTaxa - 1; i++) {
      int numtojoin = tojoin.size();
      int j = MathUtils.nextInt(numtojoin);
      Integer child0 = tojoin.get(j);
      tojoin.remove(j);
      int k = MathUtils.nextInt(numtojoin - 1);
      Integer child1 = tojoin.get(k);
      tojoin.remove(k);
      pionodes[nTaxa + i].addChildren(pionodes[child0], pionodes[child1]);
      pionodes[nTaxa + i].setHeight(treeheight + randomnodeheight(numtojoin * rate));
      treeheight = pionodes[nTaxa + i].getHeight();
      tojoin.add(nTaxa + i);
    }
    rootn = pionodes.length - 1;

    double scale = 0.99 * piosb.initialMinGeneNodeHeight() / pionodes[rootn].height;
    scaleAllHeights(scale);
    pionodes[rootn].fillinUnionsInSubtree(piosb.getSpecies().length);

    stree = makeSimpleTree();

    Logger.getLogger("dr.evomodel.speciation.popsio")
        .info(
            "\tConstructing a PopsIO Species Tree Model, please cite:\n"
                + Citable.Utils.getCitationString(this));
  }
 private void copyNonTopologyFields(PopsIONode node) {
   height = node.height;
   taxon = new Taxon(node.taxon.getId());
   nlineages = node.nlineages;
   if (node.union == null) {
     union = null;
   } else {
     union = new FixedBitSet(node.union);
   }
   coalheights = new ArrayList<Double>();
   for (int i = 0; i < node.coalheights.size(); i++) {
     coalheights.add(node.coalheights.get(i));
   }
 }