protected List<FunctionPair> pairFunctions(LSHVectorFactory vectorFactory,
		List<DescriptionManager> vec2func, List<VectorResult> vectors, int hitcount,
		int pairThreshold) {
		int totalSize = hitcount * (hitcount + 1) / 2;
		if (totalSize > pairThreshold) {
			return null;
		}
		List<FunctionPair> result = new ArrayList<FunctionPair>(totalSize);
		VectorCompare vectorCompare = new VectorCompare();
		for (int v1 = 0; v1 < vec2func.size(); ++v1) {
			for (int v2 = v1; v2 < vec2func.size(); ++v2) {
				double similarity = vectors.get(v1).vec.compare(vectors.get(v2).vec, vectorCompare);
				if (similarity < simThreshold) {
					continue;
				}
				double significance = vectorFactory.calculateSignificance(vectorCompare);
				if (significance < sigThreshold) {
					continue;
				}
				if (v1 == v2) {
					Iterator<FunctionDescription> iter1 = vec2func.get(v1).listAllFunctions();
					while (iter1.hasNext()) {
						FunctionDescription func1 = iter1.next();
						int func1Index = func1.getExecutableRecord().getXrefIndex();
						if (func1Index == 0) {
							continue;			// Executable is not in our scoring set
						}
						FunctionDescription func2;
						Iterator<FunctionDescription> iter2 = vec2func.get(v1).listAllFunctions();
						do {
							func2 = iter2.next();
							int func2Index = func2.getExecutableRecord().getXrefIndex();
							if (func2Index == 0) {
								continue;		// Executable is not in our scoring set
							}
							result.add(new FunctionPair(func1, func2, similarity, significance));
						}
						while (func2 != func1);
					}
				}
				else {
					Iterator<FunctionDescription> iter1 = vec2func.get(v1).listAllFunctions();
					while (iter1.hasNext()) {
						FunctionDescription func1 = iter1.next();
						int func1Index = func1.getExecutableRecord().getXrefIndex();
						if (func1Index == 0) {
							continue;			// Executable is not in our scoring set
						}
						Iterator<FunctionDescription> iter2 = vec2func.get(v2).listAllFunctions();
						while (iter2.hasNext()) {
							FunctionDescription func2 = iter2.next();
							int func2Index = func2.getExecutableRecord().getXrefIndex();
							if (func2Index == 0) {
								continue;		// Executable is not in our scoring set
							}
							result.add(new FunctionPair(func1, func2, similarity, significance));
						}
					}
				}
			}
		}
		return result;
	}
Generate all pairs of functions for any function associated with a list of vectors
For each pair of functions generate the FunctionPair object with corresponding
similarity and significance.  This is inherently quadratic, but we try to be efficient.
Duplicate vector pairs are only compared once and the similarity cached,
and each function pair is generated only once, ie.  (funcA,funcB)  but not (funcB,funcA)
@param vectorFactory provides weights for significance scores
@param vec2func is the list of FunctionDescription sets associated with each vector
@param vectors is the list of vectors
@param hitcount is the cumulative total of functions
@param pairThreshold is the maximum number of pairs that can be produced
@return the array of FunctionPairs or null if pairThreshold is exceeded
Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/client/ExecutableScorer.java