internal static IList <int> Find(IList <int> values)
        {
            if (values == null)
            {
                throw new ArgumentNullException("values");
            }

            if (values.Count <= 0)
            {
                return(new int[0]);
            }

            var maximum      = 1;
            var predecessors = new int[values.Count];
            var positions    = new int[values.Count];

            predecessors[0] = -1;
            positions[0]    = 0;

            for (var i = 1; i < values.Count; i++)
            {
                // We have found an element that extend the longest subsequence found so far.
                if (values[i] > values[positions[maximum - 1]])
                {
                    predecessors[i]    = positions[maximum - 1];
                    positions[maximum] = i;
                    maximum++;
                }
                else
                {
                    var index = LongestIncreasingSubsequence.FindLeastIndex(i, values, positions, maximum);

                    // This is the lowest value found so far.
                    if (index < 0)
                    {
                        predecessors[i] = -1;
                        positions[0]    = i;
                    }
                    // We have found an element that could help to find new future subsequences.
                    else
                    {
                        Debug.Assert(index + 1 < maximum);

                        predecessors[i]      = positions[index];
                        positions[index + 1] = i;
                    }
                }
            }

            var result   = new int[maximum];
            var position = positions[maximum - 1];

            for (var i = maximum - 1; i >= 0; i--)
            {
                result[i] = values[position];
                position  = predecessors[position];
            }

            return(result);
        }
        internal static void Diff <T>(
            this IList <T> source,
            IList <T> destination,
            ICollection <T> itemsAdded,
            ICollection <T> itemsRemoved,
            ICollection <T> itemsMoved)
        {
            if (source == null)
            {
                throw new ArgumentNullException("source");
            }

            if (destination == null)
            {
                throw new ArgumentNullException("destination");
            }

            // There is nothing to do since the caller is not interested by the result.
            if ((itemsAdded == null) && (itemsRemoved == null) && (itemsMoved == null))
            {
                return;
            }

            var sourceCount     = source.Count;
            var sourcePositions = new Dictionary <T, int>(sourceCount);
            var sequence        = new List <int>(System.Math.Min(sourceCount, destination.Count));
            var isAlive         = new BitArray(sourceCount, false);
            var hasNotMoved     = default(BitArray);

            for (var i = 0; i < sourceCount; i++)
            {
                sourcePositions.Add(source[i], i);
            }

            foreach (var item in destination)
            {
                int index;

                if (sourcePositions.TryGetValue(item, out index))
                {
                    isAlive[index] = true;
                    sequence.Add(index);
                }
                else if (itemsAdded != null)
                {
                    itemsAdded.Add(item);
                }
            }

            // We may omit this part of the algorithm if the caller is not interested by the items that have moved.
            if (itemsMoved != null)
            {
                hasNotMoved = new BitArray(sourceCount, false);

                // The subsequence contains the position of the item that are in the destination collection and that have not moved.
                foreach (var index in LongestIncreasingSubsequence.Find(sequence))
                {
                    hasNotMoved[index] = true;
                }
            }

            // We may omit this part of the algorithm if the caller is not interested by the items that have moved or were removed.
            if ((itemsRemoved != null) || (itemsMoved != null))
            {
                for (var i = 0; i < sourceCount; i++)
                {
                    if (isAlive[i])
                    {
                        // We check if the move collection is not null first because the bit array is null when the move collection is null.
                        if ((itemsMoved != null) && !hasNotMoved[i])
                        {
                            itemsMoved.Add(source[i]);
                        }
                    }
                    else if (itemsRemoved != null)
                    {
                        itemsRemoved.Add(source[i]);
                    }
                }
            }
        }