Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using System;
using System.Numerics;
using System.Text;
using Platform.Collections.Methods.Trees;

namespace Platform.Collections.Methods.Tests
{
public class PartiallyLockFreeSizeBalancedTree<TElement> : PartiallyLockFreeSizeBalancedTreeMethods<TElement>
where TElement: IUnsignedNumber<TElement>, IComparisonOperators<TElement, TElement, bool>
{
private struct TreeElement
{
public TElement Size;
public TElement Left;
public TElement Right;
}

private readonly TreeElement[] _elements;
private TElement _allocated;

public TElement Root;

public TElement Count => GetSizeOrZero(Root);

public PartiallyLockFreeSizeBalancedTree(int capacity, int balancingThreshold = 10)
: base(balancingThreshold)
{
_elements = new TreeElement[capacity];
_allocated = TElement.One;
}

public TElement Allocate()
{
var newNode = _allocated;
if (IsEmpty(newNode))
{
_allocated = _allocated + TElement.One;
return newNode;
}
else
{
throw new InvalidOperationException("Allocated tree element is not empty.");
}
}

public void Free(TElement node)
{
while ((_allocated != TElement.One) && IsEmpty(node))
{
var lastNode = _allocated - TElement.One;
if ((lastNode == node))
{
_allocated = lastNode;
node = node - TElement.One;
}
else
{
break;
}
}
}

protected override ref TElement GetLeftReference(TElement node) => ref _elements[int.CreateTruncating(node)].Left;
protected override ref TElement GetRightReference(TElement node) => ref _elements[int.CreateTruncating(node)].Right;
protected override TElement GetLeft(TElement node) => _elements[int.CreateTruncating(node)].Left;
protected override TElement GetRight(TElement node) => _elements[int.CreateTruncating(node)].Right;
protected override TElement GetSize(TElement node) => _elements[int.CreateTruncating(node)].Size;
protected override void SetLeft(TElement node, TElement left) => _elements[int.CreateTruncating(node)].Left = left;
protected override void SetRight(TElement node, TElement right) => _elements[int.CreateTruncating(node)].Right = right;
protected override void SetSize(TElement node, TElement size) => _elements[int.CreateTruncating(node)].Size = size;

protected override bool FirstIsToTheLeftOfSecond(TElement first, TElement second) => first < second;
protected override bool FirstIsToTheRightOfSecond(TElement first, TElement second) => first > second;

protected override void PrintNodeValue(TElement node, StringBuilder sb) => sb.Append($":{node}");

private bool IsEmpty(TElement node) => GetSize(node) == TElement.Zero;

/// <summary>
/// <para>
/// Performs delayed balancing operation on the tree root.
/// </para>
/// <para></para>
/// </summary>
protected override void PerformDelayedBalancing()
{
// Apply balancing to the root when threshold is reached
if (Root != TElement.Zero)
{
ApplyDelayedBalancing(ref Root);
}
}
}
}
94 changes: 94 additions & 0 deletions csharp/Platform.Collections.Methods.Tests/TreesTests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Threading.Tasks;
using Xunit;

namespace Platform.Collections.Methods.Tests
Expand Down Expand Up @@ -47,5 +48,98 @@ public static void SizedAndThreadedAVLBalancedTreeMultipleRandomAttachAndDetachT
var avlTree = new SizedAndThreadedAVLBalancedTree<uint>(10000);
avlTree.TestMultipleRandomCreationsAndDeletions(ref avlTree.Root, () => avlTree.Count, _n);
}

[Fact]
public static void PartiallyLockFreeSizeBalancedTreeMultipleAttachAndDetachTest()
{
var lockFreeTree = new PartiallyLockFreeSizeBalancedTree<uint>(10000, balancingThreshold: 5);
lockFreeTree.TestMultipleCreationsAndDeletions(lockFreeTree.Allocate, lockFreeTree.Free, ref lockFreeTree.Root, () => lockFreeTree.Count, _n);
}

[Fact]
public static void PartiallyLockFreeSizeBalancedTreeMultipleRandomAttachAndDetachTest()
{
var lockFreeTree = new PartiallyLockFreeSizeBalancedTree<uint>(10000, balancingThreshold: 10);
lockFreeTree.TestMultipleRandomCreationsAndDeletions(ref lockFreeTree.Root, () => lockFreeTree.Count, _n);
}

[Fact]
public static void PartiallyLockFreeSizeBalancedTreeDelayedBalancingTest()
{
var lockFreeTree = new PartiallyLockFreeSizeBalancedTree<uint>(1000, balancingThreshold: 5);

// Insert elements without triggering balancing
for (uint i = 1; i <= 4; i++)
{
var node = lockFreeTree.Allocate();
lockFreeTree.Attach(ref lockFreeTree.Root, node);
}

// At this point, balancing should not have been triggered
Assert.Equal(4, lockFreeTree.LockFreeAttachCount);
Assert.Equal(5, lockFreeTree.BalancingThreshold);

// Insert one more element to trigger balancing
var triggerNode = lockFreeTree.Allocate();
lockFreeTree.Attach(ref lockFreeTree.Root, triggerNode);

// After triggering, the counter should reset
Assert.Equal(0, lockFreeTree.LockFreeAttachCount);
Assert.Equal(5u, lockFreeTree.Count);
}

[Fact]
public static void PartiallyLockFreeSizeBalancedTreeForceBalancingTest()
{
var lockFreeTree = new PartiallyLockFreeSizeBalancedTree<uint>(1000, balancingThreshold: 10);

// Insert a few elements
for (uint i = 1; i <= 3; i++)
{
var node = lockFreeTree.Allocate();
lockFreeTree.Attach(ref lockFreeTree.Root, node);
}

Assert.Equal(3, lockFreeTree.LockFreeAttachCount);

// Force balancing manually
lockFreeTree.ForceBalancing();

// Counter should be reset after force balancing
Assert.Equal(0, lockFreeTree.LockFreeAttachCount);
Assert.Equal(3u, lockFreeTree.Count);
}

[Fact]
public static async Task PartiallyLockFreeSizeBalancedTreeConcurrentInsertionTest()
{
var lockFreeTree = new PartiallyLockFreeSizeBalancedTree<uint>(10000, balancingThreshold: 50);
const int numberOfTasks = 10;
const int insertsPerTask = 20;

// Create tasks that will insert elements concurrently
var tasks = new Task[numberOfTasks];
for (int i = 0; i < numberOfTasks; i++)
{
int taskId = i;
tasks[i] = Task.Run(() =>
{
for (int j = 0; j < insertsPerTask; j++)
{
var node = lockFreeTree.Allocate();
lockFreeTree.Attach(ref lockFreeTree.Root, node);
}
});
}

// Wait for all tasks to complete
await Task.WhenAll(tasks);

// Verify final count
Assert.Equal((uint)(numberOfTasks * insertsPerTask), lockFreeTree.Count);

// Clean up resources
lockFreeTree.Dispose();
}
}
}
Loading
Loading