private void chooseSeeds(TaskMonitor monitor) throws CancelledException {
		monitor.setMessage("Generating seeds...");
		ArrayList<FunctionPair> finalPairs = new ArrayList<FunctionPair>();
		HashSet<FunctionNode> matchedSource = new HashSet<FunctionNode>();	// Source functions that are matched
		HashSet<FunctionNode> matchedDest = new HashSet<FunctionNode>();	// Dest functions that are matched
		MultiValuedMap<FunctionNode, FunctionPair> sourceHoldOn =
			new HashSetValuedHashMap<FunctionNode, FunctionPair>();			// Conflicting source functions held for next round
		MultiValuedMap<FunctionNode, FunctionPair> destHoldOn =
			new HashSetValuedHashMap<FunctionNode, FunctionPair>();			// Conflicting dest functions held for next round
		MultiValuedMap<FunctionNode, FunctionPair> sourceFormatted =
			new HashSetValuedHashMap<FunctionNode, FunctionPair>();			// Current set of potential pairs, indexed by source
		MultiValuedMap<FunctionNode, FunctionPair> destFormatted =
			new HashSetValuedHashMap<FunctionNode, FunctionPair>();			// Current set of potential pairs, indexed by dest

		for (FunctionPair pair : discoveredMatches) {					// Copy putative matches into the "current" set of potential pairs
			sourceFormatted.put(pair.getSourceNode(), pair);
			destFormatted.put(pair.getDestNode(), pair);
		}
		discoveredMatches = null;			// The raw match list is no longer needed beyond this point

		int keepLen = sourceFormatted.size();
		if (keepLen == 0) {
			return;
		}

		boolean changed = true;
		double ratioThresh = .5;	// Initial threshold for throwing out pairs.  Counts can differ by a factor of 2 to 1.
		while (changed) {			// Keep going until no change (no new pairs)
			monitor.checkCancelled();
			final Collection<FunctionPair> values = sourceFormatted.values();
			monitor.initialize(values.size());
			for (FunctionPair entry : values) {
				monitor.checkCancelled();
				monitor.incrementProgress(1);
				if (!hasConflicts(entry, sourceFormatted, destFormatted)) {		// Check for conflicts in our current set
					finalPairs.add(entry);										// Accept immediately if no conflicts
					matchedSource.add(entry.getSourceNode());
					matchedDest.add(entry.getDestNode());
				}
				else {
					if (!matchedSource.contains(entry.getSourceNode()) &&
						!matchedDest.contains(entry.getDestNode())) {
						// If there is a conflict, but neither side has been matched yet,
						// decide if we throw out pair by comparing count ratios to ratioThresh

						// Compute "number of children" ratio
						double leftside =
							Math.min((double) entry.getSourceNode().getChildren().size(),
								(double) entry.getDestNode().getChildren().size());
						double rightside =
							Math.max((double) entry.getSourceNode().getChildren().size(),
								(double) entry.getDestNode().getChildren().size());
						double childRatio = (rightside == 0 ? 0 : leftside / rightside);		// Always <= 1.0

						// Compute byte length ratio
						leftside = (double) entry.getSourceNode().getLen() /
							(double) entry.getDestNode().getLen();
						double lenRatio = Math.min(leftside, 1 / leftside);						// Always <= 1.0
						if (lenRatio > ratioThresh && childRatio > ratioThresh) {	// Test both ratios against threshold
							// Keep (don't throw out) if both ratios exceed threshold
							sourceHoldOn.put(entry.getSourceNode(), entry);
							destHoldOn.put(entry.getDestNode(), entry);
						}
					}
				}
			}
			sourceFormatted = sourceHoldOn;			// Update our "current" set of sources
			destFormatted = destHoldOn;				// Update our "current" set of dests
			changed = (keepLen != values.size());	// Did we get any new pairs this round?
			keepLen = sourceHoldOn.values().size();
			sourceHoldOn = new HashSetValuedHashMap<FunctionNode, FunctionPair>();
			destHoldOn = new HashSetValuedHashMap<FunctionNode, FunctionPair>();
			ratioThresh = (2 + ratioThresh) / 3;	// Tighten the ratio threshold for next round
			// Move closer to 1.0 threshold (counts are exactly equal)
		}
		if (finalPairs.isEmpty()) {
			return;							// found no seeds
		}
		Collections.sort(finalPairs, CONF_COMPARATOR);

		double curConf = finalPairs.get(0).getConfResult();
		if (curConf < confThreshold) {
			Msg.warn(this, "Initial value of seed confidence too high (" + confThreshold +
				")...resetting seed confidence to " + curConf);
			confThreshold = curConf;
		}
		int lastIndex = findIndexMatchingThreshold(finalPairs, confThreshold);	// Last index that still meets threshold
		for (int i = 0; i < lastIndex + 1; ++i) {
			FunctionPair pair = finalPairs.get(i);
			seeds.add(pair);
		}
	}
Choose seed FunctionNode pairs with the highest confidence from among {@link #discoveredMatches}
making sure there are no conflicts, (a FunctionNode that is involved in multiple matches).
Selection happens in rounds.  During a round:
  a) "Accept" all pairs for which there is no immediate conflict
  b) If a pair has conflicts, throw it out if either:
         1) The number of children is different between source and dest (difference > threshold) 
         2) The function length is different between source and dest (difference > threshold)
         
Between rounds the "accepted" pairs and the "thrown out" pairs may remove conflicts from the
remaining pairs.  Each round the threshold for throwing out a conflict is tightened.

The process terminates when no new pairs are accepted during a round.
The accepted pairs are sorted by confidence, and those exceeding {@link #confThreshold} become
the final seed set.
@param monitor is the TaskMonitor
@throws CancelledException if the user cancels the correlation
Ghidra/Features/VersionTrackingBSim/src/main/java/ghidra/feature/vt/api/BSimProgramCorrelatorMatching.java