private void convolveAndRelease(
      Beagle beagle,
      int[] firstConvolutionBuffers,
      int[] secondConvolutionBuffers,
      int[] resultConvolutionBuffers,
      int operationsCount) {

    if (RUN_IN_SERIES) {
      if (operationsCount > 1) {
        throw new RuntimeException("Unable to convolve matrices in series");
      }
    }

    beagle.convolveTransitionMatrices(
        firstConvolutionBuffers, // A
        secondConvolutionBuffers, // B
        resultConvolutionBuffers, // C
        operationsCount // count
        );

    for (int i = 0; i < operationsCount; i++) {
      if (firstConvolutionBuffers[i] >= matrixBufferHelper.getBufferCount()
          && firstConvolutionBuffers[i] != reserveBufferIndex) {
        pushAvailableBuffer(firstConvolutionBuffers[i]);
      }
      if (secondConvolutionBuffers[i] >= matrixBufferHelper.getBufferCount()
          && secondConvolutionBuffers[i] != reserveBufferIndex) {
        pushAvailableBuffer(secondConvolutionBuffers[i]);
      }
    }
  } // END: convolveAndRelease
  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 int getMatrixBufferCount() {
   // plus one for the reserve buffer
   return matrixBufferHelper.getBufferCount() + extraBufferCount + 1;
 }
 public int getEigenBufferCount() {
   return eigenBufferHelper.getBufferCount();
 }