diff --git a/MoreStructures.Tests/Dictionaries/BSTDict.cs b/MoreStructures.Tests/Dictionaries/BSTDict.cs
new file mode 100644
index 00000000..a6bee284
--- /dev/null
+++ b/MoreStructures.Tests/Dictionaries/BSTDict.cs
@@ -0,0 +1,189 @@
+using MoreStructures.Dictionaries;
+
+namespace MoreStructures.Tests.Dictionaries;
+
+///
+/// A implementation based on a Binary Search Tree.
+///
+public class BSTDict : IDict
+ where TKey : notnull, IComparable
+{
+ private sealed record Node(TKey Key, TValue Value, Node? Left, Node? Right)
+ {
+ public int Count { get; } = Left?.Count ?? 0 + Right?.Count ?? 0 + 1;
+ }
+
+ private Node? Root { get; set; } = null;
+
+ ///
+ ///
+ /// Retrieved from the count stored on the root of the BST.
+ ///
+ /// Time and Space Complexity are O(1).
+ ///
+ public int Count => Root?.Count ?? 0;
+
+ ///
+ ///
+ /// Both retrieval and insertion are done by traversing the tree and looking for the node with key equal to the
+ /// provided key.
+ ///
+ /// Retrieval just returns the value of the node, if found, whereas insertion changes the value in the tree by
+ /// replacing the node or adding a new one.
+ ///
+ /// Time Complexity is O(h), where h is the height of the tree. Space Complexity is O(1).
+ ///
+ public TValue this[TKey key]
+ {
+ get
+ {
+ return Retrieve(Root);
+
+ TValue Retrieve(Node? node) => node?.Key.CompareTo(key) switch
+ {
+ null => throw new KeyNotFoundException($"Couldn't find key '{key}' in the dictionary."),
+ 0 => node.Value,
+ > 0 => Retrieve(node.Left),
+ _ => Retrieve(node.Right),
+ };
+ }
+ set
+ {
+ Root = InsertOrUpdate(Root);
+
+ Node? InsertOrUpdate(Node? node) => node?.Key.CompareTo(key) switch
+ {
+ null => new(key, value, null, null),
+ 0 => new(key, value, node.Left, node.Right),
+ > 0 => new(node.Key, node.Value, InsertOrUpdate(node.Left), node.Right),
+ _ => new(node.Key, node.Value, node.Left, InsertOrUpdate(node.Right)),
+ };
+ }
+ }
+
+ ///
+ ///
+ /// Performs an in-order traversal of tree, yielding all the node keys.
+ ///
+ /// In this implementation, the order of is consistent with the order of .
+ ///
+ /// Time Complexity is O(n), when enumerated, where n is the number of items in the dictionary.
+ ///
+ /// Space Complexity is O(1).
+ ///
+ public IEnumerable Keys
+ {
+ get
+ {
+ return InOrderTraversal(Root);
+
+ static IEnumerable InOrderTraversal(Node? node)
+ {
+ if (node == null) yield break;
+ InOrderTraversal(node.Left);
+ yield return node.Key;
+ InOrderTraversal(node.Right);
+ }
+ }
+ }
+
+ ///
+ ///
+ /// Performs an in-order traversal of tree, yielding all the node values.
+ ///
+ /// In this implementation, the order of is consistent with the order of .
+ ///
+ /// Time Complexity is O(n), when enumerated, where n is the number of items in the dictionary.
+ ///
+ /// Space Complexity is O(1).
+ ///
+ public IEnumerable Values
+ {
+ get
+ {
+ return InOrderTraversal(Root);
+
+ static IEnumerable InOrderTraversal(Node? node)
+ {
+ if (node == null) yield break;
+ InOrderTraversal(node.Left);
+ yield return node.Value;
+ InOrderTraversal(node.Right);
+ }
+ }
+ }
+
+ ///
+ ///
+ /// Insertion is done by traversing the tree and looking for a node with key equal to the provided key, as in
+ /// the setter of .
+ ///
+ /// An is raised when an item with the same key as the one provided already exists
+ /// in the dictionary.
+ ///
+ /// Time Complexity is O(h), where h is the height of the tree. Space Complexity is O(1).
+ ///
+ public void Add(TKey key, TValue value)
+ {
+ Root = Insert(Root);
+
+ Node? Insert(Node? node) => node?.Key.CompareTo(key) switch
+ {
+ null => new(key, value, null, null, 1),
+ 0 => throw new ArgumentException($"An item with the key '{key}' already exists in the dictionary."),
+ > 0 => Insert(node.Left),
+ _ => Insert(node.Right),
+ };
+ }
+
+ ///
+ ///
+ /// As in value retrieval, a traversal of the tree is done, looking for the node with key equal to the
+ /// provided key.
+ ///
+ /// Time Complexity is O(h), where h is the height of the tree. Space Complexity is O(1).
+ ///
+ public bool ContainsKey(TKey key)
+ {
+ return Find(Root);
+
+ bool Find(Node? node) => node?.Key.CompareTo(key) switch
+ {
+ null => false,
+ 0 => true,
+ > 0 => Find(node.Left),
+ _ => Find(node.Right),
+ };
+ }
+
+ public TValue? Remove(TKey key)
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ ///
+ /// Retrieval is done by traversing the tree and looking for a node with key equal to the provided key, as in
+ /// the getter of .
+ ///
+ /// is returned if such a mapping is not found, and is set to
+ /// the value for .
+ ///
+ /// Time Complexity is O(h), where h is the height of the tree. Space Complexity is O(1).
+ ///
+ public bool TryGetValue(TKey key, out TValue? value)
+ {
+ var (found, valueFound) = Find(Root);
+ value = found ? valueFound : default;
+ return found;
+
+ (bool, TValue?) Find(Node? node) => node?.Key.CompareTo(key) switch
+ {
+ null => (false, default),
+ 0 => (true, node.Value),
+ > 0 => Find(node.Left),
+ _ => Find(node.Right),
+ };
+ }
+}
+
diff --git a/MoreStructures.Tests/Dictionaries/BSTDictTests.cs b/MoreStructures.Tests/Dictionaries/BSTDictTests.cs
new file mode 100644
index 00000000..13b71ed8
--- /dev/null
+++ b/MoreStructures.Tests/Dictionaries/BSTDictTests.cs
@@ -0,0 +1,9 @@
+namespace MoreStructures.Tests.Dictionaries;
+
+[TestClass]
+public class BSTDictTests : DictTests
+{
+ public BSTDictTests() : base(() => new BSTDict())
+ {
+ }
+}
\ No newline at end of file
diff --git a/MoreStructures.Tests/Dictionaries/DictTests.cs b/MoreStructures.Tests/Dictionaries/DictTests.cs
new file mode 100644
index 00000000..d9228237
--- /dev/null
+++ b/MoreStructures.Tests/Dictionaries/DictTests.cs
@@ -0,0 +1,18 @@
+namespace MoreStructures.Tests.Dictionaries;
+
+public abstract class DictTests
+{
+ protected Func> Builder { get; }
+
+ protected DictTests(Func> builder)
+ {
+ Builder = builder;
+ }
+
+ [TestMethod]
+ public void Count_IsCorrect()
+ {
+ var dictionary = Builder();
+ dictionary.Count();
+ }
+}
diff --git a/MoreStructures/Dictionaries/BSTDict.cs b/MoreStructures/Dictionaries/BSTDict.cs
new file mode 100644
index 00000000..b15c3c4a
--- /dev/null
+++ b/MoreStructures/Dictionaries/BSTDict.cs
@@ -0,0 +1,250 @@
+namespace MoreStructures.Dictionaries;
+
+///
+/// A implementation based on a Binary Search Tree.
+///
+public class BSTDict : IDict
+ where TKey : notnull, IComparable
+{
+ private sealed class Node
+ {
+ private Node? _left = null;
+ private Node? _right = null;
+ private int _count = 1;
+
+ public TKey Key { get; set; }
+ public TValue Value { get; set; }
+
+ public Node? Left { get => _left; set { _left = value; UpdateCount(); } }
+ public Node? Right { get => _right; set { _right = value; UpdateCount(); } }
+ public int Count => _count;
+
+ public Node(TKey key, TValue value, BSTDict.Node? left, BSTDict.Node? right)
+ {
+ Key = key;
+ Value = value;
+ Left = left;
+ Right = right;
+ }
+
+ private void UpdateCount()
+ {
+ _count = (_left?.Count ?? 0) + (_right?.Count ?? 0) + 1;
+ }
+
+ public Node Mutate(Action mutation)
+ {
+ mutation(this);
+ return this;
+ }
+ }
+
+ private static (bool found, TValue? valueFound) Find(Node? node, TKey key) =>
+ node?.Key.CompareTo(key) switch
+ {
+ null => (false, default),
+ 0 => (true, node.Value),
+ > 0 => Find(node.Left, key),
+ _ => Find(node.Right, key),
+ };
+
+ private static IEnumerable InOrderTraversal(Node? node, Func valueProvider)
+ {
+ if (node == null)
+ yield break;
+
+ foreach (var leftDescendant in InOrderTraversal(node.Left, valueProvider))
+ yield return leftDescendant;
+
+ yield return valueProvider(node);
+
+ foreach (var rightDescendant in InOrderTraversal(node.Right, valueProvider))
+ yield return rightDescendant;
+ }
+
+ private Node? Root { get; set; } = null;
+
+ ///
+ ///
+ /// Retrieved from the count stored on the root of the BST.
+ ///
+ /// Time and Space Complexity are O(1).
+ ///
+ public int Count => Root?.Count ?? 0;
+
+ ///
+ ///
+ /// Both retrieval and insertion are done by traversing the tree from its root downwards, and looking for the node
+ /// with key equal to the provided .
+ ///
+ /// Retrieval just returns the value of the node, if found, raising otherwise.
+ ///
+ /// Insertion changes the value in the tree by replacing the node or adding a new one, also replacing all the nodes
+ /// in the path from the root to the insertion/update point.
+ ///
+ /// Time Complexity is O(h), where h is the height of the tree. Space Complexity is O(1).
+ ///
+ public TValue this[TKey key]
+ {
+ get
+ {
+ var (found, valueFound) = Find(Root, key);
+ if (!found)
+ throw new KeyNotFoundException($"Couldn't find key '{key}' in the dictionary.");
+ return valueFound!;
+ }
+ set
+ {
+ Root = InsertOrUpdate(Root);
+
+ Node? InsertOrUpdate(Node? node) => node?.Key.CompareTo(key) switch
+ {
+ null => new(key, value, null, null),
+ 0 => node.Mutate(n => n.Value = value),
+ > 0 => node.Mutate(n => n.Left = InsertOrUpdate(node.Left)),
+ _ => node.Mutate(n => n.Right = InsertOrUpdate(node.Right)),
+ };
+ }
+ }
+
+ ///
+ ///
+ /// Performs an in-order traversal of tree, yielding all the node keys.
+ ///
+ /// In this implementation, the order of is consistent with the order of .
+ ///
+ /// Time Complexity is O(n), when enumerated, where n is the number of items in the dictionary.
+ ///
+ /// Space Complexity is O(1). Values are streamed to the client.
+ ///
+ public IEnumerable Keys => InOrderTraversal(Root, node => node.Key);
+
+ ///
+ ///
+ /// Performs an in-order traversal of tree, yielding all the node values.
+ ///
+ /// In this implementation, the order of is consistent with the order of .
+ ///
+ /// Time Complexity is O(n), when enumerated, where n is the number of items in the dictionary.
+ ///
+ /// Space Complexity is O(1). Values are streamed to the client.
+ ///
+ public IEnumerable Values => InOrderTraversal(Root, node => node.Value);
+
+ ///
+ ///
+ /// Insertion is done by traversing the tree and looking for a node with key equal to the provided key, as in
+ /// the setter of .
+ ///
+ /// Insertion replaces all nodes in the path from the root to the insertion point, recalculating summarization the
+ /// properties kept in tree nodes, such as the count of nodes in the subtree rooted at the node.
+ ///
+ /// An is raised when an item with the same key as the one provided already exists
+ /// in the dictionary, since key duplications is not allowed in implementations.
+ ///
+ /// Time Complexity is O(h), where h is the height of the tree. Space Complexity is O(1).
+ ///
+ public void Add(TKey key, TValue value)
+ {
+ Root = Insert(Root);
+
+ Node? Insert(Node? node) => node?.Key.CompareTo(key) switch
+ {
+ null => new(key, value, null, null),
+ 0 => throw new ArgumentException($"An item with the key '{key}' already exists in the dictionary."),
+ > 0 => node.Mutate(n => n.Left = Insert(node.Left)),
+ _ => node.Mutate(n => n.Right = Insert(node.Right)),
+ };
+ }
+
+ ///
+ ///
+ /// As in value retrieval, a traversal of the tree is done, looking for the node with key equal to the
+ /// provided key.
+ ///
+ /// Time Complexity is O(h), where h is the height of the tree. Space Complexity is O(1).
+ ///
+ public bool ContainsKey(TKey key) => Find(Root, key).found;
+
+ ///
+ ///
+ /// Performs Hibbard deletion:
+ ///
+ /// - first the node v, associated with the given , and its parent p, are retrieved from the
+ /// tree;
+ ///
+ /// - if v is not found, the deletion cannot be performed and the for
+ /// is returned;
+ ///
+ /// - if v is found and is a leaf, the reference to t in p is set to null, and no other change is made in the tree;
+ ///
+ /// -
+ ///
+ public TValue? Remove(TKey key)
+ {
+ var (node, parent) = Find(Root, null);
+ if (node == null)
+ return default;
+
+ Remove(node);
+
+
+ static Node Remove(Node node)
+ {
+ // First case: the node is a leaf or has a single child
+ if (node.Left == null || node.Right == null)
+ {
+ var child = node.Left ?? node.Right;
+ if (parent == null)
+ Root = child;
+ else if (ReferenceEquals(parent.Left, node))
+ parent.Left = child;
+ else
+ parent.Right = child;
+
+ return node;
+ }
+
+ // Second case: the node has two children
+ var leftChild = node.Left;
+
+
+ }
+
+ static (Node? node, Node? parent) Find(Node? node, Node? parent) =>
+ node?.Key.CompareTo(key) switch
+ {
+ null => (default, default),
+ 0 => (node, parent),
+ > 0 => Find(node.Left, node),
+ _ => Find(node.Right, node),
+ };
+ }
+
+ ///
+ ///
+ /// Retrieval is done by traversing the tree and looking for a node with key equal to the provided key, as in
+ /// the getter of .
+ ///
+ /// is returned if such a mapping is not found, and is set to
+ /// the value for .
+ ///
+ /// Time Complexity is O(h), where h is the height of the tree. Space Complexity is O(1).
+ ///
+ public bool TryGetValue(TKey key, out TValue? value)
+ {
+ var (found, valueFound) = Find(Root, key);
+ value = found ? valueFound : default;
+ return found;
+ }
+
+ ///
+ ///
+ /// TODO
+ ///
+ public TValue? Remove(TKey key)
+ {
+ throw new NotImplementedException();
+ }
+}
+
diff --git a/MoreStructures/Dictionaries/IDict.cs b/MoreStructures/Dictionaries/IDict.cs
new file mode 100644
index 00000000..557c3793
--- /dev/null
+++ b/MoreStructures/Dictionaries/IDict.cs
@@ -0,0 +1,79 @@
+namespace MoreStructures.Dictionaries;
+
+///
+/// A mapping between instances of and instances of , where
+/// a key is associated to 0 or 1 value.
+///
+/// The type of the key instances in the dictionary.
+/// The type of the value instances in the dictionary.
+public interface IDict
+ where TKey : notnull
+{
+ ///
+ /// The number of (key, value) mappings in this dictionary.
+ ///
+ int Count { get; }
+
+ ///
+ /// Gets or inserts/updates the item with the specified .
+ ///
+ /// The key associated to the value to be retrieved.
+ ///
+ /// The value associated with the provided , if any.
+ ///
+ /// If such a key does not exist, a is thrown.
+ ///
+ TValue this[TKey key] { get; set; }
+
+ ///
+ /// A sequence enumerating all the keys in this dictionary. The order is specific to the implementation.
+ ///
+ ///
+ /// In general, it's not guaranteed that the order of is the same as the order of
+ /// .
+ ///
+ IEnumerable Keys { get; }
+
+ ///
+ /// A sequence enumerating all the values in this dictionary. The order is specific to the implementation.
+ ///
+ ///
+ /// In general, it's not guaranteed that the order of is the same as the order of
+ /// .
+ ///
+ IEnumerable Values { get; }
+
+ ///
+ /// Adds a mapping between the provided and .
+ ///
+ /// The key of the mapping.
+ /// The value of the mapping.
+ void Add(TKey key, TValue value);
+
+ ///
+ /// Whether this dictionary contains a mapping for the provided .
+ ///
+ /// The key, to look for in the dictionary.
+ /// Whether there is a mapping with the provided in this dictionary.
+ bool ContainsKey(TKey key);
+
+ ///
+ /// Removes the mapping with the provided from this dictionary, if any.
+ ///
+ /// The key of the mapping to be removed.
+ /// The value of the mapping with the provided .
+ TValue? Remove(TKey key);
+
+ ///
+ /// Tries to get the value for the provided , storing into the provided
+ /// reference.
+ ///
+ /// The key, to look for in the dictionary.
+ ///
+ /// Upon method return, it contains the value associated to , if such a mapping exists in the
+ /// dictionary. Returns the for otherwise.
+ ///
+ /// Whether the retrieval attempt was successful or not.
+ bool TryGetValue(TKey key, out TValue? value);
+}
+