using System;
using System.Collections.Generic;
namespace Best.HTTP.Shared.Databases.Indexing
{
public enum Side
{
Left,
Right
}
///
/// Implements most common list functions. With best case (no or only one item) it doesn't do any allocation.
///
public struct NoAllocList
{
private T _value;
private bool _hasValue;
private List _values;
public NoAllocList(T value)
{
this._value = value;
this._hasValue = true;
this._values = null;
}
public T this[int index] {
get => this._hasValue ? this._value : this._values[index];
set
{
if (index < 0 || (this._values == null && index > 0))
throw new IndexOutOfRangeException(index.ToString());
if (this._values != null)
this._values[index] = value;
else
{
this._value = value;
this._hasValue = true;
}
}
}
public int Count { get => this._values != null ? this._values.Count : (this._hasValue ? 1 : 0); }
public void Add(T item)
{
if (this._values != null)
this._values.Add(item);
else if (this._hasValue)
{
this._values = new List { this._value, item };
this._value = default(T);
this._hasValue = false;
}
else
{
this._value = item;
this._hasValue = true;
}
}
public void Clear()
{
this._values?.Clear();
this._values = null;
this._value = default(T);
this._hasValue = false;
}
public bool Contains(T item)
{
if (this._values != null)
return this._values.Contains(item);
// This can thrown a NullRefException if _value is null!
return this._hasValue ? this._value.Equals(item) : false;
}
public bool Remove(T item)
{
if (this._values != null)
return this._values.Remove(item);
else if (this._hasValue && this._value.Equals(item))
{
this._value = default(T);
this._hasValue = false;
return true;
}
return false;
}
public void RemoveAt(int index)
{
if (index < 0 || (this._values == null && index > 0))
throw new IndexOutOfRangeException(index.ToString());
if (this._values != null)
this._values.RemoveAt(index);
else
{
this._value = default(T);
this._hasValue = false;
}
}
}
public sealed class Node
{
public Node Parent, Left, Right;
public KeyT Key { get; private set; }
///
/// Depth of the node.
///
public int Depth;
///
/// Difference between LeftDepth and RightDepth.
///
public int BalanceFactor { get { return this.LeftDepth - this.RightDepth; } }
///
/// Left node's Depth, or -1 if it's null.
///
public int LeftDepth { get { return this.Left == null ? -1 : this.Left.Depth; } }
///
/// Right node's Depth, or -1 if it's null.
///
public int RightDepth { get { return this.Right == null ? -1 : this.Right.Depth; } }
public bool IsRoot { get { return this.Parent == null; } }
public int ChildCount { get { return (this.Left == null ? 0 : 1) + (this.Right == null ? 0 : 1); } }
// Stored values aren't public as modifing them requires special care.
private NoAllocList _item;
public Node(Node parent, KeyT key, ValueT value)
{
this.Parent = parent;
this.Key = key;
this._item = new NoAllocList(value);
// Depth is 0 by default, as it has no child
this.Depth = 0;
}
public void BubbleUpDepthChange()
{
var current = this;
while (current != null)
{
var oldDepth = current.Depth;
current.Depth = Math.Max(current.LeftDepth, current.RightDepth) + 1;
if (oldDepth != current.Depth)
current = current.Parent;
else
break;
}
}
public ValueT this[int index] { get => this._item[index]; }
public int Count { get => this._item.Count; }
public void Clear() => this._item = new NoAllocList();
public void Add(ValueT value)
{
var tmp = this._item;
tmp.Add(value);
this._item = tmp;
}
public bool Remove(ValueT value)
{
var tmp = this._item;
var result = tmp.Remove(value);
if (result)
this._item = tmp;
return result;
}
public List ToList()
{
var list = new List(this._item.Count);
for (int i = 0; i < this._item.Count; ++i)
list.Add(this._item[i]);
return list;
}
public override string ToString()
{
return $"{this.Left?.Key.ToString()} <- {this.Key.ToString()} -> {this.Right?.Key.ToString()}";
}
}
// https://www.codesdope.com/course/data-structures-avl-trees/
public sealed class AVLTree
{
public int ElemCount { get; private set; }
public int NodeCount { get; private set; }
public IComparer Comparer;
public Node RootNode { get; private set; } = null;
public AVLTree(IComparer comparer)
{
this.Comparer = comparer;
}
public void Add(Key key, Value item, bool clearValues = false)
{
if (this.RootNode == null) {
this.NodeCount++;
this.ElemCount++;
this.RootNode = new Node(null, key, item);
return;
}
var current = this.RootNode;
do
{
// +--------------------+-----------------------+
// | Value | Meaning |
// +--------------------+-----------------------+
// | Less than zero | x is less than y. |
// | Zero | x equals y. |
// | Greater than zero | x is greater than y. |
// +--------------------------------------------+
int comp = this.Comparer.Compare(/*x: */ current.Key, /*y: */ key);
// equals
if (comp == 0)
{
if (clearValues)
{
this.ElemCount -= current.Count;
current.Clear();
}
current.Add(item);
break;
}
// current's key > key
if (comp > 0)
{
// insert new node
if (current.Left == null)
{
current.Left = new Node(current, key, item);
current.BubbleUpDepthChange(/*Side.Left, 1*/);
current = current.Left;
this.NodeCount++;
break;
}
else
{
current = current.Left;
continue;
}
}
// current's key < key
if (comp < 0)
{
// insert new node
if (current.Right == null)
{
current.Right = new Node(current, key, item);
current.BubbleUpDepthChange(/*Side.Right, 1*/);
current = current.Right;
this.NodeCount++;
break;
}
else
{
current = current.Right;
continue;
}
}
} while (true);
this.ElemCount++;
while (RebalanceFrom(current) != null)
;
//TestBalance(this.root);
}
public bool TestBalance() => TestBalance(this.RootNode);
private bool TestBalance(Node node)
{
if (node == null)
return true;
if (Math.Abs(node.BalanceFactor) > 1)
{
//UnityEngine.Debug.Break();
return false;
}
return TestBalance(node.Left) && TestBalance(node.Right);
}
List path = new List(2);
private Node RebalanceFrom(Node newNode)
{
if (newNode.IsRoot || newNode.Parent.IsRoot)
return null;
path.Clear();
// find first unbalanced node or exit when found the root node (root still can be unbalanced!)
var current = newNode;
var balanceFactor = current.BalanceFactor;
while (!current.IsRoot && Math.Abs(balanceFactor) <= 1)
{
if (current.Parent.Left == current)
path.Add(Side.Left);
else
path.Add(Side.Right);
current = current.Parent;
balanceFactor = current.BalanceFactor;
}
// it's a balanced tree
if (Math.Abs(balanceFactor) <= 1)
return null;
Side last = path[path.Count - 1];// path[path.StartIdx];
Side prev = path[path.Count - 2];// path[path.EndIdx];
if (last == Side.Left && prev == Side.Left)
{
// insertion to a left child of a left child
RotateRight(current)
.BubbleUpDepthChange();
current.BubbleUpDepthChange();
}
else if (last == Side.Right && prev == Side.Right)
{
// insertion to a right child of a right child
RotateLeft(current)
.BubbleUpDepthChange();
current.BubbleUpDepthChange();
}
else if (last == Side.Right && prev == Side.Left)
{
// insertion to a left child of a right child
var current_right = current.Right;
RotateRight(current.Right);
RotateLeft(current);
current_right.BubbleUpDepthChange();
current.BubbleUpDepthChange();
}
else if (last == Side.Left && prev == Side.Right)
{
// insertion to a right child of a left child
var current_left = current.Left;
RotateLeft(current.Left);
RotateRight(current);
current_left.BubbleUpDepthChange();
current.BubbleUpDepthChange();
}
return current;
}
public void Clear()
{
this.RootNode = null;
this.ElemCount = 0;
this.NodeCount = 0;
}
public List Remove(Key key)
{
if (this.RootNode == null)
return null;
var current = this.RootNode;
do
{
int comp = this.Comparer.Compare(current.Key, key);
// equals
if (comp == 0)
{
this.NodeCount--;
this.ElemCount -= current.Count;
// remove current node from the tree
RemoveNode(current);
return current.ToList();
}
// current's key > key
if (comp > 0)
{
if (current.Left == null)
return null;
else
{
current = current.Left;
continue;
}
}
// current's key < key
if (comp < 0)
{
if (current.Right == null)
return null;
else
{
current = current.Right;
continue;
}
}
} while (true);
}
public void Remove(Key key, Value value)
{
if (this.RootNode == null)
return;
var current = this.RootNode;
do
{
int comp = this.Comparer.Compare(current.Key, key);
// equals
if (comp == 0)
{
if (current.Remove(value))
this.ElemCount--;
if (current.Count == 0)
{
// remove current node from the tree
RemoveNode(current);
this.NodeCount--;
}
return;
}
// current's key > key
if (comp > 0)
{
if (current.Left == null)
return ;
else
{
current = current.Left;
continue;
}
}
// current's key < key
if (comp < 0)
{
if (current.Right == null)
return ;
else
{
current = current.Right;
continue;
}
}
} while (true);
}
///
/// Removes node and reparent any child it has.
///
private void RemoveNode(Node node)
{
var parent = node.Parent;
Side side = parent?.Left == node ? Side.Left : Side.Right;
int childCount = node.ChildCount;
var testForRebalanceNode = parent;
switch(childCount)
{
case 0:
// node has no child
if (parent == null)
{
this.RootNode = null;
}
else
{
if (parent.Left == node)
parent.Left = null;
else
parent.Right = null;
parent.BubbleUpDepthChange();
}
node.Parent = null;
break;
case 1:
// re-parent the only child
// Example: Removing node 25 will replace it with 30
//
// 20
// 15 25
// 30
//
var child = node.Left ?? node.Right;
if (parent == null)
{
this.RootNode = child;
this.RootNode.Parent = null;
}
else
{
if (parent.Left == node)
parent.Left = child;
else
parent.Right = child;
child.Parent = parent;
parent.BubbleUpDepthChange();
}
break;
default:
// two child
// 1: Replace 20 with 25
//
// 20 20
// 15 25 15 25
// 30
//
// 2: Replace 20 with 22
//
// 20
// 15 25
// 22
// 3: Re-parent 23 for 25, replace 20 with 22
//
// 20
// 15 25
// 22
// 23
// Cases 1 and 3 are the same, both 25 and 22 has a right child. But in case 3, 22 isn't first child of 20!
// Find node with the least Key, that's a node without a left node so we have to deal only with its right node.
var nodeToReplaceWith = FindMin(node.Right);
testForRebalanceNode = nodeToReplaceWith;
side = Side.Right;
// re-parent 23 in case 3:
if (nodeToReplaceWith.Parent != node)
{
testForRebalanceNode = nodeToReplaceWith.Parent;
if (nodeToReplaceWith.Parent.Left == nodeToReplaceWith)
{
nodeToReplaceWith.Parent.Left = nodeToReplaceWith.Right;
side = Side.Left;
}
else
{
nodeToReplaceWith.Parent.Right = nodeToReplaceWith.Right;
side = Side.Right;
}
if (nodeToReplaceWith.Right != null)
nodeToReplaceWith.Right.Parent = nodeToReplaceWith.Parent;
}
if (parent == null)
this.RootNode = nodeToReplaceWith;
else
{
if (parent.Left == node)
parent.Left = nodeToReplaceWith;
else
parent.Right = nodeToReplaceWith;
}
nodeToReplaceWith.Parent = parent;
// Reparent node's left
nodeToReplaceWith.Left = node.Left;
node.Left.Parent = nodeToReplaceWith;
// Reparent node's right node, if it's not the one we replaceing it with
if (node.Right != nodeToReplaceWith)
{
nodeToReplaceWith.Right = node.Right;
node.Right.Parent = nodeToReplaceWith;
}
//else
// nodeToReplaceWith.Right = null;
if (testForRebalanceNode != nodeToReplaceWith)
testForRebalanceNode.BubbleUpDepthChange();
nodeToReplaceWith.BubbleUpDepthChange();
break;
}
while (RebalanceForRemoval(testForRebalanceNode, side) != null)
;
//TestBalance(this.root);
}
private Node RebalanceForRemoval(Node removedParentNode, Side side)
{
if (removedParentNode == null)
return null;
path.Clear();
path.Add(side);
// find first unbalanced node or exit when found the root node (root still can be unbalanced!)
var current = removedParentNode;
while (!current.IsRoot && Math.Abs(current.BalanceFactor) <= 1)
{
if (current.Parent.Left == current)
path.Add(Side.Left);
else
path.Add(Side.Right);
current = current.Parent;
}
// it's a balanced tree
if (Math.Abs(current.BalanceFactor) <= 1)
return null;
// from what direction we came from
Side fromDirection = path[path.Count - 1];
// check weather it's an inside or outside case
switch (fromDirection)
{
case Side.Right:
{
bool isOutside = current.Left.LeftDepth >= current.Left.RightDepth;
if (isOutside)
{
RotateRight(current)
.BubbleUpDepthChange();
current.BubbleUpDepthChange();
}
else
{
var current_left = current.Left;
RotateLeft(current.Left);
RotateRight(current);
current_left.BubbleUpDepthChange();
current.BubbleUpDepthChange();
}
}
break;
case Side.Left:
{
bool isOutside = current.Right.RightDepth >= current.Right.LeftDepth;
if (isOutside)
{
// Example: Removing node 14 result in a disbalance in node 20
//
// 20
// 15 25
// (14) 22 26
// 27
var current_right = current.Right; // node 25
RotateLeft(current);
// After RotateLeft(current: node 20):
// 25
// 20 26
// 15 22 27
current.BubbleUpDepthChange(); // node 20
current_right.BubbleUpDepthChange(); // node 25
}
else
{
// Example: Removing node 14 results in a disbalance in node 20.
//
// 20
// 15 25
// (14) 22 26
// 23
var current_right = current.Right;
RotateRight(current.Right);
// After RotateRight(current.Right: node 22):
// 20
// 15 22
// 25
// 23 26
RotateLeft(current);
// After RotateLeft(current: node 20):
// 22
// 20 25
// 15 23 26
current.BubbleUpDepthChange();
current_right.BubbleUpDepthChange();
}
}
break;
}
return current;
}
private Node FindMin(Node node)
{
var current = node;
while (current.Left != null)
current = current.Left;
return current;
}
private Node FindMax(Node node)
{
var current = node;
while (current.Right != null)
current = current.Right;
return current;
}
public List Find(Key key) {
if (this.RootNode == null)
return null;
var current = this.RootNode;
do
{
int comp = this.Comparer.Compare(current.Key, key);
// equals
if (comp == 0)
return current.ToList();
// current's key > key
if (comp > 0)
{
if (current.Left == null)
return null;
else
{
current = current.Left;
continue;
}
}
// current's key < key
if (comp < 0)
{
if (current.Right == null)
return null;
else
{
current = current.Right;
continue;
}
}
} while (true);
}
public IEnumerable WalkHorizontal()
{
if (this.RootNode == null)
yield break;
Queue> toWalk = new Queue>();
toWalk.Enqueue(this.RootNode);
while (toWalk.Count > 0)
{
var current = toWalk.Dequeue();
if (current.Left != null)
toWalk.Enqueue(current.Left);
if (current.Right != null)
toWalk.Enqueue(current.Right);
for (int i = 0; i < current.Count; i++)
yield return current[i];
}
}
public bool ContainsKey(Key key)
{
if (this.RootNode == null)
return false;
var current = this.RootNode;
do
{
int comp = this.Comparer.Compare(current.Key, key);
// equals
if (comp == 0)
return true;
// current's key > key
if (comp > 0)
{
if (current.Left == null)
return false;
else
{
current = current.Left;
continue;
}
}
// current's key < key
if (comp < 0)
{
if (current.Right == null)
return false;
else
{
current = current.Right;
continue;
}
}
} while (true);
}
private Node RotateRight(Node current)
{
// Current\
// 20 15
// 15 10 20
// 10 ? ?
var parent = current.Parent;
var leftChild = current.Left;
// re-parent left child
if (parent != null)
{
if (parent.Left == current)
parent.Left = leftChild;
else
parent.Right = leftChild;
}
else
this.RootNode = leftChild;
leftChild.Parent = parent;
// re-parent left child's right child
if (leftChild.Right != null)
leftChild.Right.Parent = current;
current.Left = leftChild.Right;
// re-parent current
current.Parent = leftChild;
leftChild.Right = current;
// return with the node that took the place of current
return leftChild;
}
private Node RotateLeft(Node current)
{
// /Current
// 20 15
// 15 20 10
// ? 10 ?
var parent = current.Parent;
var rightChild = current.Right;
// re-parent right child
if (parent != null)
{
if (parent.Left == current)
parent.Left = rightChild;
else
parent.Right = rightChild;
}
else
this.RootNode = rightChild;
rightChild.Parent = parent;
// re-parent right child's left child
if (rightChild.Left != null)
rightChild.Left.Parent = current;
current.Right = rightChild.Left;
// re-parent current
current.Parent = rightChild;
rightChild.Left = current;
return rightChild;
}
}
}