docs: private void passMessage(CliqueTree cliqueTree, int startFactor, int destFactor, boolean useSumProduct) {
      VariableNumMap sharedVars = cliqueTree.getFactor(startFactor).getVars().intersection(cliqueTree.getFactor(destFactor).getVars());
  
      // Find the factors which have yet to be merged into the marginal
      // distribution of factor, but are necessary for computing the 
      // specified message.
      Set<Integer> factorIndicesToCombine = Sets.newHashSet(cliqueTree.getNeighboringFactors(startFactor));
      factorIndicesToCombine.removeAll(cliqueTree.getFactorsInMarginal(startFactor));
  
      // If this is the upstream round of message passing, we might not have
      // received a message from destFactor yet. However, if we have received the 
      // message, we should include it in the product as it will increase sparsity 
      // and thereby improve efficiency.
      if (cliqueTree.getMessage(destFactor, startFactor) == null) {
        factorIndicesToCombine.remove(destFactor);
      }
  
      List<Factor> factorsToCombine = new ArrayList<Factor>();
      for (Integer adjacentFactorNum : factorIndicesToCombine) {
        factorsToCombine.add(cliqueTree.getMessage(adjacentFactorNum, startFactor));
      }
  
      // Update the marginal distribution of startFactor in the clique tree.
      Factor updatedMarginal = cliqueTree.getMarginal(startFactor).product(factorsToCombine);
      if (cliqueTree.getOutboundFactors(startFactor).size() == 0) {
        // If this factor has yet to send any outbound messages, we can 
        // use it to renormalize the probability distribution to avoid
        // possible numerical overflow issues. Updating the marginal
        // at this point is equivalent to multiplying the original factor
        // by some constant value, which doesn't affect the probability
        // distribution.
        if (renormalize) {
          updatedMarginal = updatedMarginal.product(1.0 / updatedMarginal.getTotalUnnormalizedProbability());
        }
        
        // Can also prune this marginal if a pruning strategy was provided.
        if (pruningStrategy != null) {
          updatedMarginal = pruningStrategy.apply(updatedMarginal);
        }
      }
      cliqueTree.setMarginal(startFactor, updatedMarginal);
      cliqueTree.addFactorsToMarginal(startFactor, factorIndicesToCombine);
  
      // The message from startFactor to destFactor is the marginal of
      // productFactor, divided by the message from destFactor to
      // startFactor, if it exists.
      Factor messageFactor = null;
      if (useSumProduct) {
        messageFactor = updatedMarginal.marginalize(updatedMarginal.getVars().removeAll(sharedVars).getVariableNums());
      } else {
        messageFactor = updatedMarginal.maxMarginalize(updatedMarginal.getVars().removeAll(sharedVars).getVariableNums());
      }
  
      // Divide out the destFactor -> startFactor message if necessary.
      if (cliqueTree.getFactorsInMarginal(startFactor).contains(destFactor)) {
        messageFactor = messageFactor.product(cliqueTree.getMessage(destFactor, startFactor).inverse());
      }
  
      cliqueTree.addMessage(startFactor, destFactor, messageFactor);
    }