private static List <DetectionGroup> DetectGroups(List <int> data)
        {
            // Find groups of ascending indices
            var            groups       = new List <DetectionGroup> ();
            DetectionGroup currentGroup = null;

            foreach (var val in data)
            {
                if (currentGroup != null)
                {
                    if (val > currentGroup.Last)
                    {
                        currentGroup.Last  = val;
                        currentGroup.Size += 1;
                    }
                    else
                    {
                        currentGroup = null;
                    }
                }

                if (currentGroup == null)
                {
                    if (val >= 0)
                    {
                        currentGroup = new DetectionGroup()
                        {
                            First = val,
                            Last  = val,
                            Size  = 1,
                        };
                        groups.Add(currentGroup);
                    }
                }
            }
            return(groups);
        }
        public static IEnumerable <ResolveResult> Resolve <T> (List <T> oldList, List <T> newList, Func <T, T, bool> comparer)
        {
            // Make an array of old indexes that correspond to the new array items
            var idxMap = newList
                         .Select(newItem => oldList.FindIndex(oldItem => comparer(newItem, oldItem)))
                         .ToList();

            // Find groups to keep
            var groups = new Queue <DetectionGroup> (FindGroupsToKeep(DetectGroups(idxMap)));

            // Determine items to delete
            for (var oldIdx = 0; oldIdx < oldList.Count; oldIdx++)
            {
                var oldItem = oldList [oldIdx];
                if (!newList.Any(newItem => comparer(newItem, oldItem)))
                {
                    yield return(new ResolveResult()
                    {
                        OldIndex = oldIdx,
                        NewIndex = -1,
                        Action = ResolvedAction.Delete,
                    });
                }
            }

            // Determine items to insert or keep:
            DetectionGroup currentGroup = null;

            for (var newIdx = 0; newIdx < idxMap.Count; newIdx++)
            {
                var oldIdx = idxMap [newIdx];

                // Reached end of a group:
                if (oldIdx == -1 || (currentGroup != null && currentGroup.Last < oldIdx))
                {
                    currentGroup = null;
                }

                // See if we can use next group item:
                if (currentGroup == null)
                {
                    var nextGroup = groups.Count > 0 ? groups.Peek() : null;
                    if (nextGroup != null && nextGroup.First == oldIdx)
                    {
                        currentGroup = groups.Dequeue();
                    }
                }

                if (currentGroup == null)
                {
                    yield return(new ResolveResult()
                    {
                        OldIndex = -1,
                        NewIndex = newIdx,
                        Action = ResolvedAction.Insert,
                    });
                }
                else
                {
                    yield return(new ResolveResult()
                    {
                        OldIndex = oldIdx,
                        NewIndex = newIdx,
                        Action = ResolvedAction.Keep,
                    });
                }
            }
        }