public static ILiveList<int> Range(ILiveValue<int> startValue, ILiveValue<int> countValue) { LiveObserver<IValueState<int>> startObserver = null; LiveObserver<IValueState<int>> countObserver = null; return LiveListObservable<int>.Create( innerChanged => { countValue.Subscribe(countObserver = countValue.CreateObserver(innerChanged)); startValue.Subscribe(startObserver = startValue.CreateObserver(innerChanged)); return Lockers.Empty; }, (innerChanged, notified, stateLock, oldState) => { // get state var count = countObserver.GetState(); var start = startObserver.GetState(); var newStatus = count.Status.And(start.Status); // work out delta ListDelta<int> delta = null; if (newStatus.IsDeltaRelevant() && (count.HasChange || start.HasChange)) { delta = new ListDelta<int>(); // adjust head var head = Math.Min(Math.Max(start.OldValue - start.NewValue, -count.OldValue), count.NewValue); if (head < 0) delta.Delete(0, Enumerable.Range(start.OldValue, -head)); else if (head > 0) delta.Insert(0, Enumerable.Range(start.NewValue, head)); var currentCount = count.OldValue + head; var leftoverCount = count.OldValue + Math.Min(0, head); // adjust tail var tail = count.NewValue - currentCount; if (tail > 0) delta.Insert(currentCount, Enumerable.Range(start.NewValue + currentCount, tail)); else if (tail < 0) delta.Delete(count.NewValue, Enumerable.Range(start.OldValue + count.OldValue + tail, -tail)); } // work out new state var result = new CollectionState<int, IListDelta<int>, IList<int>>(); result.SetState( oldState.GetStatus().Add(newStatus), delta, Enumerable.Range(start.NewValue, count.NewValue), Math.Max(count.LastUpdated, start.LastUpdated), stateLock); return result; }, () => { startObserver.Dispose(); countObserver.Dispose(); }); }
public void TestListDeltaCanonical() { { var d = new ListDelta<int>(); d.Insert(0, new[] { 100 }); d.Insert(1, new[] { 101 }); d.Delete(2, new[] { 0, 1 }); Assert.IsTrue(d.IndexDeltas.SequenceEqual(new[] { new IndexNode<int> { Index = 0, DenseIndex = 0, Data = new ListIndexDelta<int> { InsertItems = new[] { 100, 101 }, DeleteItems = new[] { 0, 1 }, }, }, }, new IndexNodeComparer<int>())); } { var d = new ListDelta<int>(); d.Delete(0, new[] { 0, 1 }); d.Insert(0, new[] { 100 }); d.Insert(1, new[] { 101 }); Assert.IsTrue(d.IndexDeltas.SequenceEqual(new[] { new IndexNode<int> { Index = 0, DenseIndex = 0, Data = new ListIndexDelta<int> { InsertItems = new[] { 100, 101 }, DeleteItems = new[] { 0, 1 }, }, }, }, new IndexNodeComparer<int>())); } { var d = new ListDelta<int>(); d.Delete(0, new[] { 100 }); d.Delete(2, new[] { 102 }); d.Delete(1, new[] { 101 }); var indexDeltas = d.IndexDeltas.ToArray(); Assert.IsTrue(indexDeltas.SequenceEqual(new[] { new IndexNode<int> { Index = 0, DenseIndex = 0, Data = new ListIndexDelta<int> { InsertItems = new int[] { }, DeleteItems = new[] { 100 }, }, }, new IndexNode<int> { Index = 1, DenseIndex = 1, Data = new ListIndexDelta<int> { InsertItems = new int[] { }, DeleteItems = new[] { 101, 102 }, }, }, }, new IndexNodeComparer<int>())); } // case where the root node gets canonicalized away { var d = new ListDelta<int>(); d.Update(4, 4, 104); d.Update(6, 6, 106); d.Update(2, 2, 102); d.Update(8, 8, 108); d.Update(3, 3, 103); // <== root changed from 104 to 106 var indexDeltas = d.IndexDeltas.ToArray(); Assert.IsTrue(indexDeltas.SequenceEqual(new[] { new IndexNode<int> { Index = 2, DenseIndex = 0, Data = new ListIndexDelta<int> { InsertItems = new[] { 102, 103, 104 }, DeleteItems = new[] { 2, 3, 4 }, }, }, new IndexNode<int> { Index = 6, DenseIndex = 1, Data = new ListIndexDelta<int> { InsertItems = new[] { 106 }, DeleteItems = new[] { 6 }, }, }, new IndexNode<int> { Index = 8, DenseIndex = 2, Data = new ListIndexDelta<int> { InsertItems = new[] { 108 }, DeleteItems = new[] { 8 }, }, }, }, new IndexNodeComparer<int>())); } }
public void TestVirtualListDeltaUpdates() { const int size = 10; var r = new Random(1); for (var o = 0; o < 10000; o++) { var v = new ListDelta<int>(); var l = new List<int>(Enumerable.Range(0, size).Select(i => i * 1000000)); for (var i = 0; i < 30; i++) { var index = r.Next(size); var newValue = l[index] + 1; v.Update(index, l[index], newValue); l[index] = newValue; } } }
public void TestListDeltaSimple() { // single delete { var d = new ListDelta<int>(); d.Delete(3, new[] { 1 }); Assert.IsTrue(d.IndexDeltas.SequenceEqual(new[] { new IndexNode<int> { Index = 3, DenseIndex = 0, Data = new ListIndexDelta<int> { DeleteItems = new[] { 1 }, }, }, }, new IndexNodeComparer<int>())); } // subsequent deletes { var d = new ListDelta<int>(); d.Delete(3, new[] { 1 }); d.Delete(3, new[] { 2 }); Assert.IsTrue(d.IndexDeltas.SequenceEqual(new[] { new IndexNode<int> { Index = 3, DenseIndex = 0, Data = new ListIndexDelta<int> { DeleteItems = new[] { 1, 2 }, }, }, }, new IndexNodeComparer<int>())); } // subsequent deletes { var d = new ListDelta<int>(); d.Delete(1, new[] { 1 }); d.Delete(0, new[] { 0 }); Assert.IsTrue(d.IndexDeltas.SequenceEqual(new[] { new IndexNode<int> { Index = 0, DenseIndex = 0, Data = new ListIndexDelta<int> { DeleteItems = new[] { 0, 1 }, }, }, }, new IndexNodeComparer<int>())); } // single insert { var d = new ListDelta<int>(); d.Insert(3, new[] { 1 }); Assert.IsTrue(d.IndexDeltas.SequenceEqual(new[] { new IndexNode<int> { Index = 3, DenseIndex = 0, Data = new ListIndexDelta<int> { InsertItems = new[] { 1 }, }, }, }, new IndexNodeComparer<int>())); } // subsequent inserts { var d = new ListDelta<int>(); d.Insert(3, new[] { 1 }); d.Insert(3, new[] { 2 }); Assert.IsTrue(d.IndexDeltas.SequenceEqual(new[] { new IndexNode<int> { Index = 3, DenseIndex = 0, Data = new ListIndexDelta<int> { InsertItems = new[] { 2, 1 }, }, }, }, new IndexNodeComparer<int>())); } // insert and delete complementing (update) { var d = new ListDelta<int>(); d.Delete(3, new[] { 1 }); d.Insert(3, new[] { 2 }); Assert.IsTrue(d.IndexDeltas.SequenceEqual(new[] { new IndexNode<int> { Index = 3, DenseIndex = 0, Data = new ListIndexDelta<int> { InsertItems = new[] { 2 }, DeleteItems = new[] { 1 }, }, }, }, new IndexNodeComparer<int>())); } // multiple updates { var d = new ListDelta<int>(); d.Update(3, 1, 2); d.Update(3, 2, 3); Assert.IsTrue(d.IndexDeltas.SequenceEqual(new[] { new IndexNode<int> { Index = 3, DenseIndex = 0, Data = new ListIndexDelta<int> { InsertItems = new[] { 3 }, DeleteItems = new[] { 1 }, }, }, }, new IndexNodeComparer<int>())); } // insert and delete offsetting { var d = new ListDelta<int>(); d.Insert(3, new[] { 1 }); d.Delete(3, new[] { 1 }); Assert.IsTrue(!d.IndexDeltas.Any()); } // interleaved offsetting delete/inserts { var d = new ListDelta<int>(); d.Delete(0, new[] { 1, 2, 3, 4, 5, 7 }); d.Insert(0, new[] { 1, 2, 4, 5, 6, 7 }); Assert.IsTrue(d.IndexDeltas.SequenceEqual(new[] { new IndexNode<int> { Index = 2, DenseIndex = 0, Data = new ListIndexDelta<int> { DeleteItems = new[] { 3 }, }, }, new IndexNode<int> { Index = 4, DenseIndex = 1, Data = new ListIndexDelta<int> { InsertItems = new[] { 6 }, }, } }, new IndexNodeComparer<int>())); } }
public void TestListDeltaMerge() { // subsequent deletes { var d = new ListDelta<int>(); var d2 = new ListDelta<int>(); d.Delete(2, new[] { 1, 2, 3 }); d2.Delete(2, new[] { 4, 5, 6 }); d.Add(d2); Assert.IsTrue(d.IndexDeltas.SequenceEqual(new[] { new IndexNode<int> { Index = 2, DenseIndex = 0, Data = new ListIndexDelta<int> { DeleteItems = new[] { 1, 2, 3, 4, 5, 6 }, }, }, }, new IndexNodeComparer<int>())); } // subsequent updates { var d = new ListDelta<int>(); d.Update(2, 1, 2); var d2 = new ListDelta<int>(); d2.Update(3, 1, 2); d.Add(d2); Assert.IsTrue(d.IndexDeltas.SequenceEqual(new[] { new IndexNode<int> { Index = 2, DenseIndex = 0, Data = new ListIndexDelta<int> { InsertItems = new[] { 2, 2 }, DeleteItems = new[] { 1, 1 }, }, }, }, new IndexNodeComparer<int>())); } }
public void TestListDeltaEmpty() { { var d = new ListDelta<int>(); d.Insert(0, new[] { 1 }); d.Delete(0, new[] { 1 }); Assert.IsTrue(d.IndexDeltas.SequenceEqual( new IndexNode<int>[0], new IndexNodeComparer<int>())); } { var d = new ListDelta<int>(); d.Delete(0, new[] { 1 }); d.Insert(0, new[] { 1 }); Assert.IsTrue(d.IndexDeltas.SequenceEqual( new IndexNode<int>[0], new IndexNodeComparer<int>())); } }