public SubstitutionModelDelegate(Tree tree, BranchModel branchModel, int bufferPoolSize) {

    if (MEASURE_RUN_TIME) {
      updateTime = 0;
      convolveTime = 0;
    }

    this.tree = tree;

    this.substitutionModelList = branchModel.getSubstitutionModels();

    this.branchModel = branchModel;

    eigenCount = substitutionModelList.size();
    nodeCount = tree.getNodeCount();

    // two eigen buffers for each decomposition for store and restore.
    eigenBufferHelper = new BufferIndexHelper(eigenCount, 0);

    // two matrices for each node less the root
    matrixBufferHelper = new BufferIndexHelper(nodeCount, 0);

    this.extraBufferCount =
        branchModel.requiresMatrixConvolution()
            ? (bufferPoolSize > 0 ? bufferPoolSize : BUFFER_POOL_SIZE_DEFAULT)
            : 0;

    if (branchModel.requiresMatrixConvolution() && this.extraBufferCount < eigenCount) {
      throw new RuntimeException(
          "SubstitutionModelDelegate requires at least "
              + eigenCount
              + " extra buffers to convolve matrices");
    }

    for (int i = 0; i < extraBufferCount; i++) {
      pushAvailableBuffer(i + matrixBufferHelper.getBufferCount());
    }

    // one extra created as a reserve
    // which is used to free up buffers when the avail stack is empty.
    reserveBufferIndex = matrixBufferHelper.getBufferCount() + extraBufferCount;

    if (DEBUG) {
      System.out.println("Creating reserve buffer with index: " + reserveBufferIndex);
    }
  } // END: Constructor
 public double[] getRootStateFrequencies() {
   return branchModel.getRootFrequencyModel().getFrequencies();
 } // END: getStateFrequencies
  public void updateTransitionMatrices(
      Beagle beagle, int[] branchIndices, double[] edgeLength, int updateCount) {

    int[][] probabilityIndices = new int[eigenCount][updateCount];
    double[][] edgeLengths = new double[eigenCount][updateCount];

    int[] counts = new int[eigenCount];

    List<Deque<Integer>> convolutionList = new ArrayList<Deque<Integer>>();

    for (int i = 0; i < updateCount; i++) {

      BranchModel.Mapping mapping =
          branchModel.getBranchModelMapping(tree.getNode(branchIndices[i]));
      int[] order = mapping.getOrder();
      double[] weights = mapping.getWeights();

      if (order.length == 1) {
        int k = order[0];
        probabilityIndices[k][counts[k]] = matrixBufferHelper.getOffsetIndex(branchIndices[i]);
        edgeLengths[k][counts[k]] = edgeLength[i];
        counts[k]++;
      } else {
        double sum = 0.0;
        for (double w : weights) {
          sum += w;
        }

        if (getAvailableBufferCount() < order.length) {
          // too few buffers available, process what we have and continue...
          computeTransitionMatrices(beagle, probabilityIndices, edgeLengths, counts);
          convolveMatrices(beagle, convolutionList);

          // reset the counts
          for (int k = 0; k < eigenCount; k++) {
            counts[k] = 0;
          }
        }

        Deque<Integer> bufferIndices = new ArrayDeque<Integer>();
        for (int j = 0; j < order.length; j++) {

          int buffer = popAvailableBuffer();

          if (buffer < 0) {
            // no buffers available
            throw new RuntimeException(
                "Ran out of buffers for transition matrices - computing current list.");
          }

          int k = order[j];
          probabilityIndices[k][counts[k]] = buffer;
          edgeLengths[k][counts[k]] = weights[j] * edgeLength[i] / sum;
          //                    edgeLengths[k][counts[k]] = weights[j] ;
          counts[k]++;

          bufferIndices.add(buffer);
        }
        bufferIndices.add(matrixBufferHelper.getOffsetIndex(branchIndices[i]));

        convolutionList.add(bufferIndices);
      } // END: if convolution needed
    } // END: i loop

    computeTransitionMatrices(beagle, probabilityIndices, edgeLengths, counts);
    convolveMatrices(beagle, convolutionList);
  } // END: updateTransitionMatrices