Left-leaning Red-Black Trees (2008) [pdf]
cs.princeton.edu
cs.princeton.edu
A good (and working!) implementation for a top-down no-parent pointer RB tree is by Julienne Walker: http://www.eternallyconfuzzled.com/tuts/datastructures/jsw_t.... No back-and-forth rotations here. Very good explanations and very good code.
Or use AVL trees instead, they work naturally (i.e., without clever restructuring) more cache-locally by storing the balance info one node up the tree.
github.com/aybabtme/datagen
Otherwise there's also github.com/petar/GoLLRB/llrb. (* generic red-black-tree in Standard Jersey ML *)
type key = string
datatype color = R | B
datatype tree = E | T of (color * tree * key * tree)
fun rbmem (x, E) = false
| rbmem (x, T (_,a,y,b)) =
if x < y then rbmem (x,a)
else
if x > y then rbmem (x,b)
else
true
fun balance ( (B,T (R,T (R,a,x,b),y,c),z,d)
| (B,T (R,a,x,T (R,b,y,c)),z,d)
| (B,a,x,T (R,T (R,b,y,c),z,d))
| (B,a,x,T (R,b,y,T (R,c,z,d)))) = T (R,T (B,a,x,b),y,T (B,c,z,d))
| balance body = T body
fun insert (x,s) =
let fun ins E = T (R,E,x,E)
| ins (s as T (color,a,y,b)) =
if x < y then balance (color,(ins a),y,b)
else if x > y then balance (color,a,y,(ins b))
else s
val T (_,a,y,b) = ins s (* guaranteed to be non-empty *)
in T (B,a,y,b)
end private Node insert(Node h, Key key, Value val) {
if (h == null)
return new Node(key, val, RED);
if (isRed(h.left) && isRed(h.right))
colorFlip(h);
int cmp = key.compareTo(h.key);
if (cmp == 0) h.val = val;
else if (cmp < 0)
h.left = insert(h.left, key, val);
else
h.right = insert(h.right, key, val);
if (isRed(h.right))
h = rotateLeft(h);
if (isRed(h.left) && isRed(h.left.left))
h = rotateRight(h);
return h;
}
I've looked at this code from several angles, and I still can't see how type safety might be compromised by adding an extra bit for color. The Java compiler requires you to specify the static object type that your LLRBT returns, specifically so the JVM can make as few assumptions about the underlying data types as necessary at runtime. And the color bit, that's only passed as an argument to the object constructor to expedite the elementary rotations and color flips that happen frequently in a LLRBT, to enable the creation of black ones and same-colored ones. I don't think type safety is affected by this or even related.It's not a question about type safety, Java simply has no mechanism to make this possible (a decision which was influenced by type safety concerns).
OCaml uses the low bits to encode some basic type information for the GC. Pointers have both low bits 0, integers have the lowest bit set, and sum types have the lowest bit unset and the second bit set. This means integers are 31/63 bits long and using native-sized integers carries a significant performance penalty.
Type safety would be easy; there are several routes one could go: A) Suppose we have tagged pointer types with a really terrible syntax like {MyClass + 3bits}. Then we can say {MyClass + 3bits} is not a subtype of Object, but is rather a subtype of {Object + 3bits}. MyClass is a subtype of {MyClass + 3bits}, if you like, since it doesn't hurt to mask out zero bits. This is all at compile time, and I suppose all erased types must be {Object + 3bits} now for safety. But note that the value referenced by an {Object + 3bits} can still be assigned to an Object, since the bits live in the pointer you just lose the 3 bits.
B) Simply change all existing pointers to have these extra bits, with some sort of syntax for that. Note that these bits live in the pointer, so even null would have the bits. Then there's no change whatsoever to the type system.
It's a moot point in practice since the runtime already uses those tag bits itself.
A language with references/pointers could have a provision for storing a user-defined bit in a pointer. All uses of pointers (at the language implementation level) would then be aware of this requirement. All compiler-generated code which dereferences pointers would clear the bit, and so on.
The specification would also define details like whether or not two pointers to the same object, but with a different setting of the user bit, compare equal. (Or do they compare equal and unequal under different kinds of equality tests.)
This kind of thing is, of course, not safe if the language simply leaves an "escape hatch" for flipping a bit in a pointer, without adding any other requirements.
(Java has no such escape hatch; I'm assuming the topic in this sub-thread is how such a thing can be safe in a language, including possibly a dialect of Java.)