2023-12-04 13:17:13 +00:00
using System ;
2021-09-19 12:55:07 +00:00
using System.Collections.Generic ;
using System.Linq ;
namespace Ryujinx.Common.Collections
{
/// <summary>
/// An Augmented Interval Tree based off of the "TreeDictionary"'s Red-Black Tree. Allows fast overlap checking of ranges.
/// </summary>
2023-06-28 16:41:38 +00:00
/// <typeparam name="TKey">Key</typeparam>
/// <typeparam name="TValue">Value</typeparam>
public class IntervalTree < TKey , TValue > : IntrusiveRedBlackTreeImpl < IntervalTreeNode < TKey , TValue > > where TKey : IComparable < TKey >
2021-09-19 12:55:07 +00:00
{
private const int ArrayGrowthSize = 32 ;
#region Public Methods
/// <summary>
/// Gets the values of the interval whose key is <paramref name="key"/>.
/// </summary>
/// <param name="key">Key of the node value to get</param>
/// <param name="overlaps">Overlaps array to place results in</param>
/// <returns>Number of values found</returns>
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
2023-06-28 16:41:38 +00:00
public int Get ( TKey key , ref TValue [ ] overlaps )
2021-09-19 12:55:07 +00:00
{
2022-12-27 19:27:11 +00:00
ArgumentNullException . ThrowIfNull ( key ) ;
2021-09-19 12:55:07 +00:00
2023-06-28 16:41:38 +00:00
IntervalTreeNode < TKey , TValue > node = GetNode ( key ) ;
2021-09-19 12:55:07 +00:00
if ( node = = null )
{
return 0 ;
}
if ( node . Values . Count > overlaps . Length )
{
Array . Resize ( ref overlaps , node . Values . Count ) ;
}
int overlapsCount = 0 ;
2023-06-28 16:41:38 +00:00
foreach ( RangeNode < TKey , TValue > value in node . Values )
2021-09-19 12:55:07 +00:00
{
overlaps [ overlapsCount + + ] = value . Value ;
}
return overlapsCount ;
}
/// <summary>
/// Returns the values of the intervals whose start and end keys overlap the given range.
/// </summary>
/// <param name="start">Start of the range</param>
/// <param name="end">End of the range</param>
/// <param name="overlaps">Overlaps array to place results in</param>
/// <param name="overlapCount">Index to start writing results into the array. Defaults to 0</param>
/// <returns>Number of values found</returns>
/// <exception cref="ArgumentNullException"><paramref name="start"/> or <paramref name="end"/> is null</exception>
2023-06-28 16:41:38 +00:00
public int Get ( TKey start , TKey end , ref TValue [ ] overlaps , int overlapCount = 0 )
2021-09-19 12:55:07 +00:00
{
2022-12-27 19:27:11 +00:00
ArgumentNullException . ThrowIfNull ( start ) ;
ArgumentNullException . ThrowIfNull ( end ) ;
2021-09-19 12:55:07 +00:00
2022-08-26 18:21:48 +00:00
GetValues ( Root , start , end , ref overlaps , ref overlapCount ) ;
2021-09-19 12:55:07 +00:00
return overlapCount ;
}
/// <summary>
/// Adds a new interval into the tree whose start is <paramref name="start"/>, end is <paramref name="end"/> and value is <paramref name="value"/>.
/// </summary>
/// <param name="start">Start of the range to add</param>
/// <param name="end">End of the range to insert</param>
/// <param name="value">Value to add</param>
/// <exception cref="ArgumentNullException"><paramref name="start"/>, <paramref name="end"/> or <paramref name="value"/> are null</exception>
2023-06-28 16:41:38 +00:00
public void Add ( TKey start , TKey end , TValue value )
2021-09-19 12:55:07 +00:00
{
2022-12-27 19:27:11 +00:00
ArgumentNullException . ThrowIfNull ( start ) ;
ArgumentNullException . ThrowIfNull ( end ) ;
ArgumentNullException . ThrowIfNull ( value ) ;
2021-09-19 12:55:07 +00:00
Insert ( start , end , value ) ;
}
/// <summary>
/// Removes the given <paramref name="value"/> from the tree, searching for it with <paramref name="key"/>.
/// </summary>
/// <param name="key">Key of the node to remove</param>
/// <param name="value">Value to remove</param>
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
/// <returns>Number of deleted values</returns>
2023-06-28 16:41:38 +00:00
public int Remove ( TKey key , TValue value )
2021-09-19 12:55:07 +00:00
{
2022-12-27 19:27:11 +00:00
ArgumentNullException . ThrowIfNull ( key ) ;
2021-09-19 12:55:07 +00:00
int removed = Delete ( key , value ) ;
2022-08-26 18:21:48 +00:00
Count - = removed ;
2021-09-19 12:55:07 +00:00
return removed ;
}
/// <summary>
/// Adds all the nodes in the dictionary into <paramref name="list"/>.
/// </summary>
/// <returns>A list of all RangeNodes sorted by Key Order</returns>
2023-06-28 16:41:38 +00:00
public List < RangeNode < TKey , TValue > > AsList ( )
2021-09-19 12:55:07 +00:00
{
2023-06-28 16:41:38 +00:00
List < RangeNode < TKey , TValue > > list = new ( ) ;
2021-09-19 12:55:07 +00:00
2022-08-26 18:21:48 +00:00
AddToList ( Root , list ) ;
2021-09-19 12:55:07 +00:00
return list ;
}
#endregion
#region Private Methods ( BST )
/// <summary>
/// Adds all RangeNodes that are children of or contained within <paramref name="node"/> into <paramref name="list"/>, in Key Order.
/// </summary>
/// <param name="node">The node to search for RangeNodes within</param>
/// <param name="list">The list to add RangeNodes to</param>
2023-06-28 16:41:38 +00:00
private void AddToList ( IntervalTreeNode < TKey , TValue > node , List < RangeNode < TKey , TValue > > list )
2021-09-19 12:55:07 +00:00
{
if ( node = = null )
{
return ;
}
AddToList ( node . Left , list ) ;
list . AddRange ( node . Values ) ;
AddToList ( node . Right , list ) ;
}
/// <summary>
/// Retrieve the node reference whose key is <paramref name="key"/>, or null if no such node exists.
/// </summary>
/// <param name="key">Key of the node to get</param>
/// <returns>Node reference in the tree</returns>
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
2023-06-28 16:41:38 +00:00
private IntervalTreeNode < TKey , TValue > GetNode ( TKey key )
2021-09-19 12:55:07 +00:00
{
2022-12-27 19:27:11 +00:00
ArgumentNullException . ThrowIfNull ( key ) ;
2021-09-19 12:55:07 +00:00
2023-06-28 16:41:38 +00:00
IntervalTreeNode < TKey , TValue > node = Root ;
2021-09-19 12:55:07 +00:00
while ( node ! = null )
{
int cmp = key . CompareTo ( node . Start ) ;
if ( cmp < 0 )
{
node = node . Left ;
}
else if ( cmp > 0 )
{
node = node . Right ;
}
else
{
return node ;
}
}
return null ;
}
/// <summary>
/// Retrieve all values that overlap the given start and end keys.
/// </summary>
/// <param name="start">Start of the range</param>
/// <param name="end">End of the range</param>
/// <param name="overlaps">Overlaps array to place results in</param>
/// <param name="overlapCount">Overlaps count to update</param>
2023-06-28 16:41:38 +00:00
private void GetValues ( IntervalTreeNode < TKey , TValue > node , TKey start , TKey end , ref TValue [ ] overlaps , ref int overlapCount )
2022-05-02 23:30:02 +00:00
{
2021-09-19 12:55:07 +00:00
if ( node = = null | | start . CompareTo ( node . Max ) > = 0 )
{
return ;
}
GetValues ( node . Left , start , end , ref overlaps , ref overlapCount ) ;
bool endsOnRight = end . CompareTo ( node . Start ) > 0 ;
if ( endsOnRight )
{
if ( start . CompareTo ( node . End ) < 0 )
{
// Contains this node. Add overlaps to list.
2023-06-28 16:41:38 +00:00
foreach ( RangeNode < TKey , TValue > overlap in node . Values )
2021-09-19 12:55:07 +00:00
{
if ( start . CompareTo ( overlap . End ) < 0 )
{
2023-09-26 15:37:10 +00:00
if ( overlaps . Length < = overlapCount )
2021-09-19 12:55:07 +00:00
{
Array . Resize ( ref overlaps , overlapCount + ArrayGrowthSize ) ;
}
overlaps [ overlapCount + + ] = overlap . Value ;
}
}
}
GetValues ( node . Right , start , end , ref overlaps , ref overlapCount ) ;
}
}
/// <summary>
/// Inserts a new node into the tree with a given <paramref name="start"/>, <paramref name="end"/> and <paramref name="value"/>.
/// </summary>
/// <param name="start">Start of the range to insert</param>
/// <param name="end">End of the range to insert</param>
/// <param name="value">Value to insert</param>
2023-06-28 16:41:38 +00:00
private void Insert ( TKey start , TKey end , TValue value )
2021-09-19 12:55:07 +00:00
{
2023-06-28 16:41:38 +00:00
IntervalTreeNode < TKey , TValue > newNode = BSTInsert ( start , end , value ) ;
2021-09-19 12:55:07 +00:00
RestoreBalanceAfterInsertion ( newNode ) ;
}
/// <summary>
/// Propagate an increase in max value starting at the given node, heading up the tree.
/// This should only be called if the max increases - not for rebalancing or removals.
/// </summary>
/// <param name="node">The node to start propagating from</param>
2023-06-28 16:41:38 +00:00
private static void PropagateIncrease ( IntervalTreeNode < TKey , TValue > node )
2021-09-19 12:55:07 +00:00
{
2023-06-28 16:41:38 +00:00
TKey max = node . Max ;
IntervalTreeNode < TKey , TValue > ptr = node ;
2021-09-19 12:55:07 +00:00
while ( ( ptr = ptr . Parent ) ! = null )
{
if ( max . CompareTo ( ptr . Max ) > 0 )
{
ptr . Max = max ;
}
else
{
break ;
}
}
}
/// <summary>
/// Propagate recalculating max value starting at the given node, heading up the tree.
/// This fully recalculates the max value from all children when there is potential for it to decrease.
/// </summary>
/// <param name="node">The node to start propagating from</param>
2023-06-28 16:41:38 +00:00
private static void PropagateFull ( IntervalTreeNode < TKey , TValue > node )
2021-09-19 12:55:07 +00:00
{
2023-06-28 16:41:38 +00:00
IntervalTreeNode < TKey , TValue > ptr = node ;
2021-09-19 12:55:07 +00:00
do
{
2023-06-28 16:41:38 +00:00
TKey max = ptr . End ;
2021-09-19 12:55:07 +00:00
if ( ptr . Left ! = null & & ptr . Left . Max . CompareTo ( max ) > 0 )
{
max = ptr . Left . Max ;
}
if ( ptr . Right ! = null & & ptr . Right . Max . CompareTo ( max ) > 0 )
{
max = ptr . Right . Max ;
}
ptr . Max = max ;
} while ( ( ptr = ptr . Parent ) ! = null ) ;
}
/// <summary>
/// Insertion Mechanism for the interval tree. Similar to a BST insert, with the start of the range as the key.
/// Iterates the tree starting from the root and inserts a new node where all children in the left subtree are less than <paramref name="start"/>, and all children in the right subtree are greater than <paramref name="start"/>.
/// Each node can contain multiple values, and has an end address which is the maximum of all those values.
/// Post insertion, the "max" value of the node and all parents are updated.
/// </summary>
/// <param name="start">Start of the range to insert</param>
/// <param name="end">End of the range to insert</param>
/// <param name="value">Value to insert</param>
/// <returns>The inserted Node</returns>
2023-06-28 16:41:38 +00:00
private IntervalTreeNode < TKey , TValue > BSTInsert ( TKey start , TKey end , TValue value )
2021-09-19 12:55:07 +00:00
{
2023-06-28 16:41:38 +00:00
IntervalTreeNode < TKey , TValue > parent = null ;
IntervalTreeNode < TKey , TValue > node = Root ;
2021-09-19 12:55:07 +00:00
while ( node ! = null )
{
parent = node ;
int cmp = start . CompareTo ( node . Start ) ;
if ( cmp < 0 )
{
node = node . Left ;
}
else if ( cmp > 0 )
{
node = node . Right ;
}
else
{
2023-06-28 16:41:38 +00:00
node . Values . Add ( new RangeNode < TKey , TValue > ( start , end , value ) ) ;
2021-09-19 12:55:07 +00:00
if ( end . CompareTo ( node . End ) > 0 )
{
node . End = end ;
if ( end . CompareTo ( node . Max ) > 0 )
{
node . Max = end ;
PropagateIncrease ( node ) ;
}
}
2022-08-26 18:21:48 +00:00
Count + + ;
2021-09-19 12:55:07 +00:00
return node ;
}
}
2023-06-28 16:41:38 +00:00
IntervalTreeNode < TKey , TValue > newNode = new ( start , end , value , parent ) ;
2021-09-19 12:55:07 +00:00
if ( newNode . Parent = = null )
{
2022-08-26 18:21:48 +00:00
Root = newNode ;
2021-09-19 12:55:07 +00:00
}
else if ( start . CompareTo ( parent . Start ) < 0 )
{
parent . Left = newNode ;
}
else
{
parent . Right = newNode ;
}
PropagateIncrease ( newNode ) ;
2022-08-26 18:21:48 +00:00
Count + + ;
2021-09-19 12:55:07 +00:00
return newNode ;
}
/// <summary>
/// Removes instances of <paramref name="value"> from the dictionary after searching for it with <paramref name="key">.
/// </summary>
/// <param name="key">Key to search for</param>
/// <param name="value">Value to delete</param>
/// <returns>Number of deleted values</returns>
2023-06-28 16:41:38 +00:00
private int Delete ( TKey key , TValue value )
2021-09-19 12:55:07 +00:00
{
2023-06-28 16:41:38 +00:00
IntervalTreeNode < TKey , TValue > nodeToDelete = GetNode ( key ) ;
2021-09-19 12:55:07 +00:00
if ( nodeToDelete = = null )
{
return 0 ;
}
int removed = nodeToDelete . Values . RemoveAll ( node = > node . Value . Equals ( value ) ) ;
if ( nodeToDelete . Values . Count > 0 )
{
if ( removed > 0 )
{
nodeToDelete . End = nodeToDelete . Values . Max ( node = > node . End ) ;
// Recalculate max from children and new end.
PropagateFull ( nodeToDelete ) ;
}
return removed ;
}
2023-06-28 16:41:38 +00:00
IntervalTreeNode < TKey , TValue > replacementNode ;
2021-09-19 12:55:07 +00:00
if ( LeftOf ( nodeToDelete ) = = null | | RightOf ( nodeToDelete ) = = null )
{
replacementNode = nodeToDelete ;
}
else
{
replacementNode = PredecessorOf ( nodeToDelete ) ;
}
2023-06-28 16:41:38 +00:00
IntervalTreeNode < TKey , TValue > tmp = LeftOf ( replacementNode ) ? ? RightOf ( replacementNode ) ;
2021-09-19 12:55:07 +00:00
if ( tmp ! = null )
{
tmp . Parent = ParentOf ( replacementNode ) ;
}
if ( ParentOf ( replacementNode ) = = null )
{
2022-08-26 18:21:48 +00:00
Root = tmp ;
2021-09-19 12:55:07 +00:00
}
else if ( replacementNode = = LeftOf ( ParentOf ( replacementNode ) ) )
{
ParentOf ( replacementNode ) . Left = tmp ;
}
else
{
ParentOf ( replacementNode ) . Right = tmp ;
}
if ( replacementNode ! = nodeToDelete )
{
nodeToDelete . Start = replacementNode . Start ;
nodeToDelete . Values = replacementNode . Values ;
nodeToDelete . End = replacementNode . End ;
nodeToDelete . Max = replacementNode . Max ;
}
PropagateFull ( replacementNode ) ;
if ( tmp ! = null & & ColorOf ( replacementNode ) = = Black )
{
RestoreBalanceAfterRemoval ( tmp ) ;
}
return removed ;
}
#endregion
2023-06-28 16:41:38 +00:00
protected override void RotateLeft ( IntervalTreeNode < TKey , TValue > node )
2021-09-19 12:55:07 +00:00
{
if ( node ! = null )
{
2022-08-26 18:21:48 +00:00
base . RotateLeft ( node ) ;
2021-09-19 12:55:07 +00:00
PropagateFull ( node ) ;
}
}
2023-06-28 16:41:38 +00:00
protected override void RotateRight ( IntervalTreeNode < TKey , TValue > node )
2021-09-19 12:55:07 +00:00
{
if ( node ! = null )
{
2022-08-26 18:21:48 +00:00
base . RotateRight ( node ) ;
2021-09-19 12:55:07 +00:00
PropagateFull ( node ) ;
}
}
2023-06-28 16:41:38 +00:00
public bool ContainsKey ( TKey key )
2021-09-19 12:55:07 +00:00
{
2022-12-27 19:27:11 +00:00
ArgumentNullException . ThrowIfNull ( key ) ;
2021-09-19 12:55:07 +00:00
return GetNode ( key ) ! = null ;
}
}
/// <summary>
/// Represents a value and its start and end keys.
/// </summary>
2023-06-28 16:41:38 +00:00
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
public readonly struct RangeNode < TKey , TValue >
2021-09-19 12:55:07 +00:00
{
2023-06-28 16:41:38 +00:00
public readonly TKey Start ;
public readonly TKey End ;
public readonly TValue Value ;
2021-09-19 12:55:07 +00:00
2023-06-28 16:41:38 +00:00
public RangeNode ( TKey start , TKey end , TValue value )
2021-09-19 12:55:07 +00:00
{
Start = start ;
End = end ;
Value = value ;
}
}
/// <summary>
/// Represents a node in the IntervalTree which contains start and end keys of type K, and a value of generic type V.
/// </summary>
2023-06-28 16:41:38 +00:00
/// <typeparam name="TKey">Key type of the node</typeparam>
/// <typeparam name="TValue">Value type of the node</typeparam>
public class IntervalTreeNode < TKey , TValue > : IntrusiveRedBlackTreeNode < IntervalTreeNode < TKey , TValue > >
2021-09-19 12:55:07 +00:00
{
/// <summary>
/// The start of the range.
/// </summary>
2023-06-28 16:41:38 +00:00
internal TKey Start ;
2021-09-19 12:55:07 +00:00
/// <summary>
/// The end of the range - maximum of all in the Values list.
/// </summary>
2023-06-28 16:41:38 +00:00
internal TKey End ;
2021-09-19 12:55:07 +00:00
/// <summary>
/// The maximum end value of this node and all its children.
/// </summary>
2023-06-28 16:41:38 +00:00
internal TKey Max ;
2021-09-19 12:55:07 +00:00
2022-08-26 18:21:48 +00:00
/// <summary>
/// Values contained on the node that shares a common Start value.
/// </summary>
2023-06-28 16:41:38 +00:00
internal List < RangeNode < TKey , TValue > > Values ;
2021-09-19 12:55:07 +00:00
2023-06-28 16:41:38 +00:00
internal IntervalTreeNode ( TKey start , TKey end , TValue value , IntervalTreeNode < TKey , TValue > parent )
2021-09-19 12:55:07 +00:00
{
2022-05-02 23:30:02 +00:00
Start = start ;
End = end ;
Max = end ;
2023-06-28 16:41:38 +00:00
Values = new List < RangeNode < TKey , TValue > > { new RangeNode < TKey , TValue > ( start , end , value ) } ;
2022-05-02 23:30:02 +00:00
Parent = parent ;
2021-09-19 12:55:07 +00:00
}
}
}