private void Update(Chunk <NotifyCollectionChangedEventArgs> changes)
        {
            if (changes is null || changes.Count == 0)
            {
                return;
            }

            using (changes.ClearTransaction())
            {
                if (changes.Count > 1)
                {
                    this.Refresh(changes);
                    return;
                }

                var e = changes[0];
                switch (e.Action)
                {
                case NotifyCollectionChangedAction.Add:
                {
                    if (!e.TryGetSingleNewItem <TSource>(out var newSource))
                    {
                        goto case NotifyCollectionChangedAction.Reset;
                    }

                    var index = e.NewStartingIndex;
                    var value = this.GetOrCreate(newSource, index);
                    this.Tracker.Insert(index, value);
                    var args = this.UpdateRange(index + 1, this.Count - 1);
                    args.Add(Diff.CreateAddEventArgs(value, index));
                    this.Notify(args);
                    break;
                }

                case NotifyCollectionChangedAction.Remove:
                {
                    if (!e.TryGetSingleOldItem <TSource>(out var oldSource))
                    {
                        goto case NotifyCollectionChangedAction.Reset;
                    }

                    var index = e.OldStartingIndex;
                    var value = this.Tracker[index];
                    this.Tracker.RemoveAt(index);
                    var argses = this.UpdateRange(index, this.Count - 1);
                    argses.Add(Diff.CreateRemoveEventArgs(value, index));
                    this.Notify(argses);
                    this.factory.Remove(oldSource, value);
                    break;
                }

                case NotifyCollectionChangedAction.Replace:
                {
                    if (!e.TryGetSingleNewItem <TSource>(out var newSource) ||
                        !e.TryGetSingleOldItem <TSource>(out var oldSource))
                    {
                        goto case NotifyCollectionChangedAction.Reset;
                    }

                    var index    = e.NewStartingIndex;
                    var value    = this.GetOrCreate(newSource, index);
                    var oldValue = this.Tracker[e.OldStartingIndex];
                    this.Tracker[index] = value;
                    var arg = Diff.CreateReplaceEventArgs(value, oldValue, index);
                    this.Notify(arg);
                    this.factory.Remove(oldSource, oldValue);
                    break;
                }

                case NotifyCollectionChangedAction.Move:
                {
                    var value = this.Tracker[e.OldStartingIndex];
                    this.Tracker.RemoveAt(e.OldStartingIndex);
                    this.Tracker.Insert(e.NewStartingIndex, value);
                    var args = this.UpdateRange(Math.Min(e.OldStartingIndex, e.NewStartingIndex), Math.Max(e.OldStartingIndex, e.NewStartingIndex));
                    args.Add(Diff.CreateMoveEventArgs(value, e.NewStartingIndex, e.OldStartingIndex));
                    this.Notify(args);
                    break;
                }

                case NotifyCollectionChangedAction.Reset:
                    using (this.factory.RefreshTransaction())
                    {
                        this.Refresh(changes);
                    }

                    break;

                default:
                    throw new ArgumentOutOfRangeException(nameof(changes));
                }
            }
        }