Skip to content

Commit

Permalink
Add GetNearest()
Browse files Browse the repository at this point in the history
  • Loading branch information
CodeDevAM committed Jun 14, 2024
1 parent d940a7d commit 7026b1e
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 15 deletions.
6 changes: 3 additions & 3 deletions BTree.Benchmark/BTree.Benchmark.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
<Authors>DevAM</Authors>
<Company>DevAM</Company>
<Product>BTree</Product>
<Version>1.1.0</Version>
<Version>1.2.0</Version>
<Copyright>Copyright (c) 2024 DevAM. All Rights Reserved.</Copyright>
<AssemblyVersion>1.1.0</AssemblyVersion>
<FileVersion>1.1.0</FileVersion>
<AssemblyVersion>1.2.0</AssemblyVersion>
<FileVersion>1.2.0</FileVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
6 changes: 3 additions & 3 deletions BTree.Test/BTree.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
<Authors>DevAM</Authors>
<Company>DevAM</Company>
<Product>BTree</Product>
<Version>1.1.0</Version>
<Version>1.2.0</Version>
<Copyright>Copyright (c) 2024 DevAM. All Rights Reserved.</Copyright>
<AssemblyVersion>1.1.0</AssemblyVersion>
<FileVersion>1.1.0</FileVersion>
<AssemblyVersion>1.2.0</AssemblyVersion>
<FileVersion>1.2.0</FileVersion>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
Expand Down
60 changes: 60 additions & 0 deletions BTree.Test/BTree/GetTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,66 @@ public void Get(ushort degree, int count, int minItem, int maxItem)
CollectionAssert.AreEqual(expectedRangeSequence, actualRangeList);
}

[TestCaseSource(typeof(GetTest), nameof(TestCases))]
public void GetNearest(ushort degree, int count, int minItem, int maxItem)
{
Ref<int>[] orderedItems = Enumerable.Range(1, count).Select(x => new Ref<int>(x * 10)).ToArray();
Ref<int>[] items = orderedItems.ToArray();
items.Shuffle();
BTree<Ref<int>> tree = new(degree);

foreach (Ref<int> item in items)
{
tree.InsertOrUpdate(item);
}

foreach (Ref<int> item in items)
{
BTree<Ref<int>>.NearestItems nearestItems = tree.GetNearest(item);
Assert.That(nearestItems.HasMinItem, Is.EqualTo(true));
Assert.That(nearestItems.MinItem, Is.EqualTo(item));
Assert.That(nearestItems.HasMaxItem, Is.EqualTo(false));
}

Ref<int>[] keys = Enumerable.Range(0, 10 * (count + 1) + 1).Select(x => new Ref<int>(x)).ToArray();
keys.Shuffle();

foreach (Ref<int> key in keys)
{
BTree<Ref<int>>.NearestItems nearestItems = tree.GetNearest(key);

if (key.CompareTo(10) < 0)
{
Assert.That(nearestItems.HasMinItem, Is.EqualTo(false));
Assert.That(nearestItems.HasMaxItem, Is.EqualTo(true));
Assert.That(nearestItems.MaxItem, Is.EqualTo(new Ref<int>(10)));
}
else if (key.CompareTo(count * 10) > 0)
{
Assert.That(nearestItems.HasMinItem, Is.EqualTo(true));
Assert.That(nearestItems.MinItem, Is.EqualTo(new Ref<int>(count * 10)));
Assert.That(nearestItems.HasMaxItem, Is.EqualTo(false));
}
else if (key.Value % 10 == 0)
{
Assert.That(nearestItems.HasMinItem, Is.EqualTo(true));
Assert.That(nearestItems.MinItem, Is.EqualTo(key));
Assert.That(nearestItems.HasMaxItem, Is.EqualTo(false));
}
else
{
Ref<int> expectedMinItem = (key / 10) * 10;
Ref<int> expectedMaxItem = ((key / 10) + 1) * 10;

Assert.That(nearestItems.HasMinItem, Is.EqualTo(true));
Assert.That(nearestItems.MinItem, Is.EqualTo(expectedMinItem));
Assert.That(nearestItems.HasMaxItem, Is.EqualTo(true));
Assert.That(nearestItems.MaxItem, Is.EqualTo(expectedMaxItem));
}

}
}

public static IEnumerable TestCases
{
get
Expand Down
116 changes: 116 additions & 0 deletions BTree/BTree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,103 @@ internal bool GetMax(out T maxItem)
}
}

internal NearestItems GetNearest<TKey>(TKey key) where TKey : IComparable<T>
{
if (Count <= 0)
{
return new(false, default, false, default);
}

int index = FindNextGreaterOrEqual(key);

// check this node first
if (index < Count)
{
T currentItem = Items[index];
if (key.CompareTo(currentItem) == 0)
{
return new(true, currentItem, false, default);
}
}

if (IsLeaf)
{
if (index < Count)
{
T currentItem = Items[index];
if (key.CompareTo(currentItem) < 0)
{
if (index > 0)
{
return new(true, Items[index - 1], true, currentItem);
}
else
{
return new(false, default, true, currentItem);
}
}
else // key > currentItem
{
if (index < Count - 1)
{
return new(true, currentItem, true, Items[index + 1]);
}
else
{
return new(true, currentItem, false, default);
}
}
}
else
{
return new(true, Items[Count - 1], false, default);
}
}
else
{
Node child = Children[index];

NearestItems nearestItems = child.GetNearest(key);

if (nearestItems.HasMinItem && nearestItems.HasMaxItem)
{
return nearestItems;
}
else if (nearestItems.HasMinItem)
{
if (key.CompareTo(nearestItems.MinItem) == 0)
{
return nearestItems;
}
else
{
if (index < Count)
{
return new(true, nearestItems.MinItem, true, Items[index]);
}
else
{
return nearestItems;
}
}
}
else if (nearestItems.HasMaxItem)
{
if (index > 0)
{
return new(true, Items[index - 1], true, nearestItems.MaxItem);
}
else
{
return nearestItems;
}
}

}

return new(false, default, false, default);
}

internal void DoForEach<TKey>(Action<T> action, TKey minKey, TKey maxKey, bool maxInclusive) where TKey : IComparable<T>
{
int index = FindNextGreaterOrEqual(minKey);
Expand Down Expand Up @@ -904,6 +1001,8 @@ internal string PrettyString
#endif
}

public record struct NearestItems(bool HasMinItem, T MinItem, bool HasMaxItem, T MaxItem);

/// <summary>
/// The default Degree is chosen to be a good compromise of performance and memory consumption.
/// </summary>
Expand Down Expand Up @@ -1133,6 +1232,23 @@ public bool GetMax(out T maxItem)
return _Root.GetMax(out maxItem);
}

/// <summary>
/// Gets the next greater item as <see cref="NearestItems.MaxItem"/> and the next lower item as <see cref="NearestItems.MinItem"/> if existant. If the key equals an item only this item is returned an <see cref="NearestItems.MinItem"/>.
/// The <paramref name="key"/> can be a reduced version of an item as long as it implements <see cref="IComparable{T}"/>.
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <param name="key">The <paramref name="key"/> can be a reduced version of an item as long as it implements <see cref="IComparable{T}"/></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
public NearestItems GetNearest<TKey>(TKey key) where TKey : IComparable<T>
{
if (key is null)
{
throw new ArgumentNullException(nameof(key));
}

return _Root.GetNearest(key);
}

/// <summary>
/// Performs an action for every single item within an inclusive lower limit (<paramref name="minKey"/>) and an upper limit (<paramref name="maxKey"/>).
Expand Down
6 changes: 3 additions & 3 deletions BTree/BTree.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@
<Authors>DevAM</Authors>
<Company>DevAM</Company>
<Product>BTree</Product>
<Version>1.1.0</Version>
<Version>1.2.0</Version>
<Description>Simple and high performant BTree for C# and .NET.</Description>
<Copyright>Copyright (c) 2024 DevAM. All Rights Reserved.</Copyright>
<PackageIcon>icon.jpeg</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryType>git</RepositoryType>
<PackageTags>BTree</PackageTags>
<AssemblyVersion>1.1.0</AssemblyVersion>
<FileVersion>1.1.0</FileVersion>
<AssemblyVersion>1.2.0</AssemblyVersion>
<FileVersion>1.2.0</FileVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<AssemblyName>BTree</AssemblyName>
Expand Down
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@

BTree is a simple and high performant BTree for C# and .NET.

<div align="center">
<img
src="./icon.jpeg"
width="256"
height="256">
</div>

<img
src="./icon.jpeg"
width="256"
height="256"/>

## Usage
Items of type `T` that shall be stored in the `BTree<T>` must implement the interface `IComparable<T>`. In this case the usage is straight forward.
Expand Down

0 comments on commit 7026b1e

Please sign in to comment.