@SuppressWarnings("unchecked")
	@Override
	public void autoMerge(int progressMin, int progressMax, TaskMonitor monitor)
			throws ProgramConflictException, MemoryAccessException, CancelledException {

		latestResolvedDts = (Map<Long, DataType>) mergeManager
				.getResolveInformation(MergeConstants.RESOLVED_LATEST_DTS);
		myResolvedDts = (Map<Long, DataType>) mergeManager
				.getResolveInformation(MergeConstants.RESOLVED_MY_DTS);
		origResolvedDts = (Map<Long, DataType>) mergeManager
				.getResolveInformation(MergeConstants.RESOLVED_ORIGINAL_DTS);

		initializeAutoMerge("Auto-merging Functions and determining conflicts.", progressMin,
			progressMax, monitor);

		clearResolveInfo();

		ProgramDiffFilter filter = new ProgramDiffFilter(ProgramDiffFilter.FUNCTION_DIFFS);

		updateProgress(0, "Finding Function changes in " + LATEST_TITLE + "...");
		ProgramDiff diffEntireOriginalLatest =
			new ProgramDiff(programs[ORIGINAL], programs[LATEST], listingMergeManager.latestSet);

		latestEntireDetailSet = diffEntireOriginalLatest.getDifferences(filter, monitor);
		latestDetailSet = listingMergeManager.diffOriginalLatest.getDifferences(filter, monitor);

		updateProgress(5, "Finding Function changes in " + MY_TITLE + "...");
		myDetailSet = listingMergeManager.diffOriginalMy.getDifferences(filter, monitor);
		MergeUtilities.adjustSets(latestDetailSet, myDetailSet, onlyMyChanged, bothChanged);

		// Determine removes, adds, and changes.
		updateProgress(10, "Categorizing Function changes in " + LATEST_TITLE + "...");
		getLatestEntireChangeTypes(monitor);
		getLatestChangeTypes(monitor);

		updateProgress(15, "Categorizing Function changes in " + MY_TITLE + "...");
		getMyChangeTypes(monitor);

		// Determine changes resulting in potential function body overlap conflicts.
		updateProgress(20, "Finding function body overlap conflicts.");
		AddressSet changeSet = latestEntireDetailSet.union(myDetailSet);
		determineOverlapConflicts(changeSet, monitor);

		updateProgress(25, "Finding function removal conflicts.");
		AddressSet notOverlapConflicts =
			myDetailSet.subtract(overlapAddressSet.intersect(changeSet));
		determineRemoveConflicts(notOverlapConflicts, monitor);

		updateProgress(30, "Finding function body conflicts.");
		AddressSet notRemoveConflicts = notOverlapConflicts.subtract(removeSet);
		AddressSet notRemoveConflictsAndNotAutoRemove = notRemoveConflicts.subtract(autoRemoveSet);
		determineBodyConflicts(notRemoveConflictsAndNotAutoRemove, monitor);

		// If only MY changed and didn't cause overlap with LATEST changes, then autoMerge MY.
		updateProgressMessage("Auto-merging Functions and determining conflicts.");
		AddressSet functionDetailChanges = notRemoveConflictsAndNotAutoRemove.subtract(bodySet);
		AddressSet autoSet = onlyMyChanged.intersect(functionDetailChanges);
		AddressSet onlyMyChangedThunks = getThunkEntrySet(programs[MY], autoSet);
		thunkAutoMergeSet.add(onlyMyChangedThunks);
		AddressSet nonThunkSet = autoSet.subtract(thunkAutoMergeSet);
		mergeEntireFunctions(nonThunkSet, KEEP_MY, monitor);

		AddressSet possibleDetailConflicts = functionDetailChanges.subtract(autoSet);
		long totalAddresses = possibleDetailConflicts.getNumAddresses();
		int addressCount = 0;
		// Auto-merge parts of functions where possible and determine conflicts.
		AddressIterator iter = possibleDetailConflicts.getAddresses(true);
		while (iter.hasNext()) {
			monitor.checkCancelled();
			updateProgress((int) (DETAILS_CONFLICT_START +
				((addressCount * DETAILS_CONFLICT_SIZE) / totalAddresses)));
			Address entry = iter.next();
			Function[] functions = new Function[4];
			functions[RESULT] = functionManagers[RESULT].getFunctionAt(entry);
			functions[ORIGINAL] = functionManagers[ORIGINAL].getFunctionAt(entry);
			functions[LATEST] = functionManagers[LATEST].getFunctionAt(entry);
			functions[MY] = functionManagers[MY].getFunctionAt(entry);
			boolean latestIsThunk =
				(functions[LATEST] != null) ? functions[LATEST].isThunk() : false;
			boolean myIsThunk = (functions[MY] != null) ? functions[MY].isThunk() : false;

			if (latestIsThunk || myIsThunk) {
				determineThunkConflicts(functions, monitor);
				continue;
			}

			determineFunctionConflicts(functions, false, monitor);
		}

		updateProgress(100, "Done auto-merging Functions and determining conflicts.");
		determineConflictSet();

		showResolveErrors(ERROR_TITLE);
		showResolveInfo(INFO_TITLE);
	}
Constructs a function merger.
@param listingMergeMgr the listing merge manager that owns this merger.
Ghidra/Features/Base/src/main/java/ghidra/app/merge/listing/FunctionMerger.java