/// <summary>
        /// Find the best match by file path for a given DiffEntry from a list of
        /// DiffEntrys.
        /// </summary>
        /// <remarks>
        /// Find the best match by file path for a given DiffEntry from a list of
        /// DiffEntrys. The returned DiffEntry will be of the same type as <src>. If
        /// no DiffEntry can be found that has the same type, this method will return
        /// null.
        /// </remarks>
        /// <param name="src">the DiffEntry to try to find a match for</param>
        /// <param name="list">a list of DiffEntrys to search through</param>
        /// <returns>the DiffEntry from <list> who's file path best matches <src></returns>
        private static DiffEntry BestPathMatch(DiffEntry src, IList <DiffEntry> list)
        {
            DiffEntry best  = null;
            int       score = -1;

            foreach (DiffEntry d in list)
            {
                if (SameType(Mode(d), Mode(src)))
                {
                    int tmp = SimilarityRenameDetector.NameScore(Path(d), Path(src));
                    if (tmp > score)
                    {
                        best  = d;
                        score = tmp;
                    }
                }
            }
            return(best);
        }
        /// <exception cref="System.IO.IOException"></exception>
        private void FindContentRenames(ContentSource.Pair reader, ProgressMonitor pm)
        {
            int cnt = Math.Max(added.Count, deleted.Count);

            if (GetRenameLimit() == 0 || cnt <= GetRenameLimit())
            {
                SimilarityRenameDetector d;
                d = new SimilarityRenameDetector(reader, deleted, added);
                d.SetRenameScore(GetRenameScore());
                d.Compute(pm);
                overRenameLimit |= d.IsTableOverflow();
                deleted          = d.GetLeftOverSources();
                added            = d.GetLeftOverDestinations();
                Sharpen.Collections.AddAll(entries, d.GetMatches());
            }
            else
            {
                overRenameLimit = true;
            }
        }
        private void FindExactRenames(ProgressMonitor pm)
        {
            pm.BeginTask(JGitText.Get().renamesFindingExact, added.Count + added.Count + deleted
                         .Count + added.Count * deleted.Count);
            //
            Dictionary <AbbreviatedObjectId, object> deletedMap = PopulateMap(deleted, pm);
            Dictionary <AbbreviatedObjectId, object> addedMap   = PopulateMap(added, pm);
            AList <DiffEntry>          uniqueAdds    = new AList <DiffEntry>(added.Count);
            AList <IList <DiffEntry> > nonUniqueAdds = new AList <IList <DiffEntry> >();

            foreach (object o in addedMap.Values)
            {
                if (o is DiffEntry)
                {
                    uniqueAdds.AddItem((DiffEntry)o);
                }
                else
                {
                    nonUniqueAdds.AddItem((IList <DiffEntry>)o);
                }
            }
            AList <DiffEntry> left = new AList <DiffEntry>(added.Count);

            foreach (DiffEntry a in uniqueAdds)
            {
                object del = deletedMap.Get(a.newId);
                if (del is DiffEntry)
                {
                    // We have one add to one delete: pair them if they are the same
                    // type
                    DiffEntry e = (DiffEntry)del;
                    if (SameType(e.oldMode, a.newMode))
                    {
                        e.changeType = DiffEntry.ChangeType.RENAME;
                        entries.AddItem(ExactRename(e, a));
                    }
                    else
                    {
                        left.AddItem(a);
                    }
                }
                else
                {
                    if (del != null)
                    {
                        // We have one add to many deletes: find the delete with the
                        // same type and closest name to the add, then pair them
                        IList <DiffEntry> list = (IList <DiffEntry>)del;
                        DiffEntry         best = BestPathMatch(a, list);
                        if (best != null)
                        {
                            best.changeType = DiffEntry.ChangeType.RENAME;
                            entries.AddItem(ExactRename(best, a));
                        }
                        else
                        {
                            left.AddItem(a);
                        }
                    }
                    else
                    {
                        left.AddItem(a);
                    }
                }
                pm.Update(1);
            }
            foreach (IList <DiffEntry> adds in nonUniqueAdds)
            {
                object o_1 = deletedMap.Get(adds[0].newId);
                if (o_1 is DiffEntry)
                {
                    // We have many adds to one delete: find the add with the same
                    // type and closest name to the delete, then pair them. Mark the
                    // rest as copies of the delete.
                    DiffEntry d    = (DiffEntry)o_1;
                    DiffEntry best = BestPathMatch(d, adds);
                    if (best != null)
                    {
                        d.changeType = DiffEntry.ChangeType.RENAME;
                        entries.AddItem(ExactRename(d, best));
                        foreach (DiffEntry a_1 in adds)
                        {
                            if (a_1 != best)
                            {
                                if (SameType(d.oldMode, a_1.newMode))
                                {
                                    entries.AddItem(ExactCopy(d, a_1));
                                }
                                else
                                {
                                    left.AddItem(a_1);
                                }
                            }
                        }
                    }
                    else
                    {
                        Sharpen.Collections.AddAll(left, adds);
                    }
                }
                else
                {
                    if (o_1 != null)
                    {
                        // We have many adds to many deletes: score all the adds against
                        // all the deletes by path name, take the best matches, pair
                        // them as renames, then call the rest copies
                        IList <DiffEntry> dels   = (IList <DiffEntry>)o_1;
                        long[]            matrix = new long[dels.Count * adds.Count];
                        int mNext = 0;
                        for (int delIdx = 0; delIdx < dels.Count; delIdx++)
                        {
                            string deletedName = dels[delIdx].oldPath;
                            for (int addIdx = 0; addIdx < adds.Count; addIdx++)
                            {
                                string addedName = adds[addIdx].newPath;
                                int    score     = SimilarityRenameDetector.NameScore(addedName, deletedName);
                                matrix[mNext] = SimilarityRenameDetector.Encode(score, delIdx, addIdx);
                                mNext++;
                            }
                        }
                        Arrays.Sort(matrix);
                        for (--mNext; mNext >= 0; mNext--)
                        {
                            long      ent      = matrix[mNext];
                            int       delIdx_1 = SimilarityRenameDetector.SrcFile(ent);
                            int       addIdx   = SimilarityRenameDetector.DstFile(ent);
                            DiffEntry d        = dels[delIdx_1];
                            DiffEntry a_1      = adds[addIdx];
                            if (a_1 == null)
                            {
                                pm.Update(1);
                                continue;
                            }
                            // was already matched earlier
                            DiffEntry.ChangeType type;
                            if (d.changeType == DiffEntry.ChangeType.DELETE)
                            {
                                // First use of this source file. Tag it as a rename so we
                                // later know it is already been used as a rename, other
                                // matches (if any) will claim themselves as copies instead.
                                //
                                d.changeType = DiffEntry.ChangeType.RENAME;
                                type         = DiffEntry.ChangeType.RENAME;
                            }
                            else
                            {
                                type = DiffEntry.ChangeType.COPY;
                            }
                            entries.AddItem(DiffEntry.Pair(type, d, a_1, 100));
                            adds.Set(addIdx, null);
                            // Claim the destination was matched.
                            pm.Update(1);
                        }
                    }
                    else
                    {
                        Sharpen.Collections.AddAll(left, adds);
                    }
                }
            }
            added   = left;
            deleted = new AList <DiffEntry>(deletedMap.Count);
            foreach (object o_2 in deletedMap.Values)
            {
                if (o_2 is DiffEntry)
                {
                    DiffEntry e = (DiffEntry)o_2;
                    if (e.changeType == DiffEntry.ChangeType.DELETE)
                    {
                        deleted.AddItem(e);
                    }
                }
                else
                {
                    IList <DiffEntry> list = (IList <DiffEntry>)o_2;
                    foreach (DiffEntry e in list)
                    {
                        if (e.changeType == DiffEntry.ChangeType.DELETE)
                        {
                            deleted.AddItem(e);
                        }
                    }
                }
            }
            pm.EndTask();
        }