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
33 changes: 33 additions & 0 deletions csharp/Platform.Collections.Methods.Tests/TreesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,38 @@ public static void SizedAndThreadedAVLBalancedTreeMultipleRandomAttachAndDetachT
var avlTree = new SizedAndThreadedAVLBalancedTree<uint>(10000);
avlTree.TestMultipleRandomCreationsAndDeletions(ref avlTree.Root, () => avlTree.Count, _n);
}

[Fact]
public static void SizedAndThreadedAVLBalancedTreeValidationTest()
{
var avlTree = new SizedAndThreadedAVLBalancedTree<uint>(100);

// Test basic attach operations with validation
for (uint i = 1; i <= 10; i++)
{
var node = avlTree.Allocate();
avlTree.Attach(ref avlTree.Root, node);

// Validate tree structure after each insertion
// The validation will run automatically due to ENABLE_TREE_AUTO_DEBUG_AND_VALIDATION
}

// Test detach operations with validation
for (uint i = 1; i <= 5; i++)
{
avlTree.Detach(ref avlTree.Root, i);

// Validation runs automatically after detach
}

// Test remaining elements
for (uint i = 6; i <= 10; i++)
{
avlTree.Detach(ref avlTree.Root, i);
}

// Tree should be empty
Assert.Equal(0U, avlTree.Count);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,12 @@ protected override void AttachCore(ref TElement root, TElement node)
}
#if USEARRAYPOOL
ArrayPool.Free(path);
#endif
#if ENABLE_TREE_AUTO_DEBUG_AND_VALIDATION
ValidateTree(root);
Debug.WriteLine("--AfterAttach--");
Debug.WriteLine(PrintNodes(root));
Debug.WriteLine("----------------");
#endif
}
}
Expand Down Expand Up @@ -870,6 +876,12 @@ protected override void DetachCore(ref TElement root, TElement node)
ClearNode(node);
#if USEARRAYPOOL
ArrayPool.Free(path);
#endif
#if ENABLE_TREE_AUTO_DEBUG_AND_VALIDATION
ValidateTree(root);
Debug.WriteLine("--AfterDetach--");
Debug.WriteLine(PrintNodes(root));
Debug.WriteLine("----------------");
#endif
}
}
Expand All @@ -894,5 +906,112 @@ protected override void ClearNode(TElement node)
SetRightIsChild(node, false);
SetBalance(node, 0);
}

#if ENABLE_TREE_AUTO_DEBUG_AND_VALIDATION
/// <summary>
/// <para>
/// Calculates the height of a tree node, following GLib's g_tree_node_height logic.
/// </para>
/// <para></para>
/// </summary>
/// <param name="node">
/// <para>The node.</para>
/// <para></para>
/// </param>
/// <returns>
/// <para>The height of the node.</para>
/// <para></para>
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected int GetNodeHeight(TElement node)
{
if (node == TElement.Zero)
return 0;

int leftHeight = 0;
int rightHeight = 0;

if (GetLeftIsChild(node))
leftHeight = GetNodeHeight(GetLeft(node));

if (GetRightIsChild(node))
rightHeight = GetNodeHeight(GetRight(node));

return Math.Max(leftHeight, rightHeight) + 1;
}

/// <summary>
/// <para>
/// Validates the AVL tree node structure, following GLib's g_tree_node_check logic.
/// </para>
/// <para></para>
/// </summary>
/// <param name="node">
/// <para>The node.</para>
/// <para></para>
/// </param>
/// <exception cref="InvalidOperationException">
/// <para>Thrown when tree structure validation fails.</para>
/// <para></para>
/// </exception>
public void ValidateNodeStructure(TElement node)
{
if (node == TElement.Zero)
return;

// Validate balance factor
int leftHeight = 0;
int rightHeight = 0;

if (GetLeftIsChild(node))
leftHeight = GetNodeHeight(GetLeft(node));

if (GetRightIsChild(node))
rightHeight = GetNodeHeight(GetRight(node));

int calculatedBalance = rightHeight - leftHeight;
int storedBalance = GetBalance(node);

if (calculatedBalance != storedBalance)
{
throw new InvalidOperationException($"Balance factor mismatch for node {node}. Expected: {calculatedBalance}, Actual: {storedBalance}");
}

// AVL property: balance factor must be -1, 0, or 1
if (Math.Abs(calculatedBalance) > 1)
{
throw new InvalidOperationException($"AVL balance violation for node {node}. Balance factor: {calculatedBalance}");
}

// Recursively validate left and right subtrees
if (GetLeftIsChild(node))
ValidateNodeStructure(GetLeft(node));

if (GetRightIsChild(node))
ValidateNodeStructure(GetRight(node));
}

/// <summary>
/// <para>
/// Validates the complete tree structure including sizes and AVL properties.
/// </para>
/// <para></para>
/// </summary>
/// <param name="root">
/// <para>The root node.</para>
/// <para></para>
/// </param>
public void ValidateTree(TElement root)
{
if (root == TElement.Zero)
return;

// Validate sizes (from base class)
ValidateSizes(root);

// Validate AVL structure
ValidateNodeStructure(root);
}
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,7 @@ public void Detach(ref TElement root, TElement node)
Debug.WriteLine("----------------");
ValidateSizes(root);
var sizeAfter = GetSize(root);
if (!(Arithmetic.sizeBefore - TElement.One != sizeAfter))
if (sizeBefore - TElement.One != sizeAfter)
{
throw new InvalidOperationException("Tree was broken after detach.");
}
Expand Down
Loading