namespace BTree.UnitTest
{
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class BTreeTest
{
private const int Degree = 2;
private readonly int[] testKeyData = new int[] { 10, 20, 30, 50 };
private readonly int[] testPointerData = new int[] { 50, 60, 40, 20 };
[TestMethod]
public void CreateBTree()
{
var btree = new BTree<int, int>(Degree);
Node<int, int> root = btree.Root;
Assert.IsNotNull(root);
Assert.IsNotNull(root.Entries);
Assert.IsNotNull(root.Children);
Assert.AreEqual(0, root.Entries.Count);
Assert.AreEqual(0, root.Children.Count);
}
[TestMethod]
public void InsertOneNode()
{
var btree = new BTree<int, int>(Degree);
this.InsertTestDataAndValidateTree(btree, 0);
Assert.AreEqual(1, btree.Height);
}
[TestMethod]
public void InsertMultipleNodesToSplit()
{
var btree = new BTree<int, int>(Degree);
for (int i = 0; i < this.testKeyData.Length; i++)
{
this.InsertTestDataAndValidateTree(btree, i);
}
Assert.AreEqual(2, btree.Height);
}
[TestMethod]
public void DeleteNodes()
{
var btree = new BTree<int, int>(Degree);
for (int i = 0; i < this.testKeyData.Length; i++)
{
this.InsertTestData(btree, i);
}
for (int i = 0; i < this.testKeyData.Length; i++)
{
btree.Delete(this.testKeyData[i]);
TreeValidation.ValidateTree(btree.Root, Degree, this.testKeyData.Skip(i + 1).ToArray());
}
Assert.AreEqual(1, btree.Height);
}
[TestMethod]
public void DeleteNonExistingNode()
{
var btree = new BTree<int, int>(Degree);
for (int i = 0; i < this.testKeyData.Length; i++)
{
this.InsertTestData(btree, i);
}
btree.Delete(99999);
TreeValidation.ValidateTree(btree.Root, Degree, this.testKeyData.ToArray());
}
[TestMethod]
public void SearchNodes()
{
var btree = new BTree<int, int>(Degree);
for (int i = 0; i < this.testKeyData.Length; i++)
{
this.InsertTestData(btree, i);
this.SearchTestData(btree, i);
}
}
[TestMethod]
public void SearchNonExistingNode()
{
var btree = new BTree<int, int>(Degree);
// search an empty tree
Entry<int, int> nonExisting = btree.Search(9999);
Assert.IsNull(nonExisting);
for (int i = 0; i < this.testKeyData.Length; i++)
{
this.InsertTestData(btree, i);
this.SearchTestData(btree, i);
}
// search a populated tree
nonExisting = btree.Search(9999);
Assert.IsNull(nonExisting);
}
#region Private Helper Methods
private void InsertTestData(BTree<int, int> btree, int testDataIndex)
{
btree.Insert(this.testKeyData[testDataIndex], this.testPointerData[testDataIndex]);
}
private void InsertTestDataAndValidateTree(BTree<int, int> btree, int testDataIndex)
{
btree.Insert(this.testKeyData[testDataIndex], this.testPointerData[testDataIndex]);
TreeValidation.ValidateTree(btree.Root, Degree, this.testKeyData.Take(testDataIndex + 1).ToArray());
}
private void SearchTestData(BTree<int, int> btree, int testKeyDataIndex)
{
for (int i = 0; i <= testKeyDataIndex; i++)
{
Entry<int, int> entry = btree.Search(this.testKeyData[i]);
Assert.IsNotNull(this.testKeyData[i]);
Assert.AreEqual(this.testKeyData[i], entry.Key);
Assert.AreEqual(this.testPointerData[i], entry.Pointer);
}
}
#endregion
}
}
namespace BTree.UnitTest
{
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
public static class TreeValidation
{
public static void ValidateTree(Node<int, int> tree, int degree, params int[] expectedKeys)
{
var foundKeys = new Dictionary<int, List<Entry<int, int>>>();
ValidateSubtree(tree, tree, degree, int.MinValue, int.MaxValue, foundKeys);
Assert.AreEqual(0, expectedKeys.Except(foundKeys.Keys).Count());
foreach (var keyValuePair in foundKeys)
{
Assert.AreEqual(1, keyValuePair.Value.Count);
}
}
private static void UpdateFoundKeys(Dictionary<int, List<Entry<int, int>>> foundKeys, Entry<int, int> entry)
{
List<Entry<int, int>> foundEntries;
if (!foundKeys.TryGetValue(entry.Key, out foundEntries))
{
foundEntries = new List<Entry<int, int>>();
foundKeys.Add(entry.Key, foundEntries);
}
foundEntries.Add(entry);
}
private static void ValidateSubtree(Node<int, int> root, Node<int, int> node, int degree, int nodeMin, int nodeMax, Dictionary<int, List<Entry<int, int>>> foundKeys)
{
if (root != node)
{
Assert.IsTrue(node.Entries.Count >= degree - 1);
Assert.IsTrue(node.Entries.Count <= (2 * degree) - 1);
}
for (int i = 0; i <= node.Entries.Count; i++)
{
int subtreeMin = nodeMin;
int subtreeMax = nodeMax;
if (i < node.Entries.Count)
{
var entry = node.Entries[i];
UpdateFoundKeys(foundKeys, entry);
Assert.IsTrue(entry.Key >= nodeMin && entry.Key <= nodeMax);
subtreeMax = entry.Key;
}
if (i > 0)
{
subtreeMin = node.Entries[i - 1].Key;
}
if (!node.IsLeaf)
{
Assert.IsTrue(node.Children.Count >= degree);
ValidateSubtree(root, node.Children[i], degree, subtreeMin, subtreeMax, foundKeys);
}
}
}
}
}
namespace BTree
{
using System.Collections.Generic;
public class Node<TK, TP>
{
private int degree;
public Node(int degree)
{
this.degree = degree;
this.Children = new List<Node<TK, TP>>(degree);
this.Entries = new List<Entry<TK, TP>>(degree);
}
public List<Node<TK, TP>> Children { get; set; }
public List<Entry<TK, TP>> Entries { get; set; }
public bool IsLeaf
{
get
{
return this.Children.Count == 0;
}
}
public bool HasReachedMaxEntries
{
get
{
return this.Entries.Count == (2 * this.degree) - 1;
}
}
public bool HasReachedMinEntries
{
get
{
return this.Entries.Count == this.degree - 1;
}
}
}
}
namespace BTree
{
using System;
public class Entry<TK, TP> : IEquatable<Entry<TK, TP>>
{
public TK Key { get; set; }
public TP Pointer { get; set; }
public bool Equals(Entry<TK, TP> other)
{
return this.Key.Equals(other.Key) && this.Pointer.Equals(other.Pointer);
}
}
}
{
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class BTreeTest
{
private const int Degree = 2;
private readonly int[] testKeyData = new int[] { 10, 20, 30, 50 };
private readonly int[] testPointerData = new int[] { 50, 60, 40, 20 };
[TestMethod]
public void CreateBTree()
{
var btree = new BTree<int, int>(Degree);
Node<int, int> root = btree.Root;
Assert.IsNotNull(root);
Assert.IsNotNull(root.Entries);
Assert.IsNotNull(root.Children);
Assert.AreEqual(0, root.Entries.Count);
Assert.AreEqual(0, root.Children.Count);
}
[TestMethod]
public void InsertOneNode()
{
var btree = new BTree<int, int>(Degree);
this.InsertTestDataAndValidateTree(btree, 0);
Assert.AreEqual(1, btree.Height);
}
[TestMethod]
public void InsertMultipleNodesToSplit()
{
var btree = new BTree<int, int>(Degree);
for (int i = 0; i < this.testKeyData.Length; i++)
{
this.InsertTestDataAndValidateTree(btree, i);
}
Assert.AreEqual(2, btree.Height);
}
[TestMethod]
public void DeleteNodes()
{
var btree = new BTree<int, int>(Degree);
for (int i = 0; i < this.testKeyData.Length; i++)
{
this.InsertTestData(btree, i);
}
for (int i = 0; i < this.testKeyData.Length; i++)
{
btree.Delete(this.testKeyData[i]);
TreeValidation.ValidateTree(btree.Root, Degree, this.testKeyData.Skip(i + 1).ToArray());
}
Assert.AreEqual(1, btree.Height);
}
[TestMethod]
public void DeleteNonExistingNode()
{
var btree = new BTree<int, int>(Degree);
for (int i = 0; i < this.testKeyData.Length; i++)
{
this.InsertTestData(btree, i);
}
btree.Delete(99999);
TreeValidation.ValidateTree(btree.Root, Degree, this.testKeyData.ToArray());
}
[TestMethod]
public void SearchNodes()
{
var btree = new BTree<int, int>(Degree);
for (int i = 0; i < this.testKeyData.Length; i++)
{
this.InsertTestData(btree, i);
this.SearchTestData(btree, i);
}
}
[TestMethod]
public void SearchNonExistingNode()
{
var btree = new BTree<int, int>(Degree);
// search an empty tree
Entry<int, int> nonExisting = btree.Search(9999);
Assert.IsNull(nonExisting);
for (int i = 0; i < this.testKeyData.Length; i++)
{
this.InsertTestData(btree, i);
this.SearchTestData(btree, i);
}
// search a populated tree
nonExisting = btree.Search(9999);
Assert.IsNull(nonExisting);
}
#region Private Helper Methods
private void InsertTestData(BTree<int, int> btree, int testDataIndex)
{
btree.Insert(this.testKeyData[testDataIndex], this.testPointerData[testDataIndex]);
}
private void InsertTestDataAndValidateTree(BTree<int, int> btree, int testDataIndex)
{
btree.Insert(this.testKeyData[testDataIndex], this.testPointerData[testDataIndex]);
TreeValidation.ValidateTree(btree.Root, Degree, this.testKeyData.Take(testDataIndex + 1).ToArray());
}
private void SearchTestData(BTree<int, int> btree, int testKeyDataIndex)
{
for (int i = 0; i <= testKeyDataIndex; i++)
{
Entry<int, int> entry = btree.Search(this.testKeyData[i]);
Assert.IsNotNull(this.testKeyData[i]);
Assert.AreEqual(this.testKeyData[i], entry.Key);
Assert.AreEqual(this.testPointerData[i], entry.Pointer);
}
}
#endregion
}
}
namespace BTree.UnitTest
{
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
public static class TreeValidation
{
public static void ValidateTree(Node<int, int> tree, int degree, params int[] expectedKeys)
{
var foundKeys = new Dictionary<int, List<Entry<int, int>>>();
ValidateSubtree(tree, tree, degree, int.MinValue, int.MaxValue, foundKeys);
Assert.AreEqual(0, expectedKeys.Except(foundKeys.Keys).Count());
foreach (var keyValuePair in foundKeys)
{
Assert.AreEqual(1, keyValuePair.Value.Count);
}
}
private static void UpdateFoundKeys(Dictionary<int, List<Entry<int, int>>> foundKeys, Entry<int, int> entry)
{
List<Entry<int, int>> foundEntries;
if (!foundKeys.TryGetValue(entry.Key, out foundEntries))
{
foundEntries = new List<Entry<int, int>>();
foundKeys.Add(entry.Key, foundEntries);
}
foundEntries.Add(entry);
}
private static void ValidateSubtree(Node<int, int> root, Node<int, int> node, int degree, int nodeMin, int nodeMax, Dictionary<int, List<Entry<int, int>>> foundKeys)
{
if (root != node)
{
Assert.IsTrue(node.Entries.Count >= degree - 1);
Assert.IsTrue(node.Entries.Count <= (2 * degree) - 1);
}
for (int i = 0; i <= node.Entries.Count; i++)
{
int subtreeMin = nodeMin;
int subtreeMax = nodeMax;
if (i < node.Entries.Count)
{
var entry = node.Entries[i];
UpdateFoundKeys(foundKeys, entry);
Assert.IsTrue(entry.Key >= nodeMin && entry.Key <= nodeMax);
subtreeMax = entry.Key;
}
if (i > 0)
{
subtreeMin = node.Entries[i - 1].Key;
}
if (!node.IsLeaf)
{
Assert.IsTrue(node.Children.Count >= degree);
ValidateSubtree(root, node.Children[i], degree, subtreeMin, subtreeMax, foundKeys);
}
}
}
}
}
namespace BTree
{
using System.Collections.Generic;
public class Node<TK, TP>
{
private int degree;
public Node(int degree)
{
this.degree = degree;
this.Children = new List<Node<TK, TP>>(degree);
this.Entries = new List<Entry<TK, TP>>(degree);
}
public List<Node<TK, TP>> Children { get; set; }
public List<Entry<TK, TP>> Entries { get; set; }
public bool IsLeaf
{
get
{
return this.Children.Count == 0;
}
}
public bool HasReachedMaxEntries
{
get
{
return this.Entries.Count == (2 * this.degree) - 1;
}
}
public bool HasReachedMinEntries
{
get
{
return this.Entries.Count == this.degree - 1;
}
}
}
}
namespace BTree
{
using System;
public class Entry<TK, TP> : IEquatable<Entry<TK, TP>>
{
public TK Key { get; set; }
public TP Pointer { get; set; }
public bool Equals(Entry<TK, TP> other)
{
return this.Key.Equals(other.Key) && this.Pointer.Equals(other.Pointer);
}
}
}
No comments:
Post a Comment