Skip to content

Validate (and fix) the RedBlackTree invariants#9326

Closed
mkeskells wants to merge 4 commits intoscala:2.12.xfrom
mkeskells:2.12.x_RedBlackValidate
Closed

Validate (and fix) the RedBlackTree invariants#9326
mkeskells wants to merge 4 commits intoscala:2.12.xfrom
mkeskells:2.12.x_RedBlackValidate

Conversation

@mkeskells
Copy link
Contributor

@mkeskells mkeskells commented Nov 18, 2020

It seems that we were generating invalid RedBlack trees

This PR add validation to test RedBlackTrees against the rules, fixes the trees that were invalid, and adds some debug support for detecting errors during development (commented out)

Other PRs will optimise these generated trees

Thanks to @retronym I have incorporated some of the debugging utilities from retronym#107

@scala-jenkins scala-jenkins added this to the 2.12.14 milestone Nov 18, 2020
@mkeskells mkeskells marked this pull request as draft November 18, 2020 23:23
@mkeskells mkeskells force-pushed the 2.12.x_RedBlackValidate branch from 1e4d1a7 to 5b8d9ae Compare November 18, 2020 23:27
@SethTisue
Copy link
Member

SethTisue commented Nov 19, 2020

is this a 2.12.13 blocker? (cc @retronym)

@mkeskells
Copy link
Contributor Author

is this a 2.12.13 blocker? (cc @retronym)

I don't think that this has changed. We could test it in 2.12.12 release
I just raised this to see what broke, other than the test, and to discuss

@retronym
Copy link
Member

We believe this is a long standing behaviour. I'll look into it more today to confirm this -- if so it isn't a regression and wouldn't block 2.12.13. I'll also try to see if there it can actually manifest as incorrect behaviour.

@retronym
Copy link
Member

Okay, now I've refreshed my memory on Red Black Trees, I believe that the user-facing impact is "only" a potential performance problem where the worst case depth of the tree could be deeper than the guaranteed 2 x log(N). Potentially under a pathological case of the errant operations a search could degrade to O(N).

So far, @mkeskells has the "red cannot have a red parent" invariant can be broken in to/from/until/range. I'm not sure how badly this is broken by a single instance of that operation. If it is 2 x log(N) + 1, the performance problem isn't practically important. It is even possible that that invariant is broken intentionally to speed up those operations. An attempt to make them respect the invariant should consider this and benchmark the impact of the change.

@retronym
Copy link
Member

So I'm happy to defer a full investigation and fix for this until after 2.12.13.

@mkeskells
Copy link
Contributor Author

Okay, now I've refreshed my memory on Red Black Trees, I believe that the user-facing impact is "only" a potential performance problem where the worst case depth of the tree could be deeper than the guaranteed 2 x log(N). Potentially under a pathological case of the errant operations a search could degrade to O(N).

If we just use the invariants from retronyms memory there are more problems in the tree. The additional in the current code and the other references is that the top Tree is black (but I also note that some implementations ignore this for performance)
If we don't blacken the root, then the test code indicates errors in delete and the related init and tail
To see this - in this PR change validate (tree) to validate(in)

So far, @mkeskells has the "red cannot have a red parent" invariant can be broken in to/from/until/range. I'm not sure how badly this is broken by a single instance of that operation. If it is 2 x log(N) + 1, the performance problem isn't practically important. It is even possible that that invariant is broken intentionally to speed up those operations. An attempt to make them respect the invariant should consider this and benchmark the impact of the change.

In these (very limited tests) I don't see the "black-height" invariant broken (changing the root colour would not affect this). This is strange, as this was what I expected to see when I wrote the test as when looking at removal of an absent key. It looked from https://github.com/scala/scala/pull/9316/files#diff-84e7a7e45217aae6451467950e17274e241e1a672df4ad572334fd0e7104df98L1069 that the colour of the Tree was changed (it certainly reallocates unnecessarily which was the subject of the PR)
Originally the code here to fix was

if (isBlackTree(tree.left)) balLeft(tree, del(tree.left, k), tree.right)
else tree.redWithLeft(del(tree.left, k))

but this still allocates, so I added the identity check. This presumes that the colour of the leaf node was previously changing, which I am not seeing in the current test

so either the tests are not good enough , or the presumption was wrong. My guess is the former

@retronym
Copy link
Member

retronym commented Nov 20, 2020

I've added a pretty-printer for the internal structure of the RedBlack tree and some diagnostic code the record an ID and the creation stack trace of each node. These give a fuller picture of what the errant final tree looks like and what line of code created the errant subtree.

retronym#107

/Users/jz/.jabba/jdk/adopt@1.8.0-272/Contents/Home/bin/java -ea -Didea.test.cyclic.buffer.size=1048576 -javaagent:/Users/jz/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/203.5784.10/IntelliJ IDEA 2020.3 EAP.app/Contents/lib/idea_rt.jar=55222:/Users/jz/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/203.5784.10/IntelliJ IDEA 2020.3 EAP.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Users/jz/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/203.5784.10/IntelliJ IDEA 2020.3 EAP.app/Contents/lib/idea_rt.jar:/Users/jz/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/203.5784.10/IntelliJ IDEA 2020.3 EAP.app/Contents/plugins/junit/lib/junit5-rt.jar:/Users/jz/Library/Application Support/JetBrains/Toolbox/apps/IDEA-U/ch-0/203.5784.10/IntelliJ IDEA 2020.3 EAP.app/Contents/plugins/junit/lib/junit-rt.jar:/Users/jz/.jabba/jdk/adopt@1.8.0-272/Contents/Home/jre/lib/charsets.jar:/Users/jz/.jabba/jdk/adopt@1.8.0-272/Contents/Home/jre/lib/ext/cldrdata.jar:/Users/jz/.jabba/jdk/adopt@1.8.0-272/Contents/Home/jre/lib/ext/dnsns.jar:/Users/jz/.jabba/jdk/adopt@1.8.0-272/Contents/Home/jre/lib/ext/jaccess.jar:/Users/jz/.jabba/jdk/adopt@1.8.0-272/Contents/Home/jre/lib/ext/localedata.jar:/Users/jz/.jabba/jdk/adopt@1.8.0-272/Contents/Home/jre/lib/ext/nashorn.jar:/Users/jz/.jabba/jdk/adopt@1.8.0-272/Contents/Home/jre/lib/ext/sunec.jar:/Users/jz/.jabba/jdk/adopt@1.8.0-272/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Users/jz/.jabba/jdk/adopt@1.8.0-272/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Users/jz/.jabba/jdk/adopt@1.8.0-272/Contents/Home/jre/lib/ext/zipfs.jar:/Users/jz/.jabba/jdk/adopt@1.8.0-272/Contents/Home/jre/lib/jce.jar:/Users/jz/.jabba/jdk/adopt@1.8.0-272/Contents/Home/jre/lib/jfr.jar:/Users/jz/.jabba/jdk/adopt@1.8.0-272/Contents/Home/jre/lib/jsse.jar:/Users/jz/.jabba/jdk/adopt@1.8.0-272/Contents/Home/jre/lib/management-agent.jar:/Users/jz/.jabba/jdk/adopt@1.8.0-272/Contents/Home/jre/lib/resources.jar:/Users/jz/.jabba/jdk/adopt@1.8.0-272/Contents/Home/jre/lib/rt.jar:/Users/jz/.jabba/jdk/adopt@1.8.0-272/Contents/Home/lib/dt.jar:/Users/jz/.jabba/jdk/adopt@1.8.0-272/Contents/Home/lib/jconsole.jar:/Users/jz/.jabba/jdk/adopt@1.8.0-272/Contents/Home/lib/sa-jdi.jar:/Users/jz/.jabba/jdk/adopt@1.8.0-272/Contents/Home/lib/tools.jar:/Users/jz/code/scala/target/junit/test-classes:/Users/jz/code/scala/build/quick/classes/library:/Users/jz/code/scala/build/quick/classes/reflect:/Users/jz/code/scala/build/quick/classes/compiler:/Users/jz/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/apache/ant/ant/1.9.4/ant-1.9.4.jar:/Users/jz/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/jline/jline/2.14.6/jline-2.14.6.jar:/Users/jz/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/modules/scala-xml_2.12/1.0.6/scala-xml_2.12-1.0.6.jar:/Users/jz/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/fusesource/jansi/jansi/1.12/jansi-1.12.jar:/Users/jz/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/apache/ant/ant-launcher/1.9.4/ant-launcher-1.9.4.jar:/Users/jz/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/modules/scala-asm/7.3.1-scala-1/scala-asm-7.3.1-scala-1.jar:/Users/jz/code/scala/build/quick/classes/repl:/Users/jz/code/scala/build/quick/classes/interactive:/Users/jz/code/scala/build/quick/classes/scaladoc:/Users/jz/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/webjars/jquery/3.5.1/jquery-3.5.1.jar:/Users/jz/code/scala/build/quick/classes/partest:/Users/jz/code/scala/build/quick/classes/scalap:/Users/jz/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/scala-sbt/test-interface/1.0/test-interface-1.0.jar:/Users/jz/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/com/googlecode/java-diff-utils/diffutils/1.3.0/diffutils-1.3.0.jar:/Users/jz/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/junit/junit/4.12/junit-4.12.jar:/Users/jz/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar:/Users/jz/Library/Caches/Coursier/v1/https/repo1.maven.org/maven2/org/openjdk/jol/jol-core/0.13/jol-core-0.13.jar com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 scala.collection.immutable.TreeMapTest,randomTree

java.lang.IllegalStateException: bad tree:
 BlackTree(49, ())#1633
  BlackTree(32, ())#1278
    BlackTree(13, ())#1274
      RedTree(1, ())#1172
        Empty
        Empty
      Empty
    BlackTree(39, ())#1275
      Empty
      Empty
  RedTree(271, ())#1631
    BlackTree(125, ())#1629
      BlackTree(112, ())#387
        Empty
        Empty
      BlackTree(159, ())#1107
        RedTree(149, ())#1106
          Empty
          Empty
        RedTree(254, ())#905
          Empty
          Empty
    BlackTree(387, ())#1630
      BlackTree(360, ())#1219
        RedTree(307, ())#1218
          Empty
          Empty
        Empty
      RedTree(441, ())#1626 !! Red contains Red
        BlackTree(438, ())#420
          Empty
          Empty
        RedTree(469, ())#1625
          BlackTree(457, ())#1623
            Empty
            Empty
          BlackTree(473, ())#1624
            Empty
            Empty


:expected: 
BlackTree(254, ())#1652
  BlackTree(49, ())#1642
    BlackTree(13, ())#1637
      BlackTree(1, ())#1634
        Empty
        Empty
      BlackTree(32, ())#1636
        Empty
        RedTree(39, ())#1635
          Empty
          Empty
    BlackTree(125, ())#1641
      BlackTree(112, ())#1638
        Empty
        Empty
      BlackTree(149, ())#1640
        Empty
        RedTree(159, ())#1639
          Empty
          Empty
  BlackTree(438, ())#1651
    BlackTree(307, ())#1646
      BlackTree(271, ())#1643
        Empty
        Empty
      BlackTree(360, ())#1645
        Empty
        RedTree(387, ())#1644
          Empty
          Empty
    BlackTree(457, ())#1650
      BlackTree(441, ())#1647
        Empty
        Empty
      BlackTree(469, ())#1649
        Empty
        RedTree(473, ())#1648
          Empty
          Empty
.

original input tree|b: BlackTree(616, ())#1595
  BlackTree(387, ())#1594
    RedTree(49, ())#1280
      BlackTree(32, ())#1278
        BlackTree(13, ())#1274
          RedTree(1, ())#1172
            Empty
            Empty
          Empty
        BlackTree(39, ())#1275
          Empty
          Empty
      BlackTree(125, ())#1279
        BlackTree(112, ())#387
          Empty
          Empty
        RedTree(271, ())#1220
          BlackTree(159, ())#1107
            RedTree(149, ())#1106
              Empty
              Empty
            RedTree(254, ())#905
              Empty
              Empty
          BlackTree(360, ())#1219
            RedTree(307, ())#1218
              Empty
              Empty
            Empty
    BlackTree(473, ())#1593
      RedTree(441, ())#1592
        BlackTree(438, ())#420
          Empty
          Empty
        BlackTree(457, ())#1591
          Empty
          RedTree(469, ())#1590
            Empty
            Empty
      RedTree(577, ())#1483
        BlackTree(546, ())#648
          Empty
          Empty
        BlackTree(584, ())#1482
          RedTree(580, ())#1481
            Empty
            Empty
          RedTree(615, ())#739
            Empty
            Empty
  BlackTree(794, ())#1552
    RedTree(721, ())#1441
      BlackTree(688, ())#1439
        BlackTree(643, ())#1384
          RedTree(640, ())#1383
            Empty
            Empty
          Empty
        BlackTree(693, ())#1435
          Empty
          Empty
      BlackTree(767, ())#1440
        BlackTree(758, ())#1436
          Empty
          Empty
        BlackTree(786, ())#946
          Empty
          Empty
    BlackTree(905, ())#1551
      RedTree(888, ())#1342
        BlackTree(814, ())#1340
          Empty
          Empty
        BlackTree(889, ())#1341
          Empty
          Empty
      RedTree(986, ())#1550
        BlackTree(957, ())#1549
          Empty
          RedTree(971, ())#1548
            Empty
            Empty
        BlackTree(999, ())#806
          Empty
          Empty


	at scala.collection.immutable.NewRedBlackTree$.scala$collection$immutable$NewRedBlackTree$$validate(RedBlackTree.scala:273)
	at scala.collection.immutable.NewRedBlackTree$.result(RedBlackTree.scala:232)
	at scala.collection.immutable.NewRedBlackTree$.to(RedBlackTree.scala:192)
	at scala.collection.immutable.TreeSet.to(TreeSet.scala:243)
	at scala.collection.immutable.TreeMapTest.$anonfun$randomTree$1(TreeMapTest.scala:117)
	at scala.collection.immutable.TreeMapTest.$anonfun$randomTree$1$adapted(TreeMapTest.scala:107)
	at scala.collection.immutable.Range.foreach(Range.scala:158)
	at scala.collection.immutable.TreeMapTest.randomTree(TreeMapTest.scala:107)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)
Caused by: java.lang.IllegalStateException: at RedTree#1626 (k=441) right is also red 469
	at scala.collection.immutable.NewRedBlackTree$.checkAdjacentRed$1(RedBlackTree.scala:247)
	at scala.collection.immutable.NewRedBlackTree$.scala$collection$immutable$NewRedBlackTree$$validate(RedBlackTree.scala:267)
	... 29 more
Caused by: java.lang.Throwable: tree created stack trace
	at scala.collection.immutable.NewRedBlackTree$Tree.<init>(RedBlackTree.scala:619)
	at scala.collection.immutable.NewRedBlackTree$Tree.withRight(RedBlackTree.scala:787)
	at scala.collection.immutable.NewRedBlackTree$.balanceRight(RedBlackTree.scala:445)
	at scala.collection.immutable.NewRedBlackTree$.upd(RedBlackTree.scala:467)
	at scala.collection.immutable.NewRedBlackTree$.doTo(RedBlackTree.scala:499)
	at scala.collection.immutable.NewRedBlackTree$.doTo(RedBlackTree.scala:497)
	at scala.collection.immutable.NewRedBlackTree$.doTo(RedBlackTree.scala:496)
	... 28 more

Notice that the "bad' tree structural shares part of the original input tree. This might be the motivation for weakening the invariants for to. TreeMap(1 to 255 : _*).to(254) is expected to be fast. It's a pity that these implementation details aren't documented. Ping @axel22 for a trip down memory lane?

@mkeskells
Copy link
Contributor Author

if (isBlackTree(tree.left)) balLeft(tree, del(tree.left, k), tree.right)
else tree.redWithLeft(del(tree.left, k))

Looking at the code path here - this doesn't change the colour of the Tree
The code generates a Red node and then when the recursion pops to the next level it throws that away and creates a new black node
so inefficient, but not breaking the rules

fix where the tree was invalid
@mkeskells mkeskells force-pushed the 2.12.x_RedBlackValidate branch from 5b8d9ae to b962cf2 Compare November 23, 2020 22:18
@mkeskells mkeskells changed the title validate the RedBlackTree invariants Validate (and fix) the RedBlackTree invariants Nov 23, 2020
@mkeskells mkeskells marked this pull request as ready for review November 23, 2020 22:26
Example:

```
val s1 = TreeSet((1 to 64): _*)
s1.range(2, 63 + 1).tree.debugToString(Some(s1.tree))
```

```
BlackTree(32)
  BlackTree(24)
    RedTree(16)
      BlackTree(12)
        RedTree(8)
          BlackTree(6)
            RedTree(4)
              BlackTree(3)
                RedTree(2)
                  Empty
                  Empty
                Empty
              BlackTree(5) [shared]
                Empty
                Empty
            BlackTree(7) [shared]
              Empty
              Empty
          BlackTree(10) [shared]
            BlackTree(9) [shared]
              Empty
              Empty
            BlackTree(11) [shared]
              Empty
              Empty
        BlackTree(14) [shared]
          BlackTree(13) [shared]
            Empty
            Empty
          BlackTree(15) [shared]
            Empty
            Empty
      BlackTree(20) [shared]
        BlackTree(18) [shared]
          BlackTree(17) [shared]
            Empty
            Empty
          BlackTree(19) [shared]
            Empty
            Empty
        BlackTree(22) [shared]
          BlackTree(21) [shared]
            Empty
            Empty
          BlackTree(23) [shared]
            Empty
            Empty
    BlackTree(28) [shared]
      BlackTree(26) [shared]
        BlackTree(25) [shared]
          Empty
          Empty
        BlackTree(27) [shared]
          Empty
          Empty
      BlackTree(30) [shared]
        BlackTree(29) [shared]
          Empty
          Empty
        BlackTree(31) [shared]
          Empty
          Empty
  RedTree(48)
    BlackTree(40)
      BlackTree(36) [shared]
        BlackTree(34) [shared]
          BlackTree(33) [shared]
            Empty
            Empty
          BlackTree(35) [shared]
            Empty
            Empty
        BlackTree(38) [shared]
          BlackTree(37) [shared]
            Empty
            Empty
          BlackTree(39) [shared]
            Empty
            Empty
      BlackTree(44) [shared]
        BlackTree(42) [shared]
          BlackTree(41) [shared]
            Empty
            Empty
          BlackTree(43) [shared]
            Empty
            Empty
        BlackTree(46) [shared]
          BlackTree(45) [shared]
            Empty
            Empty
          BlackTree(47) [shared]
            Empty
            Empty
    BlackTree(56)
      BlackTree(52)
        BlackTree(50) [shared]
          BlackTree(49) [shared]
            Empty
            Empty
          BlackTree(51) [shared]
            Empty
            Empty
        BlackTree(54) [shared]
          BlackTree(53) [shared]
            Empty
            Empty
          BlackTree(55) [shared]
            Empty
            Empty
      BlackTree(60)
        BlackTree(58)
          BlackTree(57) [shared]
            Empty
            Empty
          BlackTree(59) [shared]
            Empty
            Empty
        BlackTree(62)
          BlackTree(61)
            Empty
            Empty
          BlackTree(63)
            Empty
            Empty

```
@retronym
Copy link
Member

@mkeskells As discussed, I've pushed a commit that highlights structural sharing. I have also used this in a test case to pin down the amount of sharing we expect from range/from/to/until.

val s1 = TreeSet((1 to 64): _*)
s1.range(2, 63 + 1).tree.debugToString(Some(s1.tree))
BlackTree(32)
  BlackTree(24)
    RedTree(16)
      BlackTree(12)
        RedTree(8)
          BlackTree(6)
            RedTree(4)
              BlackTree(3)
                RedTree(2)
                  Empty
                  Empty
                Empty
              BlackTree(5) [shared]
                Empty
                Empty
            BlackTree(7) [shared]
              Empty
              Empty
          BlackTree(10) [shared]
            BlackTree(9) [shared]
              Empty
              Empty
            BlackTree(11) [shared]
              Empty
              Empty
        BlackTree(14) [shared]
          BlackTree(13) [shared]
            Empty
            Empty
          BlackTree(15) [shared]
            Empty
            Empty
      BlackTree(20) [shared]
        BlackTree(18) [shared]
          BlackTree(17) [shared]
            Empty
            Empty
          BlackTree(19) [shared]
            Empty
            Empty
        BlackTree(22) [shared]
          BlackTree(21) [shared]
            Empty
            Empty
          BlackTree(23) [shared]
            Empty
            Empty
    BlackTree(28) [shared]
      BlackTree(26) [shared]
        BlackTree(25) [shared]
          Empty
          Empty
        BlackTree(27) [shared]
          Empty
          Empty
      BlackTree(30) [shared]
        BlackTree(29) [shared]
          Empty
          Empty
        BlackTree(31) [shared]
          Empty
          Empty
  RedTree(48)
    BlackTree(40)
      BlackTree(36) [shared]
        BlackTree(34) [shared]
          BlackTree(33) [shared]
            Empty
            Empty
          BlackTree(35) [shared]
            Empty
            Empty
        BlackTree(38) [shared]
          BlackTree(37) [shared]
            Empty
            Empty
          BlackTree(39) [shared]
            Empty
            Empty
      BlackTree(44) [shared]
        BlackTree(42) [shared]
          BlackTree(41) [shared]
            Empty
            Empty
          BlackTree(43) [shared]
            Empty
            Empty
        BlackTree(46) [shared]
          BlackTree(45) [shared]
            Empty
            Empty
          BlackTree(47) [shared]
            Empty
            Empty
    BlackTree(56)
      BlackTree(52)
        BlackTree(50) [shared]
          BlackTree(49) [shared]
            Empty
            Empty
          BlackTree(51) [shared]
            Empty
            Empty
        BlackTree(54) [shared]
          BlackTree(53) [shared]
            Empty
            Empty
          BlackTree(55) [shared]
            Empty
            Empty
      BlackTree(60)
        BlackTree(58)
          BlackTree(57) [shared]
            Empty
            Empty
          BlackTree(59) [shared]
            Empty
            Empty
        BlackTree(62)
          BlackTree(61)
            Empty
            Empty
          BlackTree(63)
            Empty
            Empty

@mkeskells mkeskells force-pushed the 2.12.x_RedBlackValidate branch from 468d315 to f831c6f Compare November 25, 2020 08:03
@mkeskells mkeskells force-pushed the 2.12.x_RedBlackValidate branch from f831c6f to 41d12d9 Compare November 25, 2020 18:24
@SethTisue SethTisue added the library:collections PRs involving changes to the standard collection library label Dec 4, 2020
@dwijnand
Copy link
Member

So where does this PR stand now? @retronym can you review the tail of this, or should we look for another reviewer?

@dwijnand
Copy link
Member

Oh, and it conflicts now, probably with something we merged just now.

@SethTisue SethTisue marked this pull request as draft January 26, 2021 17:04
@retronym retronym removed this from the 2.12.14 milestone May 10, 2021
@scala-jenkins scala-jenkins added this to the 2.12.14 milestone May 10, 2021
@retronym retronym removed this from the 2.12.14 milestone May 17, 2021
@scala-jenkins scala-jenkins added this to the 2.12.14 milestone May 17, 2021
@SethTisue SethTisue modified the milestones: 2.12.14, 2.12.15 May 17, 2021
@SethTisue
Copy link
Member

closing for inactivity — we can reopen if activity resumes

@SethTisue SethTisue closed this Jun 2, 2021
@SethTisue SethTisue removed this from the 2.12.15 milestone Jun 2, 2021
@SethTisue
Copy link
Member

I believe that the user-facing impact is "only" a potential performance problem where the worst case depth of the tree could be deeper than the guaranteed 2 x log(N)

@scala/collections anyone interested in (at minimum) opening a scala/bug ticket characterizing the issue, and/or (more ambitiously) resuming work on this...? enough time has passed that it's looking unlikely that either Mike or Jason will return to it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

library:collections PRs involving changes to the standard collection library

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants