Skip to content
This repository has been archived by the owner on Oct 4, 2023. It is now read-only.

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
v5.0.1-Df rel
  • Loading branch information
samaysar committed Sep 24, 2020
2 parents ba3ca4d + 6e3bbaf commit dbb3dc4
Show file tree
Hide file tree
Showing 8 changed files with 192 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Reflection;
using Dot.Net.DevFast.Collections;
using Dot.Net.DevFast.Collections.Interfaces;
Expand Down Expand Up @@ -41,7 +42,7 @@ public void Properties_Are_Well_Defined(int capacity)
[Test]
public void Add_N_Try_Add_Behaves_For_Empty_Heap()
{
IHeap<int> instance = new AbstractBinaryTestHeap(0, (x, y) => x < y);
IHeap<int> instance = new TestAbstractBinaryHeap(0, (x, y) => x < y);
Assert.IsFalse(instance.TryAdd(1));
var ex = Assert.Throws<DdnDfException>(() => instance.Add(1));
Assert.NotNull(ex);
Expand All @@ -52,15 +53,15 @@ public void Add_N_Try_Add_Behaves_For_Empty_Heap()
[Test]
public void Add_N_Try_Add_Behaves_For_Non_Empty_Heap()
{
IHeap<int> instance = new AbstractBinaryTestHeap(1, (x, y) => x < y);
IHeap<int> instance = new TestAbstractBinaryHeap(1, (x, y) => x < y);
Assert.True(instance.IsEmpty);
Assert.IsTrue(instance.TryAdd(1));
Assert.IsFalse(instance.TryAdd(1));
var ex = Assert.Throws<DdnDfException>(() => instance.Add(1));
Assert.NotNull(ex);
Assert.IsTrue(ex.ErrorCode.Equals(DdnDfErrorCode.DemandUnfulfilled));
Assert.IsTrue(ex.Message.Equals("(DemandUnfulfilled) Unable to add element in the heap."));
instance = new AbstractBinaryTestHeap(3, (x, y) => x < y);
instance = new TestAbstractBinaryHeap(3, (x, y) => x < y);
instance.Add(3);
Assert.False(instance.IsFull);
instance.Add(2);
Expand All @@ -69,6 +70,33 @@ public void Add_N_Try_Add_Behaves_For_Non_Empty_Heap()
Assert.True(instance.IsFull);
}

[Test]
public void AddAll_Properly_Adds_All_Elements_And_Returns_The_Count()
{
var items = new[] {2, 4, 0, 1, 2};
Assert.IsTrue(new TestAbstractBinaryHeap(0, (x, y) => x < y).AddAll(items).Equals(0));
Assert.IsTrue(new TestAbstractBinaryHeap(3, (x, y) => x < y).AddAll(items).Equals(3));
Assert.IsTrue(new TestAbstractBinaryHeap(5, (x, y) => x < y).AddAll(items).Equals(5));
Assert.IsTrue(new TestAbstractBinaryHeap(10, (x, y) => x < y).AddAll(items).Equals(5));
}

[Test]
public void PopAll_Properly_Maintains_Order_And_Sequence()
{
var items = new[] { 2, 4, 0, 1, 2 };
var expected = new[] {0, 1, 2, 2, 4};
var instance = new TestAbstractBinaryHeap(10, (x, y) => x < y);
instance.AddAll(items);
var poppedItems = instance.PopAll().ToList();
Assert.IsTrue(poppedItems.Count.Equals(5));
Assert.AreEqual(poppedItems, expected);
poppedItems = instance.PopAll().ToList();
Assert.IsTrue(poppedItems.Count.Equals(0));
instance.AddAll(items);
poppedItems = instance.PopAll().ToList();
Assert.IsTrue(poppedItems.Count.Equals(5));
}

[Test]
[TestCase(0)]
[TestCase(1)]
Expand All @@ -83,7 +111,7 @@ public void Peek_N_TryPeek_Behaves_For_Empty_Heap(int capacity)
[Test]
public void Peek_N_TryPeek_Behaves_For_Non_Empty_Heap()
{
IHeap<int> instance = new AbstractBinaryTestHeap(1, (x, y) => x < y);
IHeap<int> instance = new TestAbstractBinaryHeap(1, (x, y) => x < y);
instance.Add(1);
Assert.AreEqual(instance.Peek(), 1);
Assert.True(instance.TryPeek(out var val) && val.Equals(1));
Expand All @@ -103,7 +131,7 @@ public void Pop_N_TryPop_Behaves_For_Empty_Heap(int capacity)
[Test]
public void Pop_N_TryPop_Behaves_For_Non_Empty_Heap()
{
IHeap<int> instance = new AbstractBinaryTestHeap(5, (x, y) => x < y);
IHeap<int> instance = new TestAbstractBinaryHeap(5, (x, y) => x < y);
instance.Add(3);
instance.Add(2);
instance.Add(1);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
using System.Threading;
using System;
using System.Linq;
using System.Threading;
using Dot.Net.DevFast.Collections;
using Dot.Net.DevFast.Collections.Concurrent;
using Dot.Net.DevFast.Collections.Interfaces;
using Dot.Net.DevFast.Etc;
using Dot.Net.DevFast.Tests.TestHelpers;
using NSubstitute;
using NUnit.Framework;

Expand Down Expand Up @@ -100,6 +103,18 @@ public void Methods_Are_Accessed_Inside_Lock()
Assert.IsTrue(Monitor.IsEntered(syncRoot));
return true;
});
var addThis = new[] {1, 2};
heap.AddAll(addThis).Returns(x =>
{
Assert.IsTrue(Monitor.IsEntered(syncRoot));
return default;
});
var popAllVals = new[] {1, 2, 3, 4};
heap.PopAll().Returns(x =>
{
Assert.IsTrue(Monitor.IsEntered(syncRoot));
return popAllVals;
});
heap.When(x => x.Compact()).Do(x => Assert.IsTrue(Monitor.IsEntered(syncRoot)));
heap.When(x => x.FreezeCapacity(true)).Do(x => Assert.IsTrue(Monitor.IsEntered(syncRoot)));

Expand All @@ -110,6 +125,8 @@ public void Methods_Are_Accessed_Inside_Lock()
Assert.IsTrue(instance.TryPop(out var val2) && val2.Equals(2));
instance.Add(1);
Assert.IsTrue(instance.TryAdd(1));
Assert.AreEqual(instance.AddAll(addThis), 0);
Assert.AreEqual(instance.PopAllConsistent(), popAllVals);
instance.Compact();
instance.FreezeCapacity(true);
heap.Received(1).Pop();
Expand All @@ -119,7 +136,23 @@ public void Methods_Are_Accessed_Inside_Lock()
heap.Received(1).Add(1);
heap.Received(1).TryAdd(1);
heap.Received(1).Compact();
heap.Received(1).AddAll(addThis);
heap.Received(1).PopAll();
heap.Received(1).FreezeCapacity(true);
// PopAll separated as it reuses TryPop!--------------->
heap.ClearReceivedCalls();
var popRetVal = 0;
heap.TryPop(out Arg.Any<int>()).Returns(x =>
{
Assert.IsTrue(Monitor.IsEntered(syncRoot));
x[0] = 10;
return Interlocked.Increment(ref popRetVal) <= 2;
});
var allPopped = instance.PopAll().ToArray();
Assert.AreEqual(allPopped, new[] {10, 10});
heap.Received(3).TryPop(out Arg.Any<int>());
Assert.IsTrue(new LockBasedConcurrentHeap<int>(new MinHeap<int>(0), syncRoot).PopAllConsistent().Count
.Equals(0));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
using System;
using System.Threading;
using Dot.Net.DevFast.Collections;
using Newtonsoft.Json;
using NUnit.Framework;

namespace Dot.Net.DevFast.Tests.TestHelpers
{
[JsonObject(MemberSerialization.OptOut)]
public class TestObject
{
[JsonIgnore]
private static readonly Random Ran = new Random();
[JsonIgnore] private static readonly Random Ran = new Random();

[JsonConstructor]
public TestObject()
Expand All @@ -24,11 +25,11 @@ public TestObject()
public byte[] BytesProp { get; set; }
}

public class AbstractBinaryTestHeap : AbstractBinaryHeap<int>
public class TestAbstractBinaryHeap : AbstractBinaryHeap<int>
{
private readonly Func<int, int, bool> _comparer;

public AbstractBinaryTestHeap(int initialCapacity,
public TestAbstractBinaryHeap(int initialCapacity,
Func<int, int, bool> comparer) : base(initialCapacity)
{
_comparer = comparer;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Dot.Net.DevFast.Collections.Interfaces;
using Dot.Net.DevFast.Etc;
using Dot.Net.DevFast.Extensions;
Expand Down Expand Up @@ -108,12 +109,42 @@ public bool TryAdd(T item)
return true;
}

/// <inheritdoc />
public IEnumerable<T> PopAll()
{
while (TryPop(out var item))
{
yield return item;
}
}

/// <inheritdoc />
public int AddAll(IEnumerable<T> items)
{
var count = 0;
foreach (var item in items)
{
if (!TryAdd(item)) return count;
count++;
}

return count;
}

/// <inheritdoc />
public void Compact()
{
InternalCopyData(Count);
}

internal List<T> PopAllConsistent()
{
var results = new List<T>(Count);
results.AddRange(PopAll());
return results;
}


private void BubbleUp(int current)
{
while (!current.Equals(0))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Dot.Net.DevFast.Collections.Interfaces;
using Dot.Net.DevFast.Etc;
using Dot.Net.DevFast.Extensions;
Expand Down Expand Up @@ -157,6 +159,72 @@ public bool TryAdd(T item)
}
}

/// <summary>
/// Pops all the items (without using snapshot of the current state), i.e.
/// during enumeration, all other permutations will be reflected.
/// Though it is safe to call this method several times concurrently, each enumeration
/// will only receive the part of the state (dependents on the exact state of the heap at runtime). Hence,
/// order is guaranteed, in absence of concurrent adding, otherwise not.
/// In effect, if only single call of this method is running without any other permutation
/// to its state (like no other add, pop is called concurrently),
/// then the output will be the enumeration of all the elements in the correct order.
/// Finally, if only concurrent pops are running (without any concurrent adding), then order
/// is still guaranteed but not the sequence pattern.
/// </summary>
public IEnumerable<T> PopAll()
{
bool hasItem;
T next;
lock (_syncRoot)
{
hasItem = _heap.TryPop(out next);
}

while (hasItem)
{
yield return next;
lock (_syncRoot)
{
hasItem = _heap.TryPop(out next);
}
}
}

/// <summary>
/// Complementary to <see cref="PopAll"/>. It extracts all the elements based on
/// current state of the heap.
/// <para>
/// CAREFUL: As the method returns <seealso cref="List{T}"/> (instead of <seealso cref="IEnumerable{T}"/>),
/// it will allocate memory. For large heap, this can lead to latency and memory consumption.
/// </para>
/// </summary>
public List<T> PopAllConsistent()
{
lock (_syncRoot)
{
return _heap is AbstractBinaryHeap<T> abstractHeap
? abstractHeap.PopAllConsistent()
: _heap.PopAll().ToList();
}
}

/// <summary>
/// Adds all the items (using snapshot of the current state) to the heap, i.e.
/// during enumeration, all elements will be added based on the state that was captured.
/// And, it is also safe to call this method several times concurrently, each enumeration
/// will add elements to heap based on its runtime state. Nonetheless, for the whole duration of the enumeration,
/// the underlying head wont be usable and all other operations will block. If the supplied enumeration is slow
/// to execute and/or time consuming otherwise, AVOID this method and instead use <see cref="Add"/>.
/// Finally, order and elements' sequencing, both, are guaranteed, in absence of concurrent pops, otherwise not.
/// </summary>
public int AddAll(IEnumerable<T> items)
{
lock (_syncRoot)
{
return _heap.AddAll(items);
}
}

/// <inheritdoc />
public void Compact()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace Dot.Net.DevFast.Collections.Interfaces
using System.Collections.Generic;

namespace Dot.Net.DevFast.Collections.Interfaces
{
/// <summary>
/// Heap data structure interface.
Expand Down Expand Up @@ -62,5 +64,17 @@ public interface IHeap<T>
/// </summary>
/// <param name="item">Element to add.</param>
bool TryAdd(T item);

/// <summary>
/// Removes all the elements from the heap.
/// </summary>
IEnumerable<T> PopAll();

/// <summary>
/// Adds all elements of the given enumeration to the heap.
/// Returns the count of the elements that were successfully added.
/// </summary>
/// <param name="items">Enumeration of the Elements to add.</param>
int AddAll(IEnumerable<T> items);
}
}
2 changes: 1 addition & 1 deletion Dot.Net.DevFast/src/Dot.Net.DevFast/Dot.Net.DevFast.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<PackageIcon>icon.png</PackageIcon>
<RepositoryUrl>https://github.com/samaysar/dotdotnet</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>devfast fastdev dot.net stream-processing declarative-programming extension-methods fastdevelopement</PackageTags>
<PackageTags>devfast fastdev dot.net stream-processing declarative-programming extension-methods fastdevelopement heap min-heap max-heap</PackageTags>
<PackageReleaseNotes>https://raw.githubusercontent.com/samaysar/dotdotnet/develop/ReleaseNotes.txt</PackageReleaseNotes>
<NeutralLanguage />
<Version>1.0.0</Version>
Expand Down
5 changes: 5 additions & 0 deletions ReleaseNotes.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
Changes to Dot.Net.DevFast 5.0.1

1. Change/Fix - Added PopAll and AddAll methods on IHeap interface
2. New feature - LockBasedConcurrentHeap additionally exposes PopAllConsistent method.

Changes to Dot.Net.DevFast 5.0.0

1. Change/Fix - IConsumer interface extends IAsyncDisposable in .Net Standard 2.1 and IDisposable in older .Net Frameworks
Expand Down

0 comments on commit dbb3dc4

Please sign in to comment.