1 module s2.util.container.rbtree_map;
2 
3 import std.container.rbtree;
4 import std.algorithm : map;
5 import std.functional : binaryFun;
6 import std.meta : allSatisfy;
7 import std.range : ElementType, isInputRange;
8 import std.traits : isDynamicArray, isImplicitlyConvertible;
9 
10 /**
11  * A dictionary or associative array backed by a Red-Black tree.
12  */
13 final class RBTreeMap(KeyT, ValueT, alias KeyLessF = "a < b", bool allowDuplicates = false) {
14 public:
15   static struct Pair {
16     KeyT key;
17     ValueT value;
18   }
19 
20   alias keyLess = binaryFun!KeyLessF;
21 
22   alias RedBlackTreeT =
23       RedBlackTree!(Pair, (pair1, pair2) => keyLess(pair1.key, pair2.key), allowDuplicates);
24 
25   RedBlackTreeT rbTree;
26 
27   // Forward compatible methods like: empty(), length(), opSlice(), etc.
28   alias rbTree this;
29 
30   this() {
31     rbTree = new RedBlackTreeT();
32   }
33 
34   this(Pair[] elems...) {
35     rbTree = new RedBlackTreeT(elems);
36   }
37 
38   this(PairRange)(PairRange pairRange)
39   if (isInputRange!PairRange && isImplicitlyConvertible!(ElementType!PairRange, Pair)) {
40     rbTree = new RedBlackTreeT(pairRange);
41   }
42 
43   override
44   bool opEquals(Object rhs) {
45     RBTreeMap that = cast(RBTreeMap) rhs;
46     if (that is null) return false;
47 
48     return rbTree == that.rbTree;
49   }
50 
51   /// Insertion
52   size_t stableInsert(K, V)(K key, V value)
53   if (isImplicitlyConvertible!(K, KeyT) && isImplicitlyConvertible!(V, ValueT)) {
54     return rbTree.stableInsert(Pair(key, value));
55   }
56   alias insert = stableInsert;
57 
58   ValueT opIndexAssign(ValueT value, KeyT key) {
59     rbTree.stableInsert(Pair(key, value));
60     return value;
61   }
62 
63   /// Membership
64   bool opBinaryRight(string op)(KeyT key) const
65   if (op == "in") {
66     return Pair(key) in rbTree;
67   }
68 
69   /// Removal
70   size_t removeKey(K...)(K keys)
71   if (allSatisfy!(isImplicitlyConvertibleToKey, K)) {
72     KeyT[K.length] toRemove = [keys];
73     return removeKey(toRemove[]);
74   }
75 
76   //Helper for removeKey.
77   private template isImplicitlyConvertibleToKey(K)
78   {
79     enum isImplicitlyConvertibleToKey = isImplicitlyConvertible!(K, KeyT);
80   }
81 
82   size_t removeKey(K)(K[] keys)
83   if (isImplicitlyConvertible!(K, KeyT)) {
84     auto keyPairs = keys.map!(key => Pair(key));
85     return rbTree.removeKey(keyPairs);
86   }
87 
88   size_t removeKey(KeyRange)(KeyRange keyRange)
89   if (isInputRange!KeyRange
90       && isImplicitlyConvertible!(ElementType!KeyRange, KeyT)
91       && !isDynamicArray!KeyRange) {
92     auto keyPairs = keys.map(key => Pair(key));
93     return rbTree.removeKey(keyPairs);
94   }
95 
96   /// Ranges
97   RedBlackTreeT.Range upperBound(KeyT key) {
98     return rbTree.upperBound(Pair(key));
99   }
100 
101   RedBlackTreeT.ConstRange upperBound(KeyT key) const {
102     return rbTree.upperBound(Pair(key));
103   }
104 
105   RedBlackTreeT.ImmutableRange upperBound(KeyT key) immutable {
106     return rbTree.upperBound(Pair(key));
107   }
108 
109   RedBlackTreeT.Range lowerBound(KeyT key) {
110     return rbTree.lowerBound(Pair(key));
111   }
112 
113   RedBlackTreeT.ConstRange lowerBound(KeyT key) const {
114     return rbTree.lowerBound(Pair(key));
115   }
116 
117   RedBlackTreeT.ImmutableRange lowerBound(KeyT key) immutable {
118     return rbTree.lowerBound(Pair(key));
119   }
120 
121   auto equalRange(KeyT key) {
122     return rbTree.equalRange(Pair(key));
123   }
124 
125 }