Closed OndrejSpanel closed 5 months ago
@scala/collections
On a modern JVM,
[error] Test scala.collection.immutable.SortedSetTest.rangeFromNPE failed: java.lang.NullPointerException: Cannot invoke "scala.collection.immutable.RedBlackTree$Tree.right()" because "tl" is null, took 0.006 sec
I assume we have the standard invariants for red-black trees? https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Properties
A red node does not have a red child.
val s1 = collection.immutable.TreeSet(6, 1, 11, 9, 10, 8)
println(s1.tree)
val s2 = s1.rangeFrom(2)
println(s2.tree)
gives
BlackTree(
9,
null,
BlackTree(
6,
null,
RedTree(1, null, null, null),
RedTree(8, null, null, null)
),
BlackTree(11, null, RedTree(10, null, null, null), null)
)
BlackTree(
10,
null,
BlackTree(
9,
null,
RedTree(8, null, RedTree(6, null, null, null), null), // oops?
null
),
BlackTree(11, null, null, null)
)
I also spent some time with wikipedia and had the same question. I almost DMd Stefan about it, but intended to return to it. As the movie line says, I picked a helluva week to give up coffee.
Found the properties here: https://github.com/scala/scala/blob/v2.13.12/test/scalacheck/redblacktree.scala#L123-L127
Is this a 2.13-only bug? If 2.12 is affected, we should backport.
RBT lobby strikes again. Why, for immutable sets, not AVLs in the first place? A higher number of rotations doesn't hurt when you need to create log(n)
nodes on the way back anyway, and they are both more balanced and much simpler to implement, which is an optimization in itself.
avl-set.com
OK, I see AVL tree in a Sedgewick exercise. 2024 is the year I am not a brutish cretin. I had just pulled the book out again an hour ago on the path to offer meaningful assistance.
Obviously RBT is supported by RoBoTs.
@OndrejSpanel great catch — thank you for isolating and reporting 🙏
Reproduction steps
Scala version: 2.13.12, 3.3.1
Problem
The expression above throws NullPointerException
java.lang.NullPointerExceptions
java.lang.NullPointerException at scala.collection.immutable.RedBlackTree$.joinRight(RedBlackTree.scala:1131) at scala.collection.immutable.RedBlackTree$.joinRight(RedBlackTree.scala:1131) at scala.collection.immutable.RedBlackTree$.scala$collection$immutable$RedBlackTree$$join(RedBlackTree.scala:1161) at scala.collection.immutable.RedBlackTree$.doFrom(RedBlackTree.scala:441) at scala.collection.immutable.RedBlackTree$.from(RedBlackTree.scala:184) at scala.collection.immutable.RedBlackTree$.rangeImpl(RedBlackTree.scala:179) at scala.collection.immutable.TreeSet.rangeImpl(TreeSet.scala:155) at scala.collection.immutable.TreeSet.rangeImpl(TreeSet.scala:39) at scala.collection.SortedOps.rangeFrom(SortedOps.scala:65) at scala.collection.SortedOps.rangeFrom$(SortedOps.scala:65) at scala.collection.immutable.TreeSet.rangeFrom(TreeSet.scala:39)