PageRenderTime 57ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/System/System.Collections.Generic/RBTree.cs

https://bitbucket.org/cosi2/dotnetanywhere-wb
C# | 675 lines | 544 code | 104 blank | 27 comment | 176 complexity | e8cbdefc6e89b33ca382f2e8bc52c9dd MD5 | raw file
  1. //#define ONE_MEMBER_CACHE
  2. #if NET_2_0
  3. using System;
  4. using System.Collections;
  5. namespace System.Collections.Generic
  6. {
  7. // [Serializable]
  8. internal class RBTree : IEnumerable, IEnumerable<RBTree.Node> {
  9. public interface INodeHelper<T> {
  10. int Compare (T key, Node node);
  11. Node CreateNode (T key);
  12. }
  13. public abstract class Node {
  14. public Node left, right;
  15. uint size_black;
  16. const uint black_mask = 1;
  17. const int black_shift = 1;
  18. public bool IsBlack {
  19. get { return (size_black & black_mask) == black_mask; }
  20. set { size_black = value ? (size_black | black_mask) : (size_black & ~black_mask); }
  21. }
  22. public uint Size {
  23. get { return size_black >> black_shift; }
  24. set { size_black = (value << black_shift) | (size_black & black_mask); }
  25. }
  26. public uint FixSize ()
  27. {
  28. Size = 1;
  29. if (left != null)
  30. Size += left.Size;
  31. if (right != null)
  32. Size += right.Size;
  33. return Size;
  34. }
  35. public Node ()
  36. {
  37. size_black = 2; // Size == 1, IsBlack = false
  38. }
  39. public abstract void SwapValue (Node other);
  40. #if TEST
  41. public int VerifyInvariants ()
  42. {
  43. int black_depth_l = 0;
  44. int black_depth_r = 0;
  45. uint size = 1;
  46. bool child_is_red = false;
  47. if (left != null) {
  48. black_depth_l = left.VerifyInvariants ();
  49. size += left.Size;
  50. child_is_red |= !left.IsBlack;
  51. }
  52. if (right != null) {
  53. black_depth_r = right.VerifyInvariants ();
  54. size += right.Size;
  55. child_is_red |= !right.IsBlack;
  56. }
  57. if (black_depth_l != black_depth_r)
  58. throw new SystemException ("Internal error: black depth mismatch");
  59. if (!IsBlack && child_is_red)
  60. throw new SystemException ("Internal error: red-red conflict");
  61. if (Size != size)
  62. throw new SystemException ("Internal error: metadata error");
  63. return black_depth_l + (IsBlack ? 1 : 0);
  64. }
  65. public abstract void Dump (string indent);
  66. #endif
  67. }
  68. Node root;
  69. object hlp;
  70. uint version;
  71. #if ONE_MEMBER_CACHE
  72. #if TARGET_JVM
  73. static readonly LocalDataStoreSlot _cachedPathStore = System.Threading.Thread.AllocateDataSlot ();
  74. static List<Node> cached_path {
  75. get { return (List<Node>) System.Threading.Thread.GetData (_cachedPathStore); }
  76. set { System.Threading.Thread.SetData (_cachedPathStore, value); }
  77. }
  78. #else
  79. [ThreadStatic]
  80. static List<Node> cached_path;
  81. #endif
  82. static List<Node> alloc_path ()
  83. {
  84. if (cached_path == null)
  85. return new List<Node> ();
  86. List<Node> path = cached_path;
  87. cached_path = null;
  88. return path;
  89. }
  90. static void release_path (List<Node> path)
  91. {
  92. if (cached_path == null || cached_path.Capacity < path.Capacity) {
  93. path.Clear ();
  94. cached_path = path;
  95. }
  96. }
  97. #else
  98. static List<Node> alloc_path ()
  99. {
  100. return new List<Node> ();
  101. }
  102. static void release_path (List<Node> path)
  103. {
  104. }
  105. #endif
  106. public RBTree (object hlp)
  107. {
  108. // hlp is INodeHelper<T> for some T
  109. this.hlp = hlp;
  110. }
  111. public void Clear ()
  112. {
  113. root = null;
  114. ++version;
  115. }
  116. // if key is already in the tree, return the node associated with it
  117. // if not, insert new_node into the tree, and return it
  118. public Node Intern<T> (T key, Node new_node)
  119. {
  120. if (root == null) {
  121. if (new_node == null)
  122. new_node = ((INodeHelper<T>) hlp).CreateNode (key);
  123. root = new_node;
  124. root.IsBlack = true;
  125. ++version;
  126. return root;
  127. }
  128. List<Node> path = alloc_path ();
  129. int in_tree_cmp = find_key (key, path);
  130. Node retval = path [path.Count - 1];
  131. if (retval == null) {
  132. if (new_node == null)
  133. new_node = ((INodeHelper<T>) hlp).CreateNode (key);
  134. retval = do_insert (in_tree_cmp, new_node, path);
  135. }
  136. // no need for a try .. finally, this is only used to mitigate allocations
  137. release_path (path);
  138. return retval;
  139. }
  140. // returns the just-removed node (or null if the value wasn't in the tree)
  141. public Node Remove<T> (T key)
  142. {
  143. if (root == null)
  144. return null;
  145. List<Node> path = alloc_path ();
  146. int in_tree_cmp = find_key (key, path);
  147. Node retval = null;
  148. if (in_tree_cmp == 0)
  149. retval = do_remove (path);
  150. // no need for a try .. finally, this is only used to mitigate allocations
  151. release_path (path);
  152. return retval;
  153. }
  154. public Node Lookup<T> (T key)
  155. {
  156. INodeHelper<T> hlp = (INodeHelper<T>) this.hlp;
  157. Node current = root;
  158. while (current != null) {
  159. int c = hlp.Compare (key, current);
  160. if (c == 0)
  161. break;
  162. current = c < 0 ? current.left : current.right;
  163. }
  164. return current;
  165. }
  166. public void Bound<T> (T key, ref Node lower, ref Node upper)
  167. {
  168. INodeHelper<T> hlp = (INodeHelper<T>) this.hlp;
  169. Node current = root;
  170. while (current != null) {
  171. int c = hlp.Compare (key, current);
  172. if (c <= 0)
  173. upper = current;
  174. if (c >= 0)
  175. lower = current;
  176. if (c == 0)
  177. break;
  178. current = c < 0 ? current.left : current.right;
  179. }
  180. }
  181. public int Count {
  182. get { return root == null ? 0 : (int) root.Size; }
  183. }
  184. public Node this [int index] {
  185. get {
  186. if (index < 0 || index >= Count)
  187. throw new IndexOutOfRangeException ("index");
  188. Node current = root;
  189. while (current != null) {
  190. int left_size = current.left == null ? 0 : (int) current.left.Size;
  191. if (index == left_size)
  192. return current;
  193. if (index < left_size) {
  194. current = current.left;
  195. } else {
  196. index -= left_size + 1;
  197. current = current.right;
  198. }
  199. }
  200. throw new SystemException ("Internal Error: index calculation");
  201. }
  202. }
  203. public NodeEnumerator GetEnumerator ()
  204. {
  205. return new NodeEnumerator (this);
  206. }
  207. // Get an enumerator that starts at 'key' or the next higher element in the tree
  208. public NodeEnumerator GetSuffixEnumerator<T> (T key)
  209. {
  210. var pennants = new Stack<Node> ();
  211. INodeHelper<T> hlp = (INodeHelper<T>) this.hlp;
  212. Node current = root;
  213. while (current != null) {
  214. int c = hlp.Compare (key, current);
  215. if (c <= 0)
  216. pennants.Push (current);
  217. if (c == 0)
  218. break;
  219. current = c < 0 ? current.left : current.right;
  220. }
  221. return new NodeEnumerator (this, pennants);
  222. }
  223. IEnumerator<Node> IEnumerable<Node>.GetEnumerator ()
  224. {
  225. return GetEnumerator ();
  226. }
  227. IEnumerator IEnumerable.GetEnumerator ()
  228. {
  229. return GetEnumerator ();
  230. }
  231. #if TEST
  232. public void VerifyInvariants ()
  233. {
  234. if (root != null) {
  235. if (!root.IsBlack)
  236. throw new SystemException ("Internal Error: root is not black");
  237. root.VerifyInvariants ();
  238. }
  239. }
  240. public void Dump ()
  241. {
  242. if (root != null)
  243. root.Dump ("");
  244. }
  245. #endif
  246. // Pre-condition: root != null
  247. int find_key<T> (T key, List<Node> path)
  248. {
  249. INodeHelper<T> hlp = (INodeHelper<T>) this.hlp;
  250. int c = 0;
  251. Node sibling = null;
  252. Node current = root;
  253. if (path != null)
  254. path.Add (root);
  255. while (current != null) {
  256. c = hlp.Compare (key, current);
  257. if (c == 0)
  258. return c;
  259. if (c < 0) {
  260. sibling = current.right;
  261. current = current.left;
  262. } else {
  263. sibling = current.left;
  264. current = current.right;
  265. }
  266. if (path != null) {
  267. path.Add (sibling);
  268. path.Add (current);
  269. }
  270. }
  271. return c;
  272. }
  273. Node do_insert (int in_tree_cmp, Node current, List<Node> path)
  274. {
  275. path [path.Count - 1] = current;
  276. Node parent = path [path.Count - 3];
  277. if (in_tree_cmp < 0)
  278. parent.left = current;
  279. else
  280. parent.right = current;
  281. for (int i = 0; i < path.Count - 2; i += 2)
  282. ++ path [i].Size;
  283. if (!parent.IsBlack)
  284. rebalance_insert (path);
  285. if (!root.IsBlack)
  286. throw new SystemException ("Internal error: root is not black");
  287. ++version;
  288. return current;
  289. }
  290. Node do_remove (List<Node> path)
  291. {
  292. int curpos = path.Count - 1;
  293. Node current = path [curpos];
  294. if (current.left != null) {
  295. Node pred = right_most (current.left, current.right, path);
  296. current.SwapValue (pred);
  297. if (pred.left != null) {
  298. Node ppred = pred.left;
  299. path.Add (null); path.Add (ppred);
  300. pred.SwapValue (ppred);
  301. }
  302. } else if (current.right != null) {
  303. Node succ = current.right;
  304. path.Add (null); path.Add (succ);
  305. current.SwapValue (succ);
  306. }
  307. curpos = path.Count - 1;
  308. current = path [curpos];
  309. if (current.Size != 1)
  310. throw new SystemException ("Internal Error: red-black violation somewhere");
  311. // remove it from our data structures
  312. path [curpos] = null;
  313. node_reparent (curpos == 0 ? null : path [curpos-2], current, 0, null);
  314. for (int i = 0; i < path.Count - 2; i += 2)
  315. -- path [i].Size;
  316. if (current.IsBlack) {
  317. current.IsBlack = false;
  318. if (curpos != 0)
  319. rebalance_delete (path);
  320. }
  321. if (root != null && !root.IsBlack)
  322. throw new SystemException ("Internal Error: root is not black");
  323. ++version;
  324. return current;
  325. }
  326. // Pre-condition: current is red
  327. void rebalance_insert (List<Node> path)
  328. {
  329. int curpos = path.Count - 1;
  330. do {
  331. // parent == curpos-2, uncle == curpos-3, grandpa == curpos-4
  332. if (path [curpos-3] == null || path [curpos-3].IsBlack) {
  333. rebalance_insert__rotate_final (curpos, path);
  334. return;
  335. }
  336. path [curpos-2].IsBlack = path [curpos-3].IsBlack = true;
  337. curpos -= 4; // move to the grandpa
  338. if (curpos == 0) // => current == root
  339. return;
  340. path [curpos].IsBlack = false;
  341. } while (!path [curpos-2].IsBlack);
  342. }
  343. // Pre-condition: current is black
  344. void rebalance_delete (List<Node> path)
  345. {
  346. int curpos = path.Count - 1;
  347. do {
  348. Node sibling = path [curpos-1];
  349. // current is black => sibling != null
  350. if (!sibling.IsBlack) {
  351. // current is black && sibling is red
  352. // => both sibling.left and sibling.right are black, and are not null
  353. curpos = ensure_sibling_black (curpos, path);
  354. // one of the nephews became the new sibling -- in either case, sibling != null
  355. sibling = path [curpos-1];
  356. }
  357. if ((sibling.left != null && !sibling.left.IsBlack) ||
  358. (sibling.right != null && !sibling.right.IsBlack)) {
  359. rebalance_delete__rotate_final (curpos, path);
  360. return;
  361. }
  362. sibling.IsBlack = false;
  363. curpos -= 2; // move to the parent
  364. if (curpos == 0)
  365. return;
  366. } while (path [curpos].IsBlack);
  367. path [curpos].IsBlack = true;
  368. }
  369. void rebalance_insert__rotate_final (int curpos, List<Node> path)
  370. {
  371. Node current = path [curpos];
  372. Node parent = path [curpos-2];
  373. Node grandpa = path [curpos-4];
  374. uint grandpa_size = grandpa.Size;
  375. Node new_root;
  376. bool l1 = parent == grandpa.left;
  377. bool l2 = current == parent.left;
  378. if (l1 && l2) {
  379. grandpa.left = parent.right; parent.right = grandpa;
  380. new_root = parent;
  381. } else if (l1 && !l2) {
  382. grandpa.left = current.right; current.right = grandpa;
  383. parent.right = current.left; current.left = parent;
  384. new_root = current;
  385. } else if (!l1 && l2) {
  386. grandpa.right = current.left; current.left = grandpa;
  387. parent.left = current.right; current.right = parent;
  388. new_root = current;
  389. } else { // (!l1 && !l2)
  390. grandpa.right = parent.left; parent.left = grandpa;
  391. new_root = parent;
  392. }
  393. grandpa.FixSize (); grandpa.IsBlack = false;
  394. if (new_root != parent)
  395. parent.FixSize (); /* parent is red already, so no need to set it */
  396. new_root.IsBlack = true;
  397. node_reparent (curpos == 4 ? null : path [curpos-6], grandpa, grandpa_size, new_root);
  398. }
  399. // Pre-condition: sibling is black, and one of sibling.left and sibling.right is red
  400. void rebalance_delete__rotate_final (int curpos, List<Node> path)
  401. {
  402. //Node current = path [curpos];
  403. Node sibling = path [curpos-1];
  404. Node parent = path [curpos-2];
  405. uint parent_size = parent.Size;
  406. bool parent_was_black = parent.IsBlack;
  407. Node new_root;
  408. if (parent.right == sibling) {
  409. // if far nephew is black
  410. if (sibling.right == null || sibling.right.IsBlack) {
  411. // => near nephew is red, move it up
  412. Node nephew = sibling.left;
  413. parent.right = nephew.left; nephew.left = parent;
  414. sibling.left = nephew.right; nephew.right = sibling;
  415. new_root = nephew;
  416. } else {
  417. parent.right = sibling.left; sibling.left = parent;
  418. sibling.right.IsBlack = true;
  419. new_root = sibling;
  420. }
  421. } else {
  422. // if far nephew is black
  423. if (sibling.left == null || sibling.left.IsBlack) {
  424. // => near nephew is red, move it up
  425. Node nephew = sibling.right;
  426. parent.left = nephew.right; nephew.right = parent;
  427. sibling.right = nephew.left; nephew.left = sibling;
  428. new_root = nephew;
  429. } else {
  430. parent.left = sibling.right; sibling.right = parent;
  431. sibling.left.IsBlack = true;
  432. new_root = sibling;
  433. }
  434. }
  435. parent.FixSize (); parent.IsBlack = true;
  436. if (new_root != sibling)
  437. sibling.FixSize (); /* sibling is already black, so no need to set it */
  438. new_root.IsBlack = parent_was_black;
  439. node_reparent (curpos == 2 ? null : path [curpos-4], parent, parent_size, new_root);
  440. }
  441. // Pre-condition: sibling is red (=> parent, sibling.left and sibling.right are black)
  442. int ensure_sibling_black (int curpos, List<Node> path)
  443. {
  444. Node current = path [curpos];
  445. Node sibling = path [curpos-1];
  446. Node parent = path [curpos-2];
  447. bool current_on_left;
  448. uint parent_size = parent.Size;
  449. if (parent.right == sibling) {
  450. parent.right = sibling.left; sibling.left = parent;
  451. current_on_left = true;
  452. } else {
  453. parent.left = sibling.right; sibling.right = parent;
  454. current_on_left = false;
  455. }
  456. parent.FixSize (); parent.IsBlack = false;
  457. sibling.IsBlack = true;
  458. node_reparent (curpos == 2 ? null : path [curpos-4], parent, parent_size, sibling);
  459. // accomodate the rotation
  460. if (curpos+1 == path.Count) {
  461. path.Add (null);
  462. path.Add (null);
  463. }
  464. path [curpos-2] = sibling;
  465. path [curpos-1] = current_on_left ? sibling.right : sibling.left;
  466. path [curpos] = parent;
  467. path [curpos+1] = current_on_left ? parent.right : parent.left;
  468. path [curpos+2] = current;
  469. return curpos + 2;
  470. }
  471. void node_reparent (Node orig_parent, Node orig, uint orig_size, Node updated)
  472. {
  473. if (updated != null && updated.FixSize () != orig_size)
  474. throw new SystemException ("Internal error: rotation");
  475. if (orig == root)
  476. root = updated;
  477. else if (orig == orig_parent.left)
  478. orig_parent.left = updated;
  479. else if (orig == orig_parent.right)
  480. orig_parent.right = updated;
  481. else
  482. throw new SystemException ("Internal error: path error");
  483. }
  484. // Pre-condition: current != null
  485. static Node right_most (Node current, Node sibling, List<Node> path)
  486. {
  487. for (;;) {
  488. path.Add (sibling);
  489. path.Add (current);
  490. if (current.right == null)
  491. return current;
  492. sibling = current.left;
  493. current = current.right;
  494. }
  495. }
  496. [Serializable]
  497. public struct NodeEnumerator : IEnumerator, IEnumerator<Node> {
  498. RBTree tree;
  499. uint version;
  500. Stack<Node> pennants, init_pennants;
  501. internal NodeEnumerator (RBTree tree)
  502. : this ()
  503. {
  504. this.tree = tree;
  505. version = tree.version;
  506. }
  507. internal NodeEnumerator (RBTree tree, Stack<Node> init_pennants)
  508. : this (tree)
  509. {
  510. this.init_pennants = init_pennants;
  511. }
  512. public void Reset ()
  513. {
  514. check_version ();
  515. pennants = null;
  516. }
  517. public Node Current {
  518. get { return pennants.Peek (); }
  519. }
  520. object IEnumerator.Current {
  521. get {
  522. check_current ();
  523. return Current;
  524. }
  525. }
  526. public bool MoveNext ()
  527. {
  528. check_version ();
  529. Node next;
  530. if (pennants == null) {
  531. if (tree.root == null)
  532. return false;
  533. if (init_pennants != null) {
  534. pennants = init_pennants;
  535. init_pennants = null;
  536. return pennants.Count != 0;
  537. }
  538. pennants = new Stack<Node> ();
  539. next = tree.root;
  540. } else {
  541. if (pennants.Count == 0)
  542. return false;
  543. Node current = pennants.Pop ();
  544. next = current.right;
  545. }
  546. for (; next != null; next = next.left)
  547. pennants.Push (next);
  548. return pennants.Count != 0;
  549. }
  550. public void Dispose ()
  551. {
  552. tree = null;
  553. pennants = null;
  554. }
  555. void check_version ()
  556. {
  557. if (tree == null)
  558. throw new ObjectDisposedException ("enumerator");
  559. if (version != tree.version)
  560. throw new InvalidOperationException ("tree modified");
  561. }
  562. internal void check_current ()
  563. {
  564. check_version ();
  565. if (pennants == null)
  566. throw new InvalidOperationException ("state invalid before the first MoveNext()");
  567. }
  568. }
  569. }
  570. }
  571. #endif