Add dictionary overloads for priority queue
This commit is contained in:
1 parent
ff38aa302a
commit
33187c1053
2 files changed
+392
No files matched your search
@@ -2,6 +2,8 @@ using System.Runtime.CompilerServices;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Recyclable.Collections.Pools;
|
using Recyclable.Collections.Pools;
|
||||||
|
|
||||||
@@ -9,6 +11,96 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
internal static class zRecyclablePriorityQueueAddRange
|
internal static class zRecyclablePriorityQueueAddRange
|
||||||
{
|
{
|
||||||
|
private static class AddRangeHelper<T>
|
||||||
|
{
|
||||||
|
private static readonly Action<RecyclablePriorityQueue<T>, object>? _dictionaryAdder;
|
||||||
|
private static readonly Action<RecyclablePriorityQueue<T>, object>? _sortedListAdder;
|
||||||
|
private static readonly Action<RecyclablePriorityQueue<T>, object>? _sortedDictionaryAdder;
|
||||||
|
private static readonly Type? _dictionaryType;
|
||||||
|
private static readonly Type? _sortedListType;
|
||||||
|
private static readonly Type? _sortedDictionaryType;
|
||||||
|
|
||||||
|
static AddRangeHelper()
|
||||||
|
{
|
||||||
|
Type elementType = typeof(T);
|
||||||
|
if (elementType.IsGenericType && elementType.GetGenericTypeDefinition() == typeof(KeyValuePair<,>))
|
||||||
|
{
|
||||||
|
Type[] args = elementType.GetGenericArguments();
|
||||||
|
_dictionaryType = typeof(RecyclableDictionary<,>).MakeGenericType(args);
|
||||||
|
_sortedDictionaryType = typeof(RecyclableSortedDictionary<,>).MakeGenericType(args);
|
||||||
|
|
||||||
|
MethodInfo? method = typeof(zRecyclablePriorityQueueAddRange).GetMethod(
|
||||||
|
nameof(AddRange),
|
||||||
|
BindingFlags.NonPublic | BindingFlags.Static,
|
||||||
|
binder: null,
|
||||||
|
types: new[] { typeof(RecyclablePriorityQueue<>).MakeGenericType(elementType), _dictionaryType },
|
||||||
|
modifiers: null);
|
||||||
|
if (method != null)
|
||||||
|
{
|
||||||
|
var queueParam = Expression.Parameter(typeof(RecyclablePriorityQueue<T>));
|
||||||
|
var objParam = Expression.Parameter(typeof(object));
|
||||||
|
var call = Expression.Call(method, queueParam, Expression.Convert(objParam, _dictionaryType));
|
||||||
|
_dictionaryAdder = Expression.Lambda<Action<RecyclablePriorityQueue<T>, object>>(call, queueParam, objParam).Compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
method = typeof(zRecyclablePriorityQueueAddRange).GetMethod(
|
||||||
|
nameof(AddRange),
|
||||||
|
BindingFlags.NonPublic | BindingFlags.Static,
|
||||||
|
binder: null,
|
||||||
|
types: new[] { typeof(RecyclablePriorityQueue<>).MakeGenericType(elementType), _sortedDictionaryType },
|
||||||
|
modifiers: null);
|
||||||
|
if (method != null)
|
||||||
|
{
|
||||||
|
var queueParam = Expression.Parameter(typeof(RecyclablePriorityQueue<T>));
|
||||||
|
var objParam = Expression.Parameter(typeof(object));
|
||||||
|
var call = Expression.Call(method, queueParam, Expression.Convert(objParam, _sortedDictionaryType));
|
||||||
|
_sortedDictionaryAdder = Expression.Lambda<Action<RecyclablePriorityQueue<T>, object>>(call, queueParam, objParam).Compile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (elementType.IsGenericType && elementType.GetGenericTypeDefinition() == typeof(ValueTuple<,>))
|
||||||
|
{
|
||||||
|
Type[] args = elementType.GetGenericArguments();
|
||||||
|
_sortedListType = typeof(RecyclableSortedList<,>).MakeGenericType(args);
|
||||||
|
MethodInfo? method = typeof(zRecyclablePriorityQueueAddRange).GetMethod(
|
||||||
|
nameof(AddRange),
|
||||||
|
BindingFlags.NonPublic | BindingFlags.Static,
|
||||||
|
binder: null,
|
||||||
|
types: new[] { typeof(RecyclablePriorityQueue<>).MakeGenericType(elementType), _sortedListType },
|
||||||
|
modifiers: null);
|
||||||
|
if (method != null)
|
||||||
|
{
|
||||||
|
var queueParam = Expression.Parameter(typeof(RecyclablePriorityQueue<T>));
|
||||||
|
var objParam = Expression.Parameter(typeof(object));
|
||||||
|
var call = Expression.Call(method, queueParam, Expression.Convert(objParam, _sortedListType));
|
||||||
|
_sortedListAdder = Expression.Lambda<Action<RecyclablePriorityQueue<T>, object>>(call, queueParam, objParam).Compile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool TryAddRange(RecyclablePriorityQueue<T> queue, IEnumerable<T> items)
|
||||||
|
{
|
||||||
|
if (_dictionaryAdder != null && _dictionaryType!.IsInstanceOfType(items))
|
||||||
|
{
|
||||||
|
_dictionaryAdder(queue, items);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_sortedDictionaryAdder != null && _sortedDictionaryType!.IsInstanceOfType(items))
|
||||||
|
{
|
||||||
|
_sortedDictionaryAdder(queue, items);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_sortedListAdder != null && _sortedListType!.IsInstanceOfType(items))
|
||||||
|
{
|
||||||
|
_sortedListAdder(queue, items);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static void EnsureCapacity<T>(RecyclablePriorityQueue<T> queue, int min)
|
private static void EnsureCapacity<T>(RecyclablePriorityQueue<T> queue, int min)
|
||||||
{
|
{
|
||||||
@@ -821,6 +913,155 @@ namespace Recyclable.Collections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
internal static void AddRange<TKey, TValue>(this RecyclablePriorityQueue<KeyValuePair<TKey, TValue>> queue, RecyclableDictionary<TKey, TValue> items)
|
||||||
|
where TKey : notnull
|
||||||
|
{
|
||||||
|
int count = items._count;
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int startIndex = queue._size;
|
||||||
|
int newCount = startIndex + count;
|
||||||
|
EnsureCapacity(queue, newCount);
|
||||||
|
|
||||||
|
var entries = items._entries;
|
||||||
|
int shift = items._blockShift,
|
||||||
|
mask = items._blockSizeMinus1;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
ref var src = ref entries[i >> shift][i & mask];
|
||||||
|
queue._heap[startIndex + i] = new KeyValuePair<TKey, TValue>(src.Key, src.Value);
|
||||||
|
}
|
||||||
|
queue._size = newCount;
|
||||||
|
|
||||||
|
int half = newCount >> 1;
|
||||||
|
for (int i = half - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
int index = i;
|
||||||
|
var item = queue._heap[index];
|
||||||
|
while (index < half)
|
||||||
|
{
|
||||||
|
int child = (index << 1) + 1;
|
||||||
|
int right = child + 1;
|
||||||
|
if (right < newCount && queue._comparer.Compare(queue._heap[right], queue._heap[child]) < 0)
|
||||||
|
{
|
||||||
|
child = right;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queue._comparer.Compare(queue._heap[child], item) >= 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue._heap[index] = queue._heap[child];
|
||||||
|
index = child;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue._heap[index] = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
internal static void AddRange<TKey, TValue>(this RecyclablePriorityQueue<KeyValuePair<TKey, TValue>> queue, RecyclableSortedDictionary<TKey, TValue> items)
|
||||||
|
where TKey : notnull
|
||||||
|
{
|
||||||
|
int count = items.Count;
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int startIndex = queue._size;
|
||||||
|
int newCount = startIndex + count;
|
||||||
|
EnsureCapacity(queue, newCount);
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
queue._heap[startIndex + i] = new KeyValuePair<TKey, TValue>(items.GetKey(i), items.GetValue(i));
|
||||||
|
}
|
||||||
|
queue._size = newCount;
|
||||||
|
|
||||||
|
int half = newCount >> 1;
|
||||||
|
for (int i = half - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
int index = i;
|
||||||
|
var item = queue._heap[index];
|
||||||
|
while (index < half)
|
||||||
|
{
|
||||||
|
int child = (index << 1) + 1;
|
||||||
|
int right = child + 1;
|
||||||
|
if (right < newCount && queue._comparer.Compare(queue._heap[right], queue._heap[child]) < 0)
|
||||||
|
{
|
||||||
|
child = right;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queue._comparer.Compare(queue._heap[child], item) >= 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue._heap[index] = queue._heap[child];
|
||||||
|
index = child;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue._heap[index] = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
|
internal static void AddRange<TKey, TValue>(this RecyclablePriorityQueue<(TKey Key, TValue Value)> queue, RecyclableSortedList<TKey, TValue> items)
|
||||||
|
where TKey : notnull
|
||||||
|
{
|
||||||
|
int count = items._count;
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int startIndex = queue._size;
|
||||||
|
int newCount = startIndex + count;
|
||||||
|
EnsureCapacity(queue, newCount);
|
||||||
|
|
||||||
|
TKey[] keys = items._keys;
|
||||||
|
TValue[] values = items._values;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
queue._heap[startIndex + i] = (keys[i], values[i]);
|
||||||
|
}
|
||||||
|
queue._size = newCount;
|
||||||
|
|
||||||
|
int half = newCount >> 1;
|
||||||
|
for (int i = half - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
int index = i;
|
||||||
|
var item = queue._heap[index];
|
||||||
|
while (index < half)
|
||||||
|
{
|
||||||
|
int child = (index << 1) + 1;
|
||||||
|
int right = child + 1;
|
||||||
|
if (right < newCount && queue._comparer.Compare(queue._heap[right], queue._heap[child]) < 0)
|
||||||
|
{
|
||||||
|
child = right;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queue._comparer.Compare(queue._heap[child], item) >= 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue._heap[index] = queue._heap[child];
|
||||||
|
index = child;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue._heap[index] = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
internal static void AddRange<T>(this RecyclablePriorityQueue<T> queue, IReadOnlyList<T> items)
|
internal static void AddRange<T>(this RecyclablePriorityQueue<T> queue, IReadOnlyList<T> items)
|
||||||
{
|
{
|
||||||
@@ -918,6 +1159,9 @@ namespace Recyclable.Collections
|
|||||||
{
|
{
|
||||||
AddRange(queue, collection);
|
AddRange(queue, collection);
|
||||||
}
|
}
|
||||||
|
else if (AddRangeHelper<T>.TryAddRange(queue, items))
|
||||||
|
{
|
||||||
|
}
|
||||||
else if (items is IReadOnlyList<T> readOnlyList)
|
else if (items is IReadOnlyList<T> readOnlyList)
|
||||||
{
|
{
|
||||||
AddRange(queue, readOnlyList);
|
AddRange(queue, readOnlyList);
|
||||||
|
|||||||
@@ -223,6 +223,154 @@ namespace Recyclable.CollectionsTests
|
|||||||
_ = result.Should().Equal(_testData.Order());
|
_ = result.Should().Equal(_testData.Order());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddRangeDictionaryShouldAddItemsInSortedOrder()
|
||||||
|
{
|
||||||
|
using var source = new RecyclableDictionary<int, int>();
|
||||||
|
for (int i = 0; i < _testData.Length; i++)
|
||||||
|
{
|
||||||
|
source.Add(i, _testData[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
using var queue = new RecyclablePriorityQueue<KeyValuePair<int, int>>(comparer: Comparer<KeyValuePair<int, int>>.Create((a, b) => a.Key.CompareTo(b.Key)));
|
||||||
|
|
||||||
|
queue.AddRange(source);
|
||||||
|
|
||||||
|
var result = new List<KeyValuePair<int, int>>();
|
||||||
|
while (queue.LongCount > 0)
|
||||||
|
{
|
||||||
|
result.Add(queue.Dequeue());
|
||||||
|
}
|
||||||
|
|
||||||
|
var expected = _testData.Select((v, i) => new KeyValuePair<int, int>(i, v)).ToList();
|
||||||
|
_ = result.Should().Equal(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddRangeDictionaryShouldNotOverrideItems()
|
||||||
|
{
|
||||||
|
using var source = new RecyclableDictionary<int, int>();
|
||||||
|
for (int i = 0; i < _testData.Length; i++)
|
||||||
|
{
|
||||||
|
source.Add(i, _testData[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
using var queue = new RecyclablePriorityQueue<KeyValuePair<int, int>>(comparer: Comparer<KeyValuePair<int, int>>.Create((a, b) => a.Key.CompareTo(b.Key)));
|
||||||
|
|
||||||
|
queue.AddRange(source);
|
||||||
|
queue.AddRange(source);
|
||||||
|
|
||||||
|
var result = new List<KeyValuePair<int, int>>();
|
||||||
|
while (queue.LongCount > 0)
|
||||||
|
{
|
||||||
|
result.Add(queue.Dequeue());
|
||||||
|
}
|
||||||
|
|
||||||
|
var expected = _testData.Select((v, i) => new KeyValuePair<int, int>(i, v))
|
||||||
|
.Concat(_testData.Select((v, i) => new KeyValuePair<int, int>(i, v)))
|
||||||
|
.ToList();
|
||||||
|
_ = result.Should().Equal(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddRangeSortedListShouldAddItemsInSortedOrder()
|
||||||
|
{
|
||||||
|
using var source = new RecyclableSortedList<int, int>();
|
||||||
|
for (int i = 0; i < _testData.Length; i++)
|
||||||
|
{
|
||||||
|
source.Add(i, _testData[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
using var queue = new RecyclablePriorityQueue<(int Key, int Value)>(comparer: Comparer<(int Key, int Value)>.Create((a, b) => a.Key.CompareTo(b.Key)));
|
||||||
|
|
||||||
|
queue.AddRange(source);
|
||||||
|
|
||||||
|
var result = new List<(int Key, int Value)>();
|
||||||
|
while (queue.LongCount > 0)
|
||||||
|
{
|
||||||
|
result.Add(queue.Dequeue());
|
||||||
|
}
|
||||||
|
|
||||||
|
var expected = _testData.Select((v, i) => (i, v)).ToList();
|
||||||
|
_ = result.Should().Equal(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddRangeSortedListShouldNotOverrideItems()
|
||||||
|
{
|
||||||
|
using var source = new RecyclableSortedList<int, int>();
|
||||||
|
for (int i = 0; i < _testData.Length; i++)
|
||||||
|
{
|
||||||
|
source.Add(i, _testData[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
using var queue = new RecyclablePriorityQueue<(int Key, int Value)>(comparer: Comparer<(int Key, int Value)>.Create((a, b) => a.Key.CompareTo(b.Key)));
|
||||||
|
|
||||||
|
queue.AddRange(source);
|
||||||
|
queue.AddRange(source);
|
||||||
|
|
||||||
|
var result = new List<(int Key, int Value)>();
|
||||||
|
while (queue.LongCount > 0)
|
||||||
|
{
|
||||||
|
result.Add(queue.Dequeue());
|
||||||
|
}
|
||||||
|
|
||||||
|
var expected = _testData.Select((v, i) => (i, v))
|
||||||
|
.Concat(_testData.Select((v, i) => (i, v)))
|
||||||
|
.OrderBy(p => p.Item1)
|
||||||
|
.ToList();
|
||||||
|
_ = result.Should().Equal(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddRangeSortedDictionaryShouldAddItemsInSortedOrder()
|
||||||
|
{
|
||||||
|
using var source = new RecyclableSortedDictionary<int, int>();
|
||||||
|
for (int i = 0; i < _testData.Length; i++)
|
||||||
|
{
|
||||||
|
source.Add(i, _testData[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
using var queue = new RecyclablePriorityQueue<KeyValuePair<int, int>>(comparer: Comparer<KeyValuePair<int, int>>.Create((a, b) => a.Key.CompareTo(b.Key)));
|
||||||
|
|
||||||
|
queue.AddRange(source);
|
||||||
|
|
||||||
|
var result = new List<KeyValuePair<int, int>>();
|
||||||
|
while (queue.LongCount > 0)
|
||||||
|
{
|
||||||
|
result.Add(queue.Dequeue());
|
||||||
|
}
|
||||||
|
|
||||||
|
var expected = _testData.Select((v, i) => new KeyValuePair<int, int>(i, v)).ToList();
|
||||||
|
_ = result.Should().Equal(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddRangeSortedDictionaryShouldNotOverrideItems()
|
||||||
|
{
|
||||||
|
using var source = new RecyclableSortedDictionary<int, int>();
|
||||||
|
for (int i = 0; i < _testData.Length; i++)
|
||||||
|
{
|
||||||
|
source.Add(i, _testData[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
using var queue = new RecyclablePriorityQueue<KeyValuePair<int, int>>(comparer: Comparer<KeyValuePair<int, int>>.Create((a, b) => a.Key.CompareTo(b.Key)));
|
||||||
|
|
||||||
|
queue.AddRange(source);
|
||||||
|
queue.AddRange(source);
|
||||||
|
|
||||||
|
var result = new List<KeyValuePair<int, int>>();
|
||||||
|
while (queue.LongCount > 0)
|
||||||
|
{
|
||||||
|
result.Add(queue.Dequeue());
|
||||||
|
}
|
||||||
|
|
||||||
|
var expected = _testData.Select((v, i) => new KeyValuePair<int, int>(i, v))
|
||||||
|
.Concat(_testData.Select((v, i) => new KeyValuePair<int, int>(i, v)))
|
||||||
|
.ToList();
|
||||||
|
_ = result.Should().Equal(expected);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Reference in new issue
Block a user