private void ngramPinner(DataVertex left, DataVertex right, int depth) {

		if (depth < 0 || left == null || left.paired || right.paired) {
			return;
		}

		establishMatch(left, right);		// Formally pair the vertices

		boolean goForIt;

		if (left.isCommutative()) {		// Nodes whose incoming edges must be disambiguated
			if (left.op.getOpcode() != PcodeOp.MULTIEQUAL) {
				DataVertex left0 = left.sources.get(0);
				DataVertex left1 = left.sources.get(1);
				DataVertex right0 = right.sources.get(0);
				DataVertex right1 = right.sources.get(1);
				int lasty = left.ngrams.size() - 1;

				if (left0.ngrams.get(lasty).hash == left1.ngrams.get(lasty).hash ||
					right0.ngrams.get(lasty).hash == right1.ngrams.get(lasty).hash) {
					return;
				}

			}
			for (DataVertex srcLeft : left.sources) {		// For each incoming edge of the LEFT side vertex
				if (srcLeft.paired) {
					continue;
				}
				for (DataVertex srcRight : right.sources) {	// Search for a matching edge on RIGHT side
					if (srcRight.paired) {
						continue;
					}
					goForIt = true;
					for (int i = 0; i < Math.max(1, depth); i++) {	// n-grams must match up to given depth
						if (srcLeft.ngrams.get(i).hash != srcRight.ngrams.get(i).hash) {
							goForIt = false;
							break;
						}
					}
					if (goForIt) {			// If all hashes match for the two edges, pair them
						// Try to extend depth of matching hashes before pairing the edges
						int newDepth = Math.max(depth - 1, 0);
						while (-1 < newDepth && newDepth < srcLeft.ngrams.size() && srcLeft.ngrams
								.get(newDepth).hash == srcRight.ngrams.get(newDepth).hash) {
							newDepth++;
						}
						ngramPinner(srcLeft, srcRight, newDepth - 1);	// Recursively match these edges
						break;
					}
				}
			}
		}
		else {					// Edges are paired in order
			if (left.sources.size() == right.sources.size()) {
				for (int n = 0; n < left.sources.size(); n++) {

					DataVertex srcLeft = left.sources.get(n);
					DataVertex srcRight = right.sources.get(n);
					goForIt = true;
					for (int i = 0; i < Math.max(1, depth); i++) {	// n-grams must match up to given depth
						if (srcLeft.ngrams.get(i).hash != srcRight.ngrams.get(i).hash) {
							goForIt = false;
							break;
						}
					}
					if (goForIt) {			// If all hashes match for the two edges, pair them
						// Try to extend depth of matching hashes before pairing edges
						int i = Math.max(depth - 1, 0);
						while (-1 < i && i < srcLeft.ngrams.size() &&
							srcLeft.ngrams.get(i).hash == srcRight.ngrams.get(i).hash) {
							i++;
						}
						ngramPinner(srcLeft, srcRight, i - 1);			// Recursively match these edges
					}
				}
			}
		}
	}
Pin the given pair of data-flow vertices, then recursively try to pin vertices
by following the immediate incoming data-flow edge.  The pair is made for a specific n-gram
depth.  Recursive pairing stops at any point where n-grams don't match up to this depth.
Edges into commutative PcodeOp nodes are also disambiguated with n-gram hashes up to this depth.
@param left is the LEFT side data-flow vertex to pair
@param right is the RIGHT side data-flow vertex to pair
@param depth is the specific n-gram depth for the pair
Ghidra/Features/CodeCompare/src/main/java/ghidra/features/codecompare/graphanalysis/Pinning.java