├── src └── main │ ├── java │ ├── bbst_showdown │ │ ├── package-info.java │ │ ├── TreeMapAVLRec.java │ │ ├── TreeMapAVLStack.java │ │ ├── AVLTreeMapRB.java │ │ ├── WAVLTreeMap.java │ │ └── AVLTreeMap.java │ └── performanceTests │ │ ├── CountRotations.java │ │ └── Standoff.java │ └── test │ └── bbst_showdown │ ├── AVLTreeMapTest.java │ ├── AVLTreeMapRBTest.java │ └── WAVLTreeMapTest.java ├── pom.xml ├── README.md └── LICENSE /src/main/java/bbst_showdown/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Many AVL tree implementations - balance factor, rank balanced, one-bit extra per node and more. 3 | * 4 | * @author dmcmanam 5 | */ 6 | package bbst_showdown; -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | dmcmanam 6 | bbst-showdown 7 | 0.0.1-SNAPSHOT 8 | jar 9 | 10 | bbst-showdown 11 | http://maven.apache.org 12 | 13 | 14 | UTF-8 15 | 1.8 16 | 1.8 17 | 18 | 19 | 20 | 21 | junit 22 | junit 23 | 4.12 24 | test 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/main/java/performanceTests/CountRotations.java: -------------------------------------------------------------------------------- 1 | package performanceTests; 2 | 3 | import java.util.Map; 4 | 5 | import bbst_showdown.AVLTreeMap; 6 | import bbst_showdown.TreeMapRedBlack; 7 | import bbst_showdown.WAVLTreeMap; 8 | 9 | public class CountRotations { 10 | public static void main(String [] args) { 11 | java.util.Random r = new java.util.Random(); 12 | Integer[] groupedRandomNumbers = new Integer [300000]; 13 | for (int i = 0; i < groupedRandomNumbers.length;) { 14 | Integer nextRand = r.nextInt(Integer.MAX_VALUE - 16); 15 | for (int j=0; j < 16; j++) { 16 | groupedRandomNumbers[i++] = nextRand + j; 17 | } 18 | } 19 | 20 | System.out.println("Results for inserting integer clusters in sequences of 16 and total size: " + groupedRandomNumbers.length + " -"); 21 | 22 | WAVLTreeMap ravl = new WAVLTreeMap<>(); 23 | AVLTreeMap avl = new AVLTreeMap<>(); 24 | TreeMapRedBlack redBlack = new TreeMapRedBlack<>(); 25 | 26 | insert(avl, groupedRandomNumbers); 27 | insert(ravl, groupedRandomNumbers); 28 | insert(redBlack, groupedRandomNumbers); 29 | } 30 | 31 | private static int insert(Map tree, Integer[] rands) { 32 | long start = System.currentTimeMillis(); 33 | for (int run = 0; run < 10; run++) { 34 | tree.clear(); 35 | for (int i = 0; i < rands.length; i++) { 36 | tree.put(rands[i], rands[i]); 37 | } 38 | } 39 | long stop = System.currentTimeMillis(); 40 | 41 | int mean = (int) (stop - start) / 10; 42 | 43 | System.out.println(" Mean insertion time: " + mean + ". " + tree); 44 | return mean; 45 | } 46 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bbst-showdown 2 | Performance of WAVL, AVL & Red-black trees compared in Java. 3 | 4 | Because AVL trees enforce stricter balance requirements than red-black trees, performance of AVL trees is substantially better when sequential elements are inserted and 5 | nearly identical for random insertions. 'AVL tree implementations were consistently faster than red-black trees, by up to 20%' in the detailed 2004 6 | analysis by Ben Pfaff- 7 | https://benpfaff.org/papers/libavl.pdf 8 | Comparing implementations from this project, the difference can be over 30% - 9 | https://refactoringlightly.wordpress.com/ 10 | 11 | What is fascinating about AVL trees is that although they were originally described in a paper from 1962, interesting variations were published in 2015(WAVL) and 2016(RAVL), 12 | http://sidsen.azurewebsites.net// 13 | so there remains a great coding opportunity to elegantly implement and explore the possibilities described in recent papers. 14 | 15 | Specifically, rank balanced AVL trees are still not common so this project explores their implementation and various common AVL tree implementations such as balance factor. 16 | 17 | Balanced binary trees such as red-black, AVL and RAVL can have a reputation for being difficult to code, 18 | that may have been the case in the 90s when coding them from a textbook or a research paper but with modern tools and resources the difficulty has decreased. 19 | Take a look at the fixAfterInsert() method for TreeMapRAVL which uses rank and rank difference to build an AVL tree for an example of a fairly simple insert retracing loop. 20 | 21 | ## The AVL vs. Red-black Tree History 22 | 23 | Unfortunately, red-black trees are the default implementation in Java, the Linux kernel and the C++ standard library. Why? For random inserts, 24 | AVL trees perform .7 rotations per insert while red-black trees perform .6 rotations per insert. Therefore, in a 1990s world of costly memory 25 | access it was commonly ASSUMED that red-black trees would perform better for insert intensive tasks while AVL would perform better on lookup intensive 26 | tasks. This type of statement, totally based on assumptions about the cost of rotations, is still common within many internet discussion groups. 27 | On modern computers, rotations are a poor metric for performance - tree height, average node height and comparisons required per operation are much 28 | better benchmarks. Also, the popular CLR algorithms textbook does not cover AVL trees, an issue I expect they will correct in future editions given 29 | current research. 30 | 31 | I welcome your input on this project. -------------------------------------------------------------------------------- /src/main/test/bbst_showdown/AVLTreeMapTest.java: -------------------------------------------------------------------------------- 1 | package bbst_showdown; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertNull; 5 | import static org.junit.Assert.assertTrue; 6 | 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | public class AVLTreeMapTest { 11 | 12 | AVLTreeMap x = new AVLTreeMap<>(); 13 | 14 | @Before 15 | public void setup(){ 16 | x.clear(); 17 | } 18 | 19 | @Test 20 | public void testInsert100() { 21 | for (int i=0; i < 100; i++) 22 | x.put(i, i); 23 | assertEquals(100, x.size()); 24 | System.out.println(x.rotations); 25 | } 26 | 27 | @Test 28 | public void testInsertMany() { 29 | Integer [] a = {477,1193,2130,398,1393,946,422,1381,1767,830,570,1085,741,598,1658,1801,487,1921,1918,258,135,975,1870}; 30 | for (int i=0; i < a.length; i++) 31 | x.put(a[i], a[i]); 32 | assertEquals(1193, (int) x.root.value); 33 | assertEquals(1767, (int) x.root.right.value); 34 | assertEquals(1393, (int) x.root.right.left.value); 35 | assertEquals(1921, (int) x.root.right.right.value); 36 | assertEquals(1870, (int) x.root.right.right.left.value); 37 | assertEquals(1801, (int) x.root.right.right.left.left.value); 38 | assertEquals(2130, (int) x.root.right.right.right.value); 39 | } 40 | 41 | @Test 42 | public void testDeleteMany() { 43 | Integer [] a = {477,1193,2130,398,1393,946,422,1381,1767,830,570,1085,741,598,1658,1801,487,1921,1918,258,135,975,1870}; 44 | for (int i=0; i < a.length; i++) 45 | x.put(a[i], a[i]); 46 | for (int i=a.length-1; i > 0; i--) { 47 | x.remove(a[i], a[i]); 48 | if (a[i] == 830) 49 | assertEquals(-1, x.root.left.balance); 50 | } 51 | assertEquals(477, (int) x.root.value); 52 | assertEquals(0, x.root.balance); 53 | assertNull(x.root.left); 54 | assertNull(x.root.right); 55 | } 56 | 57 | @Test 58 | public void testDelete6() { 59 | for (int i=0; i<6; i++) 60 | x.put(i, i); 61 | for (int i=0; i<6; i++) { 62 | System.out.println(x.root + "Deleting:"+ i); 63 | x.remove(i); 64 | } 65 | assertEquals(0, x.size()); 66 | } 67 | 68 | @Test 69 | public void testDeleteNoRotation() { 70 | x.put(2, 2); 71 | x.put(3, 3); 72 | x.put(1, 1); 73 | assertEquals(2, (int)x.root.value); 74 | 75 | x.remove(3); 76 | assertEquals(-1, x.root.balance); 77 | assertEquals(0, x.rotations); 78 | 79 | // remove root 80 | x.remove(2); 81 | x.remove(1); 82 | assertEquals(0, x.size()); 83 | } 84 | 85 | @Test 86 | public void testDeleteRoot() { 87 | x.put(8, 8); 88 | x.remove(8); 89 | assertTrue(x.size() == 0); 90 | } 91 | 92 | @Test 93 | public void testDeleteFibonacci() { 94 | x.put(8, 8); 95 | x.put(5, 5); 96 | x.put(11, 11); 97 | // 3,7,10,12 98 | x.put(3, 3); x.put(7, 7); x.put(10, 10); x.put(12, 12); 99 | // 2,4,6,9 100 | x.put(2, 2); x.put(4, 4); x.put(6, 6); x.put(9, 9); 101 | x.put(1, 1); 102 | System.out.println("Rotations Before fibonacci remove: " + x.rotations); 103 | x.remove(12, 12); 104 | System.out.println("Rotations after fibonacci remove: " + x.rotations); 105 | 106 | assertEquals(5, (int) x.root.value); 107 | assertEquals(2, x.rotations); 108 | assertEquals(-1, x.root.left.balance); 109 | } 110 | 111 | @Test 112 | public void testDeleteRightRotation() { 113 | x.put(3, 3); 114 | x.put(2, 2); 115 | x.put(4, 4); 116 | x.put(1, 1); 117 | 118 | x.remove(4); 119 | x.inOrderTraversal(x.root); 120 | } 121 | 122 | @Test 123 | public void testDeleteOneLeftRightRotation() { 124 | x.put(10, 10); 125 | x.put(8, 8); 126 | x.put(12, 12); 127 | x.put(9, 9); 128 | 129 | x.remove(12); 130 | 131 | assertEquals(0, x.root.balance); 132 | assertEquals(9, (int) x.root.value); 133 | assertEquals(2, x.rotations); 134 | } 135 | } -------------------------------------------------------------------------------- /src/main/java/bbst_showdown/TreeMapAVLRec.java: -------------------------------------------------------------------------------- 1 | package bbst_showdown; 2 | 3 | import java.util.Comparator; 4 | 5 | /** 6 | * There are many elegant online AVL implementations that use recursion and height.
7 | * This AVL insert implementation uses balance factor instead of height which adds some 8 | * complexity compared to the most common recursive implementation. 9 | * 10 | * @author David McManamon 11 | * 12 | * @param the type of keys maintained by this map 13 | * @param the type of mapped values 14 | */ 15 | public class TreeMapAVLRec extends AVLTreeMap { 16 | 17 | public TreeMapAVLRec() { 18 | super(); 19 | } 20 | 21 | public V put(K key, V value) { 22 | Entry t = root; 23 | if (t == null) { 24 | compare(key, key); // type (and possibly null) check 25 | 26 | root = new Entry(key, value, null); 27 | size = 1; 28 | modCount++; 29 | return null; 30 | } 31 | int cmp; 32 | // split comparator and comparable paths 33 | Comparator cpr = comparator; 34 | if (cpr != null) { 35 | do { 36 | cmp = cpr.compare(key, t.key); 37 | if (cmp < 0) 38 | t = t.left; 39 | else if (cmp > 0) 40 | t = t.right; 41 | else 42 | return t.setValue(value); 43 | } while (t != null); 44 | } else { 45 | if (key == null) throw new NullPointerException(); 46 | rebalanced = false; 47 | root = put(root, key, value); 48 | } 49 | 50 | size++; 51 | modCount++; 52 | return null; 53 | } 54 | 55 | boolean rebalanced; // flag to signal re-tracing can stop after insertion because a rebalance was performed or isn't necessary 56 | 57 | private Entry put(Entry x, K key, V value) { 58 | if (x == null) 59 | return new Entry<>(key, value, null); 60 | @SuppressWarnings("unchecked") 61 | Comparable k = (Comparable) key; 62 | int cmp = k.compareTo(x.key); 63 | if (cmp < 0) { 64 | x.left = put(x.left, key, value); 65 | if (!rebalanced) 66 | x.balance--; 67 | } else if (cmp > 0) { 68 | x.right = put(x.right, key, value); 69 | if (!rebalanced) 70 | x.balance++; 71 | } else { 72 | x.value = value; 73 | rebalanced = true; 74 | size--; 75 | return x; 76 | } 77 | 78 | if (x.balance == 0) { 79 | rebalanced = true; 80 | } else if (Math.abs(x.balance) == 2) { 81 | rebalanced = true; 82 | return balance(x); 83 | } 84 | return x; 85 | } 86 | 87 | /** 88 | * Restores the AVL tree property of the subtree. 89 | * 90 | * @param x the subtree 91 | * @return the subtree with restored AVL property 92 | */ 93 | private Entry balance(Entry x) { 94 | if (x.balance == -2) { 95 | if (x.left.balance == 1) { 96 | int lrBalance = x.left.right.balance; 97 | x.left.right.balance = 0; 98 | x.left.balance = 0; 99 | x.balance = 0; 100 | if (lrBalance == 1) 101 | x.left.balance = -1; 102 | else if (lrBalance == -1) 103 | x.balance = 1; 104 | x.left = rotateLeft(x.left); 105 | x = rotateRight(x); 106 | } else { 107 | x.balance = 0; 108 | x.left.balance = 0; 109 | x = rotateRight(x); 110 | } 111 | } else if (x.balance == 2) { 112 | if (x.right.balance == -1) { 113 | int rlBalance = x.right.left.balance; 114 | x.right.left.balance = 0; 115 | x.right.balance = 0; 116 | x.balance = 0; 117 | if (rlBalance == 1) 118 | x.balance = -1; 119 | else if (rlBalance == -1) 120 | x.right.balance = 1; 121 | x.right = rotateRight(x.right); 122 | x = rotateLeft(x); 123 | } else { 124 | x.balance = 0; 125 | x.right.balance = 0; 126 | x = rotateLeft(x); 127 | } 128 | } 129 | return x; 130 | } 131 | 132 | private Entry rotateLeft(Entry p) { 133 | Entry r = p.right; 134 | p.right = r.left; 135 | r.left = p; 136 | rotations++; 137 | return r; 138 | } 139 | 140 | private Entry rotateRight(Entry p) { 141 | Entry l = p.left; 142 | p.left = l.right; 143 | l.right = p; 144 | rotations++; 145 | return l; 146 | } 147 | 148 | public String toString() { 149 | return "AVL(recursive w/balance factor) tree of size: " + size + ", height: " + treeHeight() + ", rotations " + rotations; 150 | } 151 | } -------------------------------------------------------------------------------- /src/main/java/performanceTests/Standoff.java: -------------------------------------------------------------------------------- 1 | package performanceTests; 2 | 3 | import java.io.FileNotFoundException; 4 | import java.util.*; 5 | 6 | import bbst_showdown.AVLTreeMap; 7 | import bbst_showdown.AVLTreeMapRB; 8 | import bbst_showdown.TreeMapAVLRec; 9 | import bbst_showdown.TreeMapAVLStack; 10 | import bbst_showdown.TreeMapBST; 11 | import bbst_showdown.TreeMapRedBlack; 12 | import bbst_showdown.WAVLTreeMap; 13 | 14 | /** 15 | * Prints a comparison of tree performance. 16 | * 17 | * @author David McManamon 18 | */ 19 | public class Standoff { 20 | 21 | public static void main(String[] args) throws FileNotFoundException { 22 | Map redBlack = new TreeMapRedBlack<>(); 23 | Map avl = new AVLTreeMap<>(); 24 | Map avlRB = new AVLTreeMapRB<>(); 25 | Map wavl = new WAVLTreeMap<>(false); 26 | Map bst = new TreeMapBST<>(); 27 | // Map avlNoParent = new TreeMapAVLStack<>(); 28 | // Map avlRec = new TreeMapAVLRec<>(); 29 | 30 | List> maps = new ArrayList>(); 31 | maps.add(redBlack); // 0=red-black 32 | maps.add(avl); // 1=avl 33 | maps.add(avlRB); // 2=avl rb 34 | maps.add(wavl); // 3=wavl 35 | maps.add(bst); // 4-bst (no rotations) 36 | 37 | // TODO update the integer to choose a different tree implementation 38 | Map treeMap = maps.get(3); 39 | 40 | int mean; 41 | Integer [] randomInts = readRandomInts(); 42 | 43 | mean = delete(treeMap, randomInts); 44 | System.out.println("Delete time: " + mean + "ms, " + treeMap); 45 | 46 | mean = insert(treeMap, randomInts); 47 | System.out.println("Random insert time: " + mean + "ms, " + treeMap); 48 | 49 | mean = insertInOrder(treeMap, 100000); 50 | System.out.println("Sequential insert time: " + mean + "ms, " + treeMap); 51 | } 52 | 53 | private static Integer[] readRandomInts() throws FileNotFoundException { 54 | Integer[] v = new Integer[1000000]; 55 | String fileName = "randomInts.txt"; 56 | java.io.File file = new java.io.File(fileName); 57 | 58 | java.util.Scanner inputStream = new java.util.Scanner(file); 59 | inputStream.nextLine(); 60 | while (inputStream.hasNext()) { 61 | String data = inputStream.next(); 62 | String[] values = data.split(","); 63 | 64 | for (int i = 0; i < 1000000; i++) { 65 | v[i] = Integer.parseInt(values[i]); 66 | } 67 | } 68 | inputStream.close(); 69 | return v; 70 | } 71 | 72 | private static int insertInOrder(Map tree, int nElements) { 73 | int[] times = new int[5]; 74 | for (int j = 0; j < 5; j++) { 75 | long start = System.currentTimeMillis(); 76 | tree.clear(); 77 | for (Integer i = 0; i < nElements; i++) { 78 | tree.put(i, i); 79 | } 80 | long stop = System.currentTimeMillis(); 81 | times[j] = (int) (stop - start); 82 | } 83 | 84 | Arrays.sort(times); 85 | 86 | return (times[1] + times[2] + times[3]) / 3; 87 | } 88 | 89 | private static int delete(Map tree, Integer[] rands) { 90 | for (int i = 0; i < 100000; i++) { 91 | tree.put(rands[i], rands[i]); 92 | } 93 | long start = System.currentTimeMillis(); 94 | for (int i = 100000; i > 0 ; i--) { 95 | tree.remove(rands[i-1]); 96 | } 97 | long stop = System.currentTimeMillis(); 98 | return (int)(stop-start); 99 | } 100 | 101 | private static int insert(Map tree, Integer[] rands) { 102 | int [] times = new int[10]; 103 | int x = 0; 104 | for (int j = 0; j < 10; j++) { 105 | long start = System.currentTimeMillis(); 106 | tree.clear(); 107 | for (int i = 0; i < 100000; i++) { 108 | tree.put(rands[x], rands[x]); 109 | x++; 110 | } 111 | long stop = System.currentTimeMillis(); 112 | times[j] = (int) (stop - start); 113 | } 114 | 115 | Arrays.sort(times); 116 | 117 | return (times[2] + times[3] + times[4] + times[5] + times[6] + times[7]) / 6; 118 | } 119 | 120 | public static long insertDeleteLookup(Map x) { 121 | long start = System.currentTimeMillis(); 122 | java.util.Random r = new java.util.Random(); 123 | Integer[] inserted = new Integer[1000000]; 124 | for (Integer i = 0; i < 1000000; i++) { 125 | if (i < 20000) { 126 | inserted[i] = i; 127 | x.put(i, i); 128 | } else { 129 | Integer next = r.nextInt(); 130 | inserted[i] = next; 131 | x.put(next, next); 132 | } 133 | } 134 | 135 | for (Integer i = 0; i < 500000; i++) { 136 | if (i % 2 == 0) 137 | x.remove(inserted[i]); 138 | else 139 | x.get(inserted[i]); 140 | } 141 | 142 | long stop = System.currentTimeMillis(); 143 | return stop - start; 144 | } 145 | } -------------------------------------------------------------------------------- /src/main/test/bbst_showdown/AVLTreeMapRBTest.java: -------------------------------------------------------------------------------- 1 | package bbst_showdown; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | 9 | public class AVLTreeMapRBTest { 10 | AVLTreeMapRB x = new AVLTreeMapRB<>(); 11 | 12 | @Before 13 | public void setup(){ 14 | x.clear(); 15 | } 16 | 17 | @Test 18 | public void testTreeHeight() { 19 | x.put(2, 2); 20 | assertEquals(0, x.treeHeight()); 21 | x.put(3, 3); 22 | assertEquals(1, x.treeHeight()); 23 | x.put(1, 1); 24 | x.put(0, 0); 25 | assertEquals(2, x.treeHeight()); 26 | } 27 | 28 | @Test 29 | public void testInsertDoNothing() { 30 | x.put(2, 2); 31 | x.put(3, 3); 32 | assertEquals(0, x.rotations); 33 | assertEquals(2, (int)x.root.value); 34 | 35 | x.put(1, 1); 36 | assertEquals(2, (int)x.root.value); 37 | 38 | assertEquals(0, x.rotations); 39 | assertTrue(x.root.right.deltaR == x.root.left.deltaR); 40 | } 41 | 42 | @Test 43 | public void testInsert100() { 44 | for (int i=0; i < 100; i++) 45 | x.put(i, i); 46 | assertEquals(100, x.size()); 47 | assertEquals(93, x.rotations); 48 | } 49 | 50 | @Test 51 | public void testInsert6() { 52 | for (int i=0; i<6; i++) 53 | x.put(i, i); 54 | assertEquals(3, x.rotations); 55 | assertEquals(3, (int) x.root.value); 56 | x.inOrderTraversal(x.root); 57 | } 58 | 59 | @Test 60 | public void testInsertOneLeftRotation() { 61 | x.put(1, 1); 62 | x.put(2, 2); 63 | x.put(3, 3); 64 | 65 | assertEquals(1, x.rotations); 66 | assertEquals(AVLTreeMapRB.ONE, x.root.right.deltaR); 67 | assertEquals(AVLTreeMapRB.ONE, x.root.left.deltaR); 68 | assertEquals(2, (int) x.root.value); 69 | } 70 | 71 | /* 2 72 | 73 | / \ 74 | 75 | 1 4 76 | 77 | / \ 78 | 79 | 3 5 80 | */ 81 | @Test 82 | public void testInsertTwoLeftRotations() { 83 | x.put(1, 1); 84 | x.put(2, 2); 85 | x.put(3, 3); 86 | x.put(4, 4); 87 | x.put(5, 5); 88 | 89 | assertEquals(AVLTreeMapRB.TWO, x.root.left.deltaR); 90 | assertEquals(AVLTreeMapRB.ONE, x.root.right.left.deltaR); 91 | assertEquals(AVLTreeMapRB.ONE, x.root.right.deltaR); 92 | assertEquals(2, (int) x.root.value); 93 | assertEquals(2, x.rotations); 94 | } 95 | 96 | /* 97 | 4 98 | 99 | / \ 100 | 101 | 2 5 102 | 103 | / \ \ 104 | 105 | 1 3 6 106 | */ 107 | @Test 108 | public void testInsertThreeLeftRotations() { 109 | x.put(1, 1); 110 | x.put(2, 2); 111 | x.put(3, 3); 112 | x.put(4, 4); 113 | x.put(5, 5); 114 | assertEquals(5, (int) x.root.right.right.value); 115 | System.out.println("Inserting 6"); 116 | assertEquals(AVLTreeMapRB.TWO, x.root.left.deltaR); 117 | x.put(6, 6); 118 | 119 | assertEquals(3, x.rotations); 120 | assertEquals(4, (int) x.root.value); 121 | assertEquals(AVLTreeMapRB.ONE, x.root.right.deltaR); 122 | assertEquals(AVLTreeMapRB.ONE, x.root.right.right.deltaR); 123 | assertEquals(AVLTreeMapRB.ONE, x.root.left.deltaR); 124 | assertEquals(AVLTreeMapRB.ONE, x.root.left.left.deltaR); 125 | assertEquals(AVLTreeMapRB.ONE, x.root.left.right.deltaR); 126 | } 127 | 128 | @Test 129 | public void testInsertLeftRightRotation() { 130 | x.put(3, 3); 131 | x.put(1, 1); 132 | x.put(2, 2); 133 | 134 | assertEquals(2, x.rotations); 135 | assertEquals(2, (int) x.root.value); 136 | assertEquals(AVLTreeMapRB.ONE, x.root.right.deltaR); 137 | assertEquals(AVLTreeMapRB.ONE, x.root.left.deltaR); 138 | } 139 | 140 | @Test 141 | public void testInsertRightLeftRotation() { 142 | x.put(3, 3); 143 | x.put(6, 6); 144 | x.put(4, 4); 145 | 146 | assertEquals(2, x.rotations); 147 | assertEquals(4, (int) x.root.value); 148 | assertEquals(AVLTreeMapRB.ONE, x.root.right.deltaR); 149 | assertEquals(AVLTreeMapRB.ONE, x.root.left.deltaR); 150 | } 151 | 152 | @Test 153 | public void testInsertRightLeftRotation2() { 154 | x.put(1921, 1921); 155 | x.put(1801, 1801); 156 | x.put(2130, 2130); 157 | x.put(1918, 1918); 158 | x.put(1870, 1870); 159 | 160 | assertEquals(2, x.rotations); 161 | assertEquals(1921, (int) x.root.value); 162 | assertEquals(AVLTreeMapRB.TWO, x.root.right.deltaR); 163 | } 164 | 165 | /* 166 | 8 167 | 5 11 168 | 3 7 10 12 169 | 2 4 6 9 170 | 1 171 | 172 | */ 173 | @Test 174 | public void testInsertBuildFibonacciTree() { 175 | x.put(8, 8); 176 | x.put(5, 5); x.put(11, 11); 177 | // 3,7,10,12 178 | x.put(3, 3); x.put(7, 7); x.put(10, 10); x.put(12, 12); 179 | // 2,4,6,9 180 | x.put(2, 2); assertEquals(0, x.rotations); 181 | x.put(4, 4); assertEquals(0, x.rotations); 182 | x.put(6, 6); assertEquals(0, x.rotations); 183 | x.put(9, 9); assertEquals(0, x.rotations); 184 | x.put(1, 1); 185 | System.out.println("Rotations: " + x.rotations); 186 | assertEquals(0, x.rotations); 187 | x.inOrderTraversal(x.root); 188 | } 189 | 190 | @Test 191 | public void testInsertMaintainDelta2Parent() { 192 | x.put(20, 20); 193 | x.put(10, 10); x.put(40, 40); 194 | x.put(5, 5); x.put(15, 15); x.put(30, 30); x.put(80, 80); 195 | x.put(0, 0); x.put(7, 7); x.put(12, 12); x.put(60, 60); x.put(160, 160); 196 | x.put(8, 8); 197 | assertEquals(AVLTreeMapRB.TWO, x.root.right.deltaR); 198 | x.put(100, 100); 199 | assertEquals(AVLTreeMapRB.TWO, x.root.right.deltaR); 200 | } 201 | 202 | @Test 203 | public void testInsertRemoveDelta2Child() { 204 | x.put(1767, 1767); 205 | x.put(1658, 1658); 206 | x.put(1921, 1921); 207 | x.put(1801, 1801); 208 | x.put(2130, 2130); // temporary delta 2 removed by rotations when insert 1918 209 | System.out.println("Inserting 1918"); 210 | x.put(1918, 1918); 211 | 212 | assertEquals(1801, (int) x.root.value); 213 | assertEquals(AVLTreeMapRB.ONE, x.root.right.right.deltaR); 214 | } 215 | 216 | @Test 217 | public void testInsertTwoRightRotations() { 218 | x.put(5, 5); 219 | x.put(4, 4); 220 | x.put(3, 3); 221 | x.put(2, 4); 222 | x.put(1, 1); 223 | 224 | assertEquals(2, x.rotations); 225 | assertEquals(4, (int) x.root.value); 226 | assertEquals(AVLTreeMapRB.TWO, x.root.right.deltaR); 227 | assertEquals(AVLTreeMapRB.ONE, x.root.left.left.deltaR); 228 | assertEquals(AVLTreeMapRB.ONE, x.root.left.deltaR); 229 | } 230 | 231 | @Test 232 | public void testInsertMany() { 233 | Integer [] a = {1493,477,1193,2130,398,1393,946,422,1381,1767,830,570,1085,741,598,1658,1801,487,1921, 234 | 1918,258,135,975,1870,1655,1585,1935,271,1969,1313,1290,162,1485,26,86,244,1140}; 235 | for (int i=0; i < a.length; i++) { 236 | System.out.println("INSERT:" + i +"=" + a[i]); 237 | x.put(a[i], a[i]); 238 | //assertTrue(x.identicalTrees(x.root, y.root)); 239 | } 240 | assertEquals(1193, (int) x.root.value); 241 | 242 | } 243 | } -------------------------------------------------------------------------------- /src/main/test/bbst_showdown/WAVLTreeMapTest.java: -------------------------------------------------------------------------------- 1 | package bbst_showdown; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | 8 | public class WAVLTreeMapTest { 9 | 10 | WAVLTreeMap x = new WAVLTreeMap<>(false); 11 | 12 | @Before 13 | public void setup(){ 14 | x.clear(); 15 | } 16 | 17 | @Test 18 | public void testTreeHeight() { 19 | x.put(2, 2); 20 | assertEquals(0, x.treeHeight()); 21 | x.put(3, 3); 22 | assertEquals(1, x.treeHeight()); 23 | x.put(1, 1); 24 | x.put(0, 0); 25 | assertEquals(2, x.treeHeight()); 26 | } 27 | 28 | @Test 29 | public void testInsertDoNothing() { 30 | x.put(2, 2); 31 | x.put(3, 3); 32 | assertEquals(0, x.rotations); 33 | assertEquals(2, (int)x.root.value); 34 | assertEquals(1, x.root.rank); 35 | 36 | x.put(1, 1); 37 | assertEquals(2, (int)x.root.value); 38 | 39 | assertEquals(1, x.root.rank); 40 | assertEquals(0, x.rotations); 41 | } 42 | 43 | @Test 44 | public void testInsert100() { 45 | for (int i=0; i<100; i++) 46 | x.put(i, i); 47 | assertEquals(100, x.size()); 48 | } 49 | 50 | @Test 51 | public void testInsertMany() { 52 | Integer [] a = {477, 1193, 2130,398,1393,946,422,1381,1767,830,570,1085,741,598,1658,1801,487,1921,1918,258,135,975,1870}; 53 | for (int i=0; i < a.length; i++) 54 | x.put(a[i], a[i]); 55 | assertEquals(1193, (int) x.root.value); 56 | assertEquals(1767, (int) x.root.right.value); 57 | assertEquals(1393, (int) x.root.right.left.value); 58 | assertEquals(1921, (int) x.root.right.right.value); 59 | assertEquals(1870, (int) x.root.right.right.left.value); 60 | assertEquals(1801, (int) x.root.right.right.left.left.value); 61 | assertEquals(2130, (int) x.root.right.right.right.value); 62 | } 63 | 64 | @Test 65 | public void testDeleteMany() { 66 | Integer [] a = {477,1193,2130,398,1393,946,422,1381,1767,830,570,1085,741,598,1658,1801,487};//,1921,1918,258,135,975,1870}; 67 | for (int i=0; i < a.length; i++) 68 | x.put(a[i], a[i]); 69 | for (int i=a.length-1; i > 0; i--) { 70 | System.out.println("Deleting:" + i + " value:" + a[i]); 71 | x.remove(a[i], a[i]); 72 | 73 | } 74 | assertEquals(477, (int) x.root.value); 75 | assertEquals(0, x.root.rank); 76 | assertNull(x.root.left); 77 | assertNull(x.root.right); 78 | } 79 | 80 | @Test 81 | public void testInsertOneLeftRotation() { 82 | x.put(1, 1); 83 | x.put(2, 2); 84 | x.put(3, 3); 85 | 86 | assertEquals(1, x.root.rank); 87 | assertEquals(2, (int) x.root.value); 88 | assertEquals(0, x.root.right.rank); 89 | } 90 | 91 | @Test 92 | public void testInsertTwoLeftRotations() { 93 | x.put(1, 1); 94 | x.put(2, 2); 95 | x.put(3, 3); 96 | x.put(4, 4); 97 | x.put(5, 5); 98 | 99 | assertEquals(2, x.root.rank); 100 | assertEquals(2, (int) x.root.value); 101 | assertEquals(2, x.rotations); 102 | assertEquals(1, x.root.right.rank); 103 | assertEquals(0, x.root.left.rank); 104 | } 105 | 106 | @Test 107 | public void testInsertThreeLeftRotations() { 108 | x.put(1, 1); 109 | x.put(2, 2); 110 | x.put(3, 3); 111 | x.put(4, 4); 112 | x.put(5, 5); 113 | x.put(6, 6); 114 | 115 | assertEquals(3, x.rotations); 116 | assertEquals(4, (int) x.root.value); 117 | assertEquals(2, x.root.rank); 118 | assertTrue(x.root.right.rank == 1 && x.root.left.rank == 1); 119 | } 120 | 121 | @Test 122 | public void testInsertLeftRightRotation() { 123 | x.put(3, 3); 124 | x.put(1, 1); 125 | x.put(2, 2); 126 | 127 | assertEquals(2, x.rotations); 128 | assertEquals(2, (int) x.root.value); 129 | assertEquals(1, x.root.rank); 130 | } 131 | 132 | @Test 133 | public void testInsertRightLeftRotation() { 134 | x.put(3, 3); 135 | x.put(6, 6); 136 | x.put(4, 4); 137 | 138 | assertEquals(2, x.rotations); 139 | assertEquals(4, (int) x.root.value); 140 | assertEquals(1, x.root.rank); 141 | } 142 | 143 | @Test 144 | public void testInsertBuildFibonacciTree() { 145 | x.put(8, 8); 146 | x.put(5, 5); 147 | x.put(11, 11); 148 | // 3,7,10,12 149 | x.put(3, 3); x.put(7, 7); x.put(10, 10); x.put(12, 12); 150 | // 2,4,6,9 151 | x.put(2, 2); x.put(4, 4); x.put(6, 6); x.put(9, 9); 152 | x.put(1, 1); 153 | System.out.println("Rotations: " + x.rotations); 154 | assertEquals(0, x.rotations); 155 | } 156 | 157 | @Test 158 | public void testInsertFibAfterDelete() { 159 | x.put(8, 8); // root 160 | x.put(5, 5); x.put(11, 11); 161 | // 3,7,10,12 162 | x.put(3, 3); x.put(7, 7); x.put(10, 10); x.put(12, 12); 163 | // 2,4,6,9 164 | x.put(2, 2); x.put(4, 4); x.put(6, 6); x.put(9, 9); 165 | x.put(1, 1); 166 | 167 | x.remove(12, 12); 168 | //TODO 169 | } 170 | 171 | @Test 172 | public void testInsert6() { 173 | for (int i=0; i<6; i++) 174 | x.put(i, i); 175 | assertEquals(3, x.rotations); 176 | assertEquals(3, (int) x.root.value); 177 | x.inOrderTraversal(x.root); 178 | } 179 | 180 | @Test 181 | public void testInsertTwoRightRotations() { 182 | x.put(5, 5); 183 | x.put(4, 4); 184 | x.put(3, 3); 185 | x.put(2, 4); 186 | x.put(1, 1); 187 | 188 | assertEquals(2, x.rotations); 189 | assertEquals(2, x.root.rank); 190 | assertEquals(4, (int) x.root.value); 191 | assertEquals(0, x.root.right.rank); 192 | assertEquals(1, x.root.left.rank); 193 | } 194 | 195 | @Test 196 | public void testDeleteNoRotation() { 197 | x.put(2, 2); 198 | x.put(3, 3); 199 | x.put(1, 1); 200 | assertEquals(2, (int)x.root.value); 201 | 202 | x.remove(3); 203 | assertEquals(1, x.root.rank); 204 | assertEquals(0, x.rotations); 205 | 206 | // remove root 207 | x.remove(2); 208 | x.remove(1); 209 | assertEquals(0, x.size()); 210 | } 211 | 212 | @Test 213 | public void testDeleteNoRotationWAVL() { 214 | x = new WAVLTreeMap<>(true); 215 | x.put(2, 2); 216 | x.put(3, 3); 217 | x.put(1, 1); 218 | assertEquals(2, (int)x.root.value); 219 | 220 | x.remove(3); 221 | assertEquals(1, x.root.rank); 222 | assertEquals(0, x.rotations); 223 | 224 | // remove root 225 | x.remove(2); 226 | x.remove(1); 227 | assertEquals(0, x.size()); 228 | } 229 | 230 | @Test 231 | public void testDeleteNoRotationLeftTallDecrementRank() { 232 | x.put(2, 2); 233 | x.put(3, 3); 234 | x.put(1, 1); 235 | x.put(0, 0); 236 | assertEquals(2, (int)x.root.value); 237 | 238 | x.remove(0); 239 | /* 240 | 2 241 | 1 3 242 | */ 243 | assertEquals(1, x.root.rank); 244 | assertEquals(0, x.rotations); 245 | } 246 | 247 | @Test 248 | public void testDeleteOneRightRotation() { 249 | x.put(10, 10); 250 | x.put(8, 8); x.put(12, 12); 251 | x.put(6, 6); 252 | 253 | x.remove(12); 254 | /* 255 | 8 256 | 6 10 257 | */ 258 | assertEquals(1, x.rotations); 259 | assertEquals(1, x.root.rank); 260 | assertEquals(8, (int) x.root.value); 261 | } 262 | 263 | @Test 264 | public void testDeleteOneRightRotationWAVL() { 265 | x = new WAVLTreeMap<>(true); 266 | x.put(10, 10); 267 | x.put(8, 8); x.put(12, 12); 268 | x.put(6, 6); 269 | 270 | x.remove(12); 271 | /* 272 | 8 273 | 6 10 274 | */ 275 | assertEquals(1, x.rotations); 276 | assertEquals(2, x.root.rank); // created 2,2 node, non-leaf 277 | assertEquals(0, x.root.right.rank); 278 | assertEquals(0, x.root.left.rank); 279 | assertEquals(8, (int) x.root.value); 280 | } 281 | 282 | @Test 283 | public void testDeleteOneRightRotationSiblingBalanced() { 284 | x.put(10, 10); 285 | x.put(8, 8);x.put(12, 12); 286 | x.put(6,6);x.put(9,9); 287 | 288 | assertEquals(0, x.rotations); 289 | 290 | x.remove(12); 291 | /* after 292 | 8 293 | 6 10 294 | 9 295 | */ 296 | assertEquals(2, x.root.rank); 297 | assertEquals(6, (int) x.root.left.value); 298 | assertEquals(1, x.root.right.rank); 299 | assertEquals(0, x.root.left.rank); 300 | assertEquals(8, (int) x.root.value); 301 | assertEquals(1, x.rotations); 302 | } 303 | 304 | @Test 305 | public void testDeleteOneRightRotationSiblingBalancedWAVL() { 306 | x = new WAVLTreeMap<>(true); 307 | x.put(10, 10); 308 | x.put(8, 8);x.put(12, 12); 309 | x.put(6,6);x.put(9,9); 310 | 311 | assertEquals(0, x.rotations); 312 | 313 | x.remove(12); 314 | /* after 315 | 8 316 | 6 10 317 | 9 318 | */ 319 | assertEquals(2, x.root.rank); 320 | assertEquals(6, (int) x.root.left.value); 321 | assertEquals(1, x.root.right.rank); 322 | assertEquals(0, x.root.right.left.rank); 323 | assertEquals(0, x.root.left.rank); 324 | assertEquals(8, (int) x.root.value); 325 | assertEquals(1, x.rotations); 326 | } 327 | 328 | @Test 329 | public void testDeleteOneLeftRightRotation() { 330 | x.put(10, 10); 331 | x.put(8, 8); 332 | x.put(12, 12); 333 | x.put(9, 9); 334 | 335 | x.remove(12); 336 | /* 337 | 9 338 | 8 10 339 | */ 340 | 341 | assertEquals(0, x.root.right.rank); 342 | assertEquals(0, x.root.left.rank); 343 | assertEquals(1, x.root.rank); 344 | assertEquals(9, (int) x.root.value); 345 | assertEquals(2, x.rotations); 346 | } 347 | 348 | @Test 349 | public void testDeleteOneLeftRightRotationWAVL() { 350 | x = new WAVLTreeMap<>(true); 351 | x.put(10, 10); 352 | x.put(8, 8); 353 | x.put(12, 12); 354 | x.put(9, 9); 355 | 356 | x.remove(12); 357 | /* 358 | 9 359 | 8 10 360 | */ 361 | 362 | assertEquals(0, x.root.right.rank); 363 | assertEquals(0, x.root.left.rank); 364 | assertEquals(2, x.root.rank); // created 2,2 node 365 | assertEquals(9, (int) x.root.value); 366 | assertEquals(2, x.rotations); 367 | } 368 | 369 | @Test 370 | public void testDelete5() { 371 | for (int i=0; i<6; i++) 372 | x.put(i, i); 373 | for (int i=0; i<5; i++) { 374 | System.out.println("Root:" + x.root + " Deleting:"+ i); 375 | x.remove(i); 376 | } 377 | assertEquals(1, x.size()); 378 | assertEquals(0, x.root.rank); 379 | } 380 | 381 | @Test 382 | public void testDelete5WAVL() { 383 | x = new WAVLTreeMap<>(true); 384 | for (int i=0; i<6; i++) 385 | x.put(i, i); 386 | for (int i=0; i<5; i++) { 387 | System.out.println("Root:" + x.root + " Deleting:"+ i); 388 | x.remove(i); 389 | } 390 | assertEquals(1, x.size()); 391 | assertEquals(0, x.root.rank); 392 | 393 | x.remove(5); 394 | } 395 | 396 | @Test 397 | public void testDeleteFibonacciTree() { 398 | x.put(8, 8); // root 399 | x.put(5, 5); x.put(11, 11); 400 | // 3,7,10,12 401 | x.put(3, 3); x.put(7, 7); x.put(10, 10); x.put(12, 12); 402 | // 2,4,6,9 403 | x.put(2, 2); x.put(4, 4); x.put(6, 6); x.put(9, 9); 404 | x.put(1, 1); 405 | System.out.println("Rotations before remove 12 from Fibonacci: " + x.rotations); 406 | x.remove(12, 12); 407 | System.out.println("Rotations after remove 12: " + x.rotations); 408 | System.out.println("ROOT:"+ x.root); 409 | assertEquals(2, x.rotations); 410 | assertEquals(5, (int) x.root.value); 411 | 412 | x.remove(4); 413 | assertEquals(2, (int) x.root.left.value); 414 | assertEquals(1, x.root.left.rank); 415 | x.inOrderTraversal(x.root); 416 | 417 | } 418 | 419 | @Test 420 | public void testDeleteFibonacciTreeWAVL() { 421 | x = new WAVLTreeMap<>(true); 422 | x.put(8, 8); // root 423 | x.put(5, 5); x.put(11, 11); 424 | // 3,7,10,12 425 | x.put(3, 3); x.put(7, 7); x.put(10, 10); x.put(12, 12); 426 | // 2,4,6,9 427 | x.put(2, 2); x.put(4, 4); x.put(6, 6); x.put(9, 9); 428 | x.put(1, 1); 429 | 430 | x.remove(12, 12); 431 | System.out.println("Rotations after remove 12: " + x.rotations); 432 | System.out.println("ROOT:"+ x.root); 433 | assertEquals(1, x.rotations); 434 | assertEquals(8, (int) x.root.value); 435 | assertEquals(0, x.root.right.right.rank); 436 | assertEquals(0, x.root.right.left.rank); 437 | assertEquals(2, x.root.right.rank); 438 | } 439 | 440 | @Test 441 | public void testDeleteAlmostFibonacciTreeWAVL() { 442 | x = new WAVLTreeMap<>(true); 443 | x.put(8, 8); // root 444 | x.put(5, 5); x.put(12, 12); 445 | // 3,7,10,12 446 | x.put(3, 3); x.put(7, 7); x.put(10, 10); x.put(13, 13); 447 | // 2,4,6,9,11 448 | x.put(2, 2); x.put(4, 4); x.put(6, 6); x.put(9, 9); x.put(11,11); 449 | x.put(1, 1); 450 | 451 | x.remove(13, 13); 452 | System.out.println("Rotations after remove 13: " + x.rotations); 453 | System.out.println("ROOT:"+ x.root); 454 | assertEquals(1, x.rotations); 455 | assertEquals(8, (int) x.root.value); 456 | assertEquals(1, x.root.right.right.rank); 457 | assertEquals(0, x.root.right.left.rank); 458 | assertEquals(2, x.root.right.rank); 459 | } 460 | 461 | @Test 462 | public void testDeleteManyWAVL() { 463 | x = new WAVLTreeMap<>(true); 464 | Integer [] a = {477,1193,2130,398,1393,946,422,1381,1767,830,570,1085,741,598,1658,1801,487,1921,1918,258,135,975,1870}; 465 | for (int i=0; i < a.length; i++) { 466 | System.out.print(a[i] + ","); 467 | x.put(a[i], a[i]); 468 | } 469 | System.out.println(); 470 | 471 | for (int i=a.length-1; i > 0; i--) { 472 | System.out.println("Deleting:" + i + " value:" + a[i]); 473 | x.remove(a[i], a[i]); 474 | } 475 | assertEquals(477, (int) x.root.value); 476 | assertEquals(0, x.root.rank); 477 | assertNull(x.root.left); 478 | assertNull(x.root.right); 479 | } 480 | } -------------------------------------------------------------------------------- /src/main/java/bbst_showdown/TreeMapAVLStack.java: -------------------------------------------------------------------------------- 1 | package bbst_showdown; 2 | 3 | import java.util.AbstractMap; 4 | import java.util.Comparator; 5 | import java.util.Map; 6 | import java.util.NavigableMap; 7 | import java.util.NoSuchElementException; 8 | import java.util.Set; 9 | 10 | 11 | /** 12 | * An AVL tree {@link NavigableMap} implementation without parent pointers, 13 | * balance factors & non-recursive insert and delete. Re-tracing after update 14 | * operations is done via a stack. This version is incomplete, performance did not appear promising. 15 | * 16 | * @author David McManamon 17 | * 18 | * @param the type of keys maintained by this map 19 | * @param the type of mapped values 20 | */ 21 | public class TreeMapAVLStack extends AbstractMap { 22 | 23 | protected transient Entry root = null; 24 | 25 | /** 26 | * The number of entries in the tree 27 | */ 28 | protected transient int size = 0; 29 | 30 | /** 31 | * The comparator used to maintain order in this tree map, or 32 | * null if it uses the natural ordering of its keys. 33 | * 34 | * @serial 35 | */ 36 | protected final Comparator comparator; 37 | 38 | /** 39 | * The number of structural modifications to the tree. 40 | */ 41 | protected transient int modCount = 0; 42 | 43 | protected transient int rotations = 0; 44 | 45 | /** 46 | * Constructs a new, empty tree map, using the natural ordering of its 47 | * keys. All keys inserted into the map must implement the {@link 48 | * Comparable} interface. Furthermore, all such keys must be 49 | * mutually comparable: {@code k1.compareTo(k2)} must not throw 50 | * a {@code ClassCastException} for any keys {@code k1} and 51 | * {@code k2} in the map. If the user attempts to put a key into the 52 | * map that violates this constraint (for example, the user attempts to 53 | * put a string key into a map whose keys are integers), the 54 | * {@code put(Object key, Object value)} call will throw a 55 | * {@code ClassCastException}. 56 | */ 57 | public TreeMapAVLStack() { 58 | comparator = null; 59 | } 60 | 61 | /** 62 | * Constructs a new tree map containing the same mappings as the given 63 | * map, ordered according to the natural ordering of its keys. 64 | * All keys inserted into the new map must implement the {@link 65 | * Comparable} interface. Furthermore, all such keys must be 66 | * mutually comparable: {@code k1.compareTo(k2)} must not throw 67 | * a {@code ClassCastException} for any keys {@code k1} and 68 | * {@code k2} in the map. This method runs in n*log(n) time. 69 | * 70 | * @param m the map whose mappings are to be placed in this map 71 | * @throws ClassCastException if the keys in m are not {@link Comparable}, 72 | * or are not mutually comparable 73 | * @throws NullPointerException if the specified map is null 74 | */ 75 | public TreeMapAVLStack(Map m) { 76 | comparator = null; 77 | putAll(m); 78 | } 79 | 80 | public int treeHeight() { 81 | return treeHeight(root); 82 | } 83 | 84 | public int treeHeight(Entry node) { 85 | if (node == null) 86 | return 0; 87 | return (1 + Math.max(treeHeight(node.left), treeHeight(node.right))); 88 | } 89 | 90 | public int rotations() { 91 | return rotations; 92 | } 93 | 94 | public String toString() { 95 | return "AVL tree (no parent ref., no recursion) of size: " + size + ", height: " + treeHeight() + ", rotations " 96 | + rotations; 97 | } 98 | 99 | /** 100 | * Returns the number of key-value mappings in this map. 101 | * 102 | * @return the number of key-value mappings in this map 103 | */ 104 | public int size() { 105 | return size; 106 | } 107 | 108 | /** 109 | * Returns the value to which the specified key is mapped, 110 | * or {@code null} if this map contains no mapping for the key. 111 | * 112 | *

More formally, if this map contains a mapping from a key 113 | * {@code k} to a value {@code v} such that {@code key} compares 114 | * equal to {@code k} according to the map's ordering, then this 115 | * method returns {@code v}; otherwise it returns {@code null}. 116 | * (There can be at most one such mapping.) 117 | * 118 | *

A return value of {@code null} does not necessarily 119 | * indicate that the map contains no mapping for the key; it's also 120 | * possible that the map explicitly maps the key to {@code null}. 121 | * The {@link #containsKey containsKey} operation may be used to 122 | * distinguish these two cases. 123 | * 124 | * @throws ClassCastException if the specified key cannot be compared 125 | * with the keys currently in the map 126 | * @throws NullPointerException if the specified key is null 127 | * and this map uses natural ordering, or its comparator 128 | * does not permit null keys 129 | */ 130 | public V get(Object key) { 131 | Entry p = getEntry(key); 132 | return (p==null ? null : p.value); 133 | } 134 | 135 | /** 136 | * Node in the Tree. 137 | * Doubles as a means to pass key-value pairs back to 138 | * user (see Map.Entry). 139 | */ 140 | static final class Entry implements Map.Entry { 141 | K key; 142 | V value; 143 | Entry left = null; 144 | Entry right = null; 145 | byte balance = 0; // Height(RightSubtree(N)) - Height(LeftSubtree(N)) i.e. right heavy=positive balance, left heavy negative 146 | 147 | /** 148 | * Make a new cell with given key, value, and parent, and with 149 | * {@code null} child links, and BLACK color. 150 | */ 151 | Entry(K key, V value, Entry parent) { 152 | this.key = key; 153 | this.value = value; 154 | } 155 | 156 | /** 157 | * Returns the key. 158 | * 159 | * @return the key 160 | */ 161 | public K getKey() { 162 | return key; 163 | } 164 | 165 | /** 166 | * Returns the value associated with the key. 167 | * 168 | * @return the value associated with the key 169 | */ 170 | public V getValue() { 171 | return value; 172 | } 173 | 174 | /** 175 | * Replaces the value currently associated with the key with the given 176 | * value. 177 | * 178 | * @return the value associated with the key before this method was 179 | * called 180 | */ 181 | public V setValue(V value) { 182 | V oldValue = this.value; 183 | this.value = value; 184 | return oldValue; 185 | } 186 | 187 | public boolean equals(Object o) { 188 | if (!(o instanceof Map.Entry)) 189 | return false; 190 | Map.Entry e = (Map.Entry)o; 191 | 192 | return valEquals(key,e.getKey()) && valEquals(value,e.getValue()); 193 | } 194 | 195 | public int hashCode() { 196 | int keyHash = (key==null ? 0 : key.hashCode()); 197 | int valueHash = (value==null ? 0 : value.hashCode()); 198 | return keyHash ^ valueHash; 199 | } 200 | 201 | public String toString() { 202 | return key + "=" + value; 203 | } 204 | } 205 | 206 | /** 207 | * Returns this map's entry for the given key, or {@code null} if the map 208 | * does not contain an entry for the key. 209 | * 210 | * @return this map's entry for the given key, or {@code null} if the map 211 | * does not contain an entry for the key 212 | * @throws ClassCastException if the specified key cannot be compared 213 | * with the keys currently in the map 214 | * @throws NullPointerException if the specified key is null 215 | * and this map uses natural ordering, or its comparator 216 | * does not permit null keys 217 | */ 218 | final Entry getEntry(Object key) { 219 | // Offload comparator-based version for sake of performance 220 | if (comparator != null) 221 | return getEntryUsingComparator(key); 222 | if (key == null) 223 | throw new NullPointerException(); 224 | @SuppressWarnings("unchecked") 225 | Comparable k = (Comparable) key; 226 | Entry p = root; 227 | while (p != null) { 228 | int cmp = k.compareTo(p.key); 229 | if (cmp < 0) 230 | p = p.left; 231 | else if (cmp > 0) 232 | p = p.right; 233 | else 234 | return p; 235 | } 236 | return null; 237 | } 238 | 239 | /** 240 | * Version of getEntry using comparator. Split off from getEntry 241 | * for performance. (This is not worth doing for most methods, 242 | * that are less dependent on comparator performance, but is 243 | * worthwhile here.) 244 | */ 245 | final Entry getEntryUsingComparator(Object key) { 246 | @SuppressWarnings("unchecked") 247 | K k = (K) key; 248 | Comparator cpr = comparator; 249 | if (cpr != null) { 250 | Entry p = root; 251 | while (p != null) { 252 | int cmp = cpr.compare(k, p.key); 253 | if (cmp < 0) 254 | p = p.left; 255 | else if (cmp > 0) 256 | p = p.right; 257 | else 258 | return p; 259 | } 260 | } 261 | return null; 262 | } 263 | 264 | /** 265 | * Associates the specified value with the specified key in this map. 266 | * If the map previously contained a mapping for the key, the old 267 | * value is replaced. 268 | * 269 | * @param key key with which the specified value is to be associated 270 | * @param value value to be associated with the specified key 271 | * 272 | * @return the previous value associated with {@code key}, or 273 | * {@code null} if there was no mapping for {@code key}. 274 | * (A {@code null} return can also indicate that the map 275 | * previously associated {@code null} with {@code key}.) 276 | * @throws ClassCastException if the specified key cannot be compared 277 | * with the keys currently in the map 278 | * @throws NullPointerException if the specified key is null 279 | * and this map uses natural ordering, or its comparator 280 | * does not permit null keys 281 | */ 282 | // TODO 283 | private byte[] direction = new byte[100]; 284 | @SuppressWarnings("unchecked") 285 | private Entry [] stack = new Entry[100]; 286 | 287 | public V put(K key, V value) { 288 | //System.out.println("Inserting:" + key); 289 | Entry t = root; 290 | if (t == null) { 291 | compare(key, key); // type (and possibly null) check 292 | 293 | root = new Entry<>(key, value, null); 294 | size = 1; 295 | modCount++; 296 | return null; 297 | } 298 | int cmp; 299 | Entry parent; 300 | int treeDepth = 0; 301 | // split comparator and comparable paths 302 | Comparator cpr = comparator; 303 | if (cpr != null) { 304 | do { 305 | parent = t; 306 | cmp = cpr.compare(key, t.key); 307 | if (cmp < 0) { 308 | t = t.left; 309 | // TODO 310 | } else if (cmp > 0) { 311 | t = t.right; 312 | }else { 313 | return t.setValue(value); 314 | } 315 | } while (t != null); 316 | } else { 317 | if (key == null) 318 | throw new NullPointerException(); 319 | @SuppressWarnings("unchecked") 320 | Comparable k = (Comparable) key; 321 | do { 322 | parent = t; 323 | cmp = k.compareTo(t.key); 324 | if (cmp < 0) { 325 | stack[treeDepth] = parent; 326 | direction[treeDepth++] = -1; 327 | t = t.left; 328 | } else if (cmp > 0) { 329 | stack[treeDepth] = parent; 330 | direction[treeDepth++] = +1; 331 | t = t.right; 332 | } else { 333 | return t.setValue(value); 334 | } 335 | } while (t != null); 336 | } 337 | 338 | Entry e = new Entry<>(key, value, parent); 339 | if (cmp < 0) { 340 | parent.left = e; 341 | } else { 342 | parent.right = e; 343 | } 344 | fixAfterInsertion(parent, --treeDepth); 345 | 346 | size++; 347 | modCount++; 348 | return null; 349 | } 350 | 351 | private void fixAfterInsertion(Entry x, int treeDepth) { 352 | while ((x.balance += direction[treeDepth]) != 0) { 353 | if (x.balance == 2) { // right heavy by 2? 354 | if (x.right.balance == 1) { 355 | x.balance = 0; 356 | x.right.balance = 0; 357 | x = rotateLeft(x); 358 | break; 359 | } else { // x.right.balance = -1 360 | int rlBalance = x.right.left.balance; 361 | x.right.left.balance = 0; 362 | x.right.balance = 0; 363 | x.balance = 0; 364 | if (rlBalance == 1) 365 | x.balance = -1; 366 | else if (rlBalance == -1) 367 | x.right.balance = 1; 368 | 369 | x.right = rotateRight(x.right); 370 | x = rotateLeft(x); 371 | break; 372 | } 373 | } else if (x.balance == -2) { 374 | if (x.left.balance == -1) { 375 | x.balance = 0; 376 | x.left.balance = 0; 377 | x = rotateRight(x); 378 | break; 379 | } else { // x.left.balance = 1 380 | int lrBalance = x.left.right.balance; 381 | x.left.right.balance = 0; 382 | x.left.balance = 0; 383 | x.balance = 0; 384 | if (lrBalance == 1) 385 | x.left.balance = -1; 386 | else if (lrBalance == -1) 387 | x.balance = 1; 388 | 389 | x.left = rotateLeft(x.left); 390 | x = rotateRight(x); 391 | break; 392 | } 393 | } 394 | treeDepth--; 395 | if (treeDepth == -1) { 396 | return; 397 | } 398 | x = stack[treeDepth]; 399 | } 400 | // set parent reference if necessary 401 | treeDepth--; 402 | 403 | if (treeDepth == -1) 404 | root = x; 405 | else if (direction[treeDepth] == 1) 406 | stack[treeDepth].right = x; 407 | else 408 | stack[treeDepth].left = x; 409 | } 410 | 411 | private Entry rotateLeft(Entry p) { 412 | Entry r = p.right; 413 | p.right = r.left; 414 | r.left = p; 415 | rotations++; 416 | return r; 417 | } 418 | 419 | private Entry rotateRight(Entry p) { 420 | Entry l = p.left; 421 | p.left = l.right; 422 | l.right = p; 423 | rotations++; 424 | return l; 425 | } 426 | 427 | public void inOrderTraversal(Entry x) { 428 | if (x == null) 429 | return; 430 | inOrderTraversal(x.left); 431 | System.out.println(x.value + ", " + x.balance); 432 | inOrderTraversal(x.right); 433 | } 434 | 435 | /** 436 | * Removes the mapping for this key from this TreeMap if present. 437 | * 438 | * @param key key for which mapping should be removed 439 | * @return the previous value associated with {@code key}, or 440 | * {@code null} if there was no mapping for {@code key}. 441 | * (A {@code null} return can also indicate that the map 442 | * previously associated {@code null} with {@code key}.) 443 | * @throws ClassCastException if the specified key cannot be compared 444 | * with the keys currently in the map 445 | * @throws NullPointerException if the specified key is null 446 | * and this map uses natural ordering, or its comparator 447 | * does not permit null keys 448 | */ 449 | public V remove(Object key) { 450 | Entry p = getEntry(key); 451 | if (p == null) 452 | return null; 453 | 454 | V oldValue = p.value; 455 | deleteEntry(p); 456 | return oldValue; 457 | } 458 | 459 | private void deleteEntry(Entry p) { 460 | modCount++; 461 | size--; 462 | // TODO 463 | } 464 | 465 | /** 466 | * Removes all of the mappings from this map. 467 | * The map will be empty after this call returns. 468 | */ 469 | public void clear() { 470 | modCount++; 471 | size = 0; 472 | root = null; 473 | rotations = 0; 474 | } 475 | 476 | /** 477 | * Test two values for equality. Differs from o1.equals(o2) only in 478 | * that it copes with {@code null} o1 properly. 479 | */ 480 | static final boolean valEquals(Object o1, Object o2) { 481 | return (o1==null ? o2==null : o1.equals(o2)); 482 | } 483 | 484 | /** 485 | * Compares two keys using the correct comparison method for this TreeMap. 486 | */ 487 | @SuppressWarnings("unchecked") 488 | final int compare(Object k1, Object k2) { 489 | return comparator == null ? ((Comparable) k1).compareTo((K) k2) : comparator.compare((K) k1, (K) k2); 490 | } 491 | 492 | /** 493 | * Returns the key corresponding to the specified Entry. 494 | * @throws NoSuchElementException if the Entry is null 495 | */ 496 | static K key(Entry e) { 497 | if (e==null) 498 | throw new NoSuchElementException(); 499 | return e.key; 500 | } 501 | 502 | /** 503 | * Returns the first Entry in the TreeMap (according to the TreeMap's 504 | * key-sort function). Returns null if the TreeMap is empty. 505 | */ 506 | final Entry getFirstEntry() { 507 | Entry p = root; 508 | if (p != null) 509 | while (p.left != null) 510 | p = p.left; 511 | return p; 512 | } 513 | 514 | /** 515 | * Returns the last Entry in the TreeMap (according to the TreeMap's 516 | * key-sort function). Returns null if the TreeMap is empty. 517 | */ 518 | final Entry getLastEntry() { 519 | Entry p = root; 520 | if (p != null) 521 | while (p.right != null) 522 | p = p.right; 523 | return p; 524 | } 525 | 526 | /** 527 | * Returns the successor of the specified Entry, or null if no such. 528 | */ 529 | static Entry successor(Entry t) { 530 | if (t == null) 531 | return null; 532 | else if (t.right != null) { 533 | Entry p = t.right; 534 | while (p.left != null) 535 | p = p.left; 536 | return p; 537 | } else { 538 | // TODO 539 | /* 540 | Entry p = t.parent; 541 | Entry ch = t; 542 | while (p != null && ch == p.right) { 543 | ch = p; 544 | p = p.parent; 545 | } 546 | return p; 547 | */ 548 | return null; 549 | } 550 | } 551 | 552 | /** 553 | * Returns the predecessor of the specified Entry, or null if no such. 554 | */ 555 | static Entry predecessor(Entry t) { 556 | if (t == null) 557 | return null; 558 | else if (t.left != null) { 559 | Entry p = t.left; 560 | while (p.right != null) 561 | p = p.right; 562 | return p; 563 | } else { 564 | // TODO 565 | /* 566 | Entry p = t.parent; 567 | Entry ch = t; 568 | while (p != null && ch == p.left) { 569 | ch = p; 570 | p = p.parent; 571 | } 572 | return p; 573 | */ 574 | return null; 575 | } 576 | } 577 | 578 | /** 579 | * Return SimpleImmutableEntry for entry, or null if null 580 | */ 581 | static Map.Entry exportEntry(TreeMapAVLStack.Entry e) { 582 | return (e == null) ? null : 583 | new AbstractMap.SimpleImmutableEntry<>(e); 584 | } 585 | 586 | /** 587 | * Return key for entry, or null if null 588 | */ 589 | static K keyOrNull(TreeMapAVLStack.Entry e) { 590 | return (e == null) ? null : e.key; 591 | } 592 | 593 | // NavigableMap API methods 594 | 595 | /** 596 | * @since 1.6 597 | */ 598 | public Map.Entry firstEntry() { 599 | return exportEntry(getFirstEntry()); 600 | } 601 | 602 | /** 603 | * @since 1.6 604 | */ 605 | public Map.Entry lastEntry() { 606 | return exportEntry(getLastEntry()); 607 | } 608 | 609 | /** 610 | * @since 1.6 611 | */ 612 | public Map.Entry pollFirstEntry() { 613 | Entry p = getFirstEntry(); 614 | Map.Entry result = exportEntry(p); 615 | if (p != null) 616 | deleteEntry(p); 617 | return result; 618 | } 619 | 620 | /** 621 | * @since 1.6 622 | */ 623 | public Map.Entry pollLastEntry() { 624 | Entry p = getLastEntry(); 625 | Map.Entry result = exportEntry(p); 626 | if (p != null) 627 | deleteEntry(p); 628 | return result; 629 | } 630 | 631 | @Override 632 | public Set> entrySet() { 633 | // TODO Auto-generated method stub 634 | return null; 635 | } 636 | } -------------------------------------------------------------------------------- /src/main/java/bbst_showdown/AVLTreeMapRB.java: -------------------------------------------------------------------------------- 1 | package bbst_showdown; 2 | 3 | import java.util.AbstractMap; 4 | import java.util.Comparator; 5 | import java.util.Map; 6 | import java.util.NoSuchElementException; 7 | import java.util.Set; 8 | 9 | 10 | /** 11 | * A rank-balanced AVL tree implementation with only one extra bit per node - 12 | * (delta rank is one or two by definition of the rank-balanced AVL tree). 13 | * 14 | * This code is based on the paper "Rank Balanced Trees". 15 | * 16 | * At the time I wrote it I found no other similar implementations. 17 | * 18 | * TODO 19 | * I didn't code the delete method yet. If you need it email me, minimum 1 day of work needed. 20 | * 21 | * @author David McManamon 22 | * 23 | * @param the type of keys maintained by this map 24 | * @param the type of mapped values 25 | */ 26 | public class AVLTreeMapRB extends AbstractMap { 27 | // every node except the root must have a delta r of 1 or 2 28 | protected static final boolean ONE = true; 29 | protected static final boolean TWO = false; 30 | 31 | protected transient Entry root = null; 32 | 33 | /** 34 | * The number of entries in the tree 35 | */ 36 | protected transient int size = 0; 37 | 38 | /** 39 | * The comparator used to maintain order in this tree map, or 40 | * null if it uses the natural ordering of its keys. 41 | * 42 | * @serial 43 | */ 44 | protected final Comparator comparator; 45 | 46 | /** 47 | * The number of structural modifications to the tree. 48 | */ 49 | protected transient int modCount = 0; 50 | 51 | protected transient int rotations = 0; 52 | 53 | /** 54 | * Constructs a new, empty tree map, using the natural ordering of its 55 | * keys. All keys inserted into the map must implement the {@link 56 | * Comparable} interface. Furthermore, all such keys must be 57 | * mutually comparable: {@code k1.compareTo(k2)} must not throw 58 | * a {@code ClassCastException} for any keys {@code k1} and 59 | * {@code k2} in the map. If the user attempts to put a key into the 60 | * map that violates this constraint (for example, the user attempts to 61 | * put a string key into a map whose keys are integers), the 62 | * {@code put(Object key, Object value)} call will throw a 63 | * {@code ClassCastException}. 64 | */ 65 | public AVLTreeMapRB() { 66 | comparator = null; 67 | } 68 | 69 | /** 70 | * Constructs a new tree map containing the same mappings as the given 71 | * map, ordered according to the natural ordering of its keys. 72 | * All keys inserted into the new map must implement the {@link 73 | * Comparable} interface. Furthermore, all such keys must be 74 | * mutually comparable: {@code k1.compareTo(k2)} must not throw 75 | * a {@code ClassCastException} for any keys {@code k1} and 76 | * {@code k2} in the map. This method runs in n*log(n) time. 77 | * 78 | * @param m the map whose mappings are to be placed in this map 79 | * @throws ClassCastException if the keys in m are not {@link Comparable}, 80 | * or are not mutually comparable 81 | * @throws NullPointerException if the specified map is null 82 | */ 83 | public AVLTreeMapRB(Map m) { 84 | comparator = null; 85 | putAll(m); 86 | } 87 | 88 | public int treeHeight() { 89 | return treeHeight(root) - 1; 90 | } 91 | 92 | protected int treeHeight(Entry node) { 93 | if (node == null) 94 | return 0; 95 | return (1 + Math.max(treeHeight(node.left), treeHeight(node.right))); 96 | } 97 | 98 | public int rotations() { 99 | return rotations; 100 | } 101 | 102 | public String toString() { 103 | return "Rank balanced AVL tree of size: " + size + ", height: " + treeHeight() + ", rotations " + rotations; 104 | } 105 | 106 | /** 107 | * Returns the number of key-value mappings in this map. 108 | * 109 | * @return the number of key-value mappings in this map 110 | */ 111 | public int size() { 112 | return size; 113 | } 114 | 115 | /** 116 | * Returns the value to which the specified key is mapped, 117 | * or {@code null} if this map contains no mapping for the key. 118 | * 119 | *

More formally, if this map contains a mapping from a key 120 | * {@code k} to a value {@code v} such that {@code key} compares 121 | * equal to {@code k} according to the map's ordering, then this 122 | * method returns {@code v}; otherwise it returns {@code null}. 123 | * (There can be at most one such mapping.) 124 | * 125 | *

A return value of {@code null} does not necessarily 126 | * indicate that the map contains no mapping for the key; it's also 127 | * possible that the map explicitly maps the key to {@code null}. 128 | * The {@link #containsKey containsKey} operation may be used to 129 | * distinguish these two cases. 130 | * 131 | * @throws ClassCastException if the specified key cannot be compared 132 | * with the keys currently in the map 133 | * @throws NullPointerException if the specified key is null 134 | * and this map uses natural ordering, or its comparator 135 | * does not permit null keys 136 | */ 137 | public V get(Object key) { 138 | Entry p = getEntry(key); 139 | return (p==null ? null : p.value); 140 | } 141 | 142 | /** 143 | * Node in the Tree. 144 | * Doubles as a means to pass key-value pairs back to 145 | * user (see Map.Entry). 146 | */ 147 | static final class Entry implements Map.Entry { 148 | K key; 149 | V value; 150 | Entry left = null; 151 | Entry right = null; 152 | Entry parent = null; 153 | boolean deltaR = ONE; 154 | 155 | /** 156 | * Make a new cell with given key, value, and parent, and with 157 | * {@code null} child links, and delta r of 1 158 | */ 159 | Entry(K key, V value, Entry parent) { 160 | this.key = key; 161 | this.value = value; 162 | this.parent = parent; 163 | } 164 | 165 | /** 166 | * Returns the key. 167 | * 168 | * @return the key 169 | */ 170 | public K getKey() { 171 | return key; 172 | } 173 | 174 | /** 175 | * Returns the value associated with the key. 176 | * 177 | * @return the value associated with the key 178 | */ 179 | public V getValue() { 180 | return value; 181 | } 182 | 183 | /** 184 | * Replaces the value currently associated with the key with the given 185 | * value. 186 | * 187 | * @return the value associated with the key before this method was 188 | * called 189 | */ 190 | public V setValue(V value) { 191 | V oldValue = this.value; 192 | this.value = value; 193 | return oldValue; 194 | } 195 | 196 | public boolean equals(Object o) { 197 | if (!(o instanceof Map.Entry)) 198 | return false; 199 | Map.Entry e = (Map.Entry)o; 200 | 201 | return valEquals(key,e.getKey()) && valEquals(value,e.getValue()); 202 | } 203 | 204 | public int hashCode() { 205 | int keyHash = (key==null ? 0 : key.hashCode()); 206 | int valueHash = (value==null ? 0 : value.hashCode()); 207 | return keyHash ^ valueHash; 208 | } 209 | 210 | public String toString() { 211 | return key + "=" + value + "," + deltaR; 212 | } 213 | } 214 | 215 | /** 216 | * Returns this map's entry for the given key, or {@code null} if the map 217 | * does not contain an entry for the key. 218 | * 219 | * @return this map's entry for the given key, or {@code null} if the map 220 | * does not contain an entry for the key 221 | * @throws ClassCastException if the specified key cannot be compared 222 | * with the keys currently in the map 223 | * @throws NullPointerException if the specified key is null 224 | * and this map uses natural ordering, or its comparator 225 | * does not permit null keys 226 | */ 227 | final Entry getEntry(Object key) { 228 | // Offload comparator-based version for sake of performance 229 | if (comparator != null) 230 | return getEntryUsingComparator(key); 231 | if (key == null) 232 | throw new NullPointerException(); 233 | @SuppressWarnings("unchecked") 234 | Comparable k = (Comparable) key; 235 | Entry p = root; 236 | while (p != null) { 237 | int cmp = k.compareTo(p.key); 238 | if (cmp < 0) 239 | p = p.left; 240 | else if (cmp > 0) 241 | p = p.right; 242 | else 243 | return p; 244 | } 245 | return null; 246 | } 247 | 248 | /** 249 | * Version of getEntry using comparator. Split off from getEntry 250 | * for performance. (This is not worth doing for most methods, 251 | * that are less dependent on comparator performance, but is 252 | * worthwhile here.) 253 | */ 254 | final Entry getEntryUsingComparator(Object key) { 255 | @SuppressWarnings("unchecked") 256 | K k = (K) key; 257 | Comparator cpr = comparator; 258 | if (cpr != null) { 259 | Entry p = root; 260 | while (p != null) { 261 | int cmp = cpr.compare(k, p.key); 262 | if (cmp < 0) 263 | p = p.left; 264 | else if (cmp > 0) 265 | p = p.right; 266 | else 267 | return p; 268 | } 269 | } 270 | return null; 271 | } 272 | 273 | /** 274 | * Associates the specified value with the specified key in this map. 275 | * If the map previously contained a mapping for the key, the old 276 | * value is replaced. 277 | * 278 | * @param key key with which the specified value is to be associated 279 | * @param value value to be associated with the specified key 280 | * 281 | * @return the previous value associated with {@code key}, or 282 | * {@code null} if there was no mapping for {@code key}. 283 | * (A {@code null} return can also indicate that the map 284 | * previously associated {@code null} with {@code key}.) 285 | * @throws ClassCastException if the specified key cannot be compared 286 | * with the keys currently in the map 287 | * @throws NullPointerException if the specified key is null 288 | * and this map uses natural ordering, or its comparator 289 | * does not permit null keys 290 | */ 291 | public V put(K key, V value) { 292 | Entry t = root; 293 | if (t == null) { 294 | compare(key, key); // type (and possibly null) check 295 | 296 | root = new Entry<>(key, value, null); 297 | size = 1; 298 | modCount++; 299 | return null; 300 | } 301 | int cmp; 302 | Entry parent; 303 | // split comparator and comparable paths 304 | Comparator cpr = comparator; 305 | if (cpr != null) { 306 | do { 307 | parent = t; 308 | cmp = cpr.compare(key, t.key); 309 | if (cmp < 0) 310 | t = t.left; 311 | else if (cmp > 0) 312 | t = t.right; 313 | else 314 | return t.setValue(value); 315 | } while (t != null); 316 | } else { 317 | if (key == null) 318 | throw new NullPointerException(); 319 | @SuppressWarnings("unchecked") 320 | Comparable k = (Comparable) key; 321 | do { 322 | parent = t; 323 | cmp = k.compareTo(t.key); 324 | if (cmp < 0) 325 | t = t.left; 326 | else if (cmp > 0) 327 | t = t.right; 328 | else 329 | return t.setValue(value); 330 | } while (t != null); 331 | } 332 | 333 | Entry e = new Entry<>(key, value, parent); 334 | Entry sibling; 335 | if (cmp < 0) { 336 | parent.left = e; 337 | sibling = parent.right; 338 | } else { 339 | parent.right = e; 340 | sibling = parent.left; 341 | } 342 | 343 | if (sibling == null) 344 | fixAfterInsertion(parent); 345 | 346 | size++; 347 | modCount++; 348 | return null; 349 | } 350 | 351 | /** 352 | If the path of incremented ranks reaches the root of the tree, then the rebalancing procedure stops. 353 | If the path of incremented ranks reaches a node whose parent's rank previously differed by two, the rebalancing procedure stops. 354 | If the procedure increases the rank of a node x, so that it becomes equal to the rank of the parent y of x, 355 | but the other child of y has a rank that is smaller by two (so that the rank of y cannot be increased) 356 | then again the rebalancing procedure stops after performing rotations necessary. 357 | */ 358 | private void fixAfterInsertion(Entry x) { 359 | while (x.deltaR != TWO && x.parent != null) { 360 | Entry p = x.parent; 361 | if (p.left == x) { // node was added on left so check if left side is unbalanced 362 | Entry sibling = p.right; 363 | if (sibling == null || sibling.deltaR == TWO) { // need to rebalance 364 | if (sibling != null) 365 | sibling.deltaR = ONE; 366 | 367 | if (x.right != null) { 368 | if(x.right.deltaR == ONE) { 369 | if (x.left != null) x.left.deltaR = ONE; 370 | rotateLeft(x); 371 | } else 372 | x.right.deltaR = ONE; 373 | } 374 | 375 | if (p.deltaR == TWO) { // maintain delta 2 at parent 376 | p.deltaR = ONE; 377 | p.left.deltaR = TWO; 378 | } 379 | rotateRight(p); 380 | return; 381 | } else if (sibling.deltaR == ONE) { 382 | sibling.deltaR = TWO; 383 | } 384 | } else { // checking right side heavy 385 | Entry sibling = p.left; 386 | if (sibling == null || sibling.deltaR == TWO) { // need to rebalance 387 | if (sibling != null) 388 | sibling.deltaR = ONE; 389 | 390 | if (x.left != null) { 391 | if (x.left.deltaR == ONE) { 392 | if (x.right != null) x.right.deltaR = ONE; 393 | rotateRight(x); 394 | } else 395 | x.left.deltaR = ONE; 396 | } 397 | 398 | if (p.deltaR == TWO) { // maintain delta 2 at parent 399 | p.deltaR = ONE; 400 | p.right.deltaR = TWO; 401 | } 402 | rotateLeft(p); 403 | return; 404 | } else if (sibling.deltaR == ONE) { 405 | sibling.deltaR = TWO; 406 | } 407 | } 408 | 409 | x = x.parent; 410 | } 411 | if (x.deltaR == TWO) 412 | x.deltaR=ONE; 413 | } 414 | 415 | 416 | /** From CLR */ 417 | private void rotateLeft(Entry p) { 418 | Entry r = p.right; 419 | p.right = r.left; 420 | if (r.left != null) 421 | r.left.parent = p; 422 | r.parent = p.parent; 423 | if (p.parent == null) 424 | root = r; 425 | else if (p.parent.left == p) 426 | p.parent.left = r; 427 | else 428 | p.parent.right = r; 429 | r.left = p; 430 | p.parent = r; 431 | rotations++; 432 | } 433 | 434 | /** From CLR */ 435 | private void rotateRight(Entry p) { 436 | Entry l = p.left; 437 | p.left = l.right; 438 | if (l.right != null) 439 | l.right.parent = p; 440 | l.parent = p.parent; 441 | if (p.parent == null) 442 | root = l; 443 | else if (p.parent.right == p) 444 | p.parent.right = l; 445 | else 446 | p.parent.left = l; 447 | l.right = p; 448 | p.parent = l; 449 | rotations++; 450 | } 451 | 452 | /** 453 | * Removes the mapping for this key from this TreeMap if present. 454 | * 455 | * @param key key for which mapping should be removed 456 | * @return the previous value associated with {@code key}, or 457 | * {@code null} if there was no mapping for {@code key}. 458 | * (A {@code null} return can also indicate that the map 459 | * previously associated {@code null} with {@code key}.) 460 | * @throws ClassCastException if the specified key cannot be compared 461 | * with the keys currently in the map 462 | * @throws NullPointerException if the specified key is null 463 | * and this map uses natural ordering, or its comparator 464 | * does not permit null keys 465 | */ 466 | public V remove(Object key) { 467 | Entry p = getEntry(key); 468 | if (p == null) 469 | return null; 470 | 471 | V oldValue = p.value; 472 | deleteEntry(p); 473 | return oldValue; 474 | } 475 | 476 | /** 477 | * Delete node p, and then rebalance the tree. 478 | */ 479 | private void deleteEntry(Entry p) { 480 | modCount++; 481 | size--; 482 | 483 | // If strictly internal, copy successor's element to p and then make p 484 | // point to successor. 485 | if (p.left != null && p.right != null) { 486 | Entry s = successor(p); 487 | p.key = s.key; 488 | p.value = s.value; 489 | p = s; 490 | } // p has 2 children 491 | 492 | // Start fixup at replacement node, if it exists. 493 | Entry replacement = (p.left != null ? p.left : p.right); 494 | 495 | if (replacement != null) { 496 | // Link replacement to parent 497 | replacement.parent = p.parent; 498 | Entry mirror = null; 499 | if (p.parent == null) { 500 | root = replacement; 501 | return; 502 | } else if (p == p.parent.left) { 503 | p.parent.left = replacement; 504 | mirror = p.parent.right; 505 | } else { 506 | p.parent.right = replacement; 507 | mirror = p.parent.left; 508 | } 509 | 510 | // Null out links so they are OK to use by fixAfterDeletion. 511 | p.left = p.right = p.parent = null; 512 | //TODO 513 | fixAfterDeletion(replacement.parent, mirror); 514 | } else if (p.parent == null) { // return if we are the only node. 515 | root = null; 516 | } else { // No children. Use self as phantom replacement and unlink. 517 | // TODO check if null check necessary? 518 | Entry fixPoint = p.parent; 519 | @SuppressWarnings("unused") 520 | Entry sibling = null; 521 | 522 | if (p == p.parent.left) { 523 | p.parent.left = null; 524 | sibling = fixPoint.right; 525 | } else if (p == p.parent.right) { 526 | p.parent.right = null; 527 | sibling = fixPoint.left; 528 | } 529 | p.parent = null; 530 | 531 | // TODO 532 | // if (mirror == null || (fixPoint.rank - p.rank >= 2) || (fixPoint.rank - mirror.rank) != 1 ) { 533 | fixAfterDeletion(fixPoint, null); 534 | // } 535 | } 536 | } 537 | 538 | private void fixAfterDeletion(Entry p, Entry mirror) { 539 | throw new RuntimeException(); 540 | } 541 | 542 | /** 543 | * Removes all of the mappings from this map. 544 | * The map will be empty after this call returns. 545 | */ 546 | public void clear() { 547 | modCount++; 548 | size = 0; 549 | root = null; 550 | rotations = 0; 551 | } 552 | 553 | /** 554 | * Test two values for equality. Differs from o1.equals(o2) only in 555 | * that it copes with {@code null} o1 properly. 556 | */ 557 | static final boolean valEquals(Object o1, Object o2) { 558 | return (o1==null ? o2==null : o1.equals(o2)); 559 | } 560 | 561 | /** 562 | * Compares two keys using the correct comparison method for this TreeMap. 563 | */ 564 | @SuppressWarnings("unchecked") 565 | final int compare(Object k1, Object k2) { 566 | return comparator == null ? ((Comparable) k1).compareTo((K) k2) : comparator.compare((K) k1, (K) k2); 567 | } 568 | 569 | /** 570 | * Returns the key corresponding to the specified Entry. 571 | * 572 | * @throws NoSuchElementException 573 | * if the Entry is null 574 | */ 575 | static K key(Entry e) { 576 | if (e==null) 577 | throw new NoSuchElementException(); 578 | return e.key; 579 | } 580 | 581 | /** 582 | * Returns the first Entry in the TreeMap (according to the TreeMap's 583 | * key-sort function). Returns null if the TreeMap is empty. 584 | */ 585 | final Entry getFirstEntry() { 586 | Entry p = root; 587 | if (p != null) 588 | while (p.left != null) 589 | p = p.left; 590 | return p; 591 | } 592 | 593 | /** 594 | * Returns the last Entry in the TreeMap (according to the TreeMap's 595 | * key-sort function). Returns null if the TreeMap is empty. 596 | */ 597 | final Entry getLastEntry() { 598 | Entry p = root; 599 | if (p != null) 600 | while (p.right != null) 601 | p = p.right; 602 | return p; 603 | } 604 | 605 | /** 606 | * Returns the successor of the specified Entry, or null if no such. 607 | */ 608 | static Entry successor(Entry t) { 609 | if (t == null) 610 | return null; 611 | else if (t.right != null) { 612 | Entry p = t.right; 613 | while (p.left != null) 614 | p = p.left; 615 | return p; 616 | } else { 617 | Entry p = t.parent; 618 | Entry ch = t; 619 | while (p != null && ch == p.right) { 620 | ch = p; 621 | p = p.parent; 622 | } 623 | return p; 624 | } 625 | } 626 | 627 | /** 628 | * Returns the predecessor of the specified Entry, or null if no such. 629 | */ 630 | static Entry predecessor(Entry t) { 631 | if (t == null) 632 | return null; 633 | else if (t.left != null) { 634 | Entry p = t.left; 635 | while (p.right != null) 636 | p = p.right; 637 | return p; 638 | } else { 639 | Entry p = t.parent; 640 | Entry ch = t; 641 | while (p != null && ch == p.left) { 642 | ch = p; 643 | p = p.parent; 644 | } 645 | return p; 646 | } 647 | } 648 | 649 | @Override 650 | public Set> entrySet() { 651 | // TODO Auto-generated method stub 652 | return null; 653 | } 654 | 655 | public void inOrderTraversal(Entry x) { 656 | if (x == null) 657 | return; 658 | inOrderTraversal(x.left); 659 | System.out.println(x.value + ", " + x.deltaR); 660 | inOrderTraversal(x.right); 661 | } 662 | 663 | public boolean identicalTrees(Entry a, AVLTreeMap.Entry b) { 664 | /* 1. both empty */ 665 | if (a == null && b == null) 666 | return true; 667 | 668 | /* 2. both non-empty -> compare them */ 669 | if (a != null && b != null) 670 | return (a.value == b.value && identicalTrees(a.left, b.left) && identicalTrees(a.right, b.right)); 671 | 672 | /* 3. one empty, one not -> false */ 673 | return false; 674 | } 675 | } -------------------------------------------------------------------------------- /src/main/java/bbst_showdown/WAVLTreeMap.java: -------------------------------------------------------------------------------- 1 | package bbst_showdown; 2 | 3 | import java.util.AbstractMap; 4 | import java.util.AbstractSet; 5 | import java.util.Comparator; 6 | import java.util.ConcurrentModificationException; 7 | import java.util.Iterator; 8 | import java.util.Map; 9 | import java.util.NoSuchElementException; 10 | import java.util.Set; 11 | import java.util.Spliterator; 12 | 13 | /** 14 | * The WAVL tree combines elements of AVL & Red-black trees. 15 | * 16 | * The WAVL tree deletion is described in the 2015 paper "Rank Balanced Trees" 17 | * The related RAVL tree is described in the 2016 paper "Deletion Without Rebalancing in Binary Search Trees" 18 | * both are available at: 19 | * http://sidsen.azurewebsites.net// 20 | * 21 | * @author David McManamon 22 | * 23 | * @param the type of keys maintained by this map 24 | * @param the type of mapped values 25 | */ 26 | public class WAVLTreeMap extends AbstractMap { 27 | 28 | protected transient Entry root = null; 29 | 30 | /** 31 | * The number of entries in the tree 32 | */ 33 | protected transient int size = 0; 34 | 35 | /** 36 | * The comparator used to maintain order in this tree map, or 37 | * null if it uses the natural ordering of its keys. 38 | * 39 | * @serial 40 | */ 41 | protected final Comparator comparator; 42 | 43 | /** 44 | * The number of structural modifications to the tree. 45 | */ 46 | protected transient int modCount = 0; 47 | 48 | protected transient int rotations = 0; 49 | 50 | protected boolean deleteWAVL = false; 51 | 52 | 53 | public WAVLTreeMap() { 54 | this.comparator = null; 55 | } 56 | /** 57 | * Constructs a new, empty tree map, using the natural ordering of its 58 | * keys. All keys inserted into the map must implement the {@link 59 | * Comparable} interface. Furthermore, all such keys must be 60 | * mutually comparable: {@code k1.compareTo(k2)} must not throw 61 | * a {@code ClassCastException} for any keys {@code k1} and 62 | * {@code k2} in the map. If the user attempts to put a key into the 63 | * map that violates this constraint (for example, the user attempts to 64 | * put a string key into a map whose keys are integers), the 65 | * {@code put(Object key, Object value)} call will throw a 66 | * {@code ClassCastException}. 67 | */ 68 | public WAVLTreeMap(boolean deleteWAVL) { 69 | this.deleteWAVL = deleteWAVL; 70 | this.comparator = null; 71 | } 72 | 73 | /** 74 | * Constructs a new tree map containing the same mappings as the given 75 | * map, ordered according to the natural ordering of its keys. 76 | * All keys inserted into the new map must implement the {@link 77 | * Comparable} interface. Furthermore, all such keys must be 78 | * mutually comparable: {@code k1.compareTo(k2)} must not throw 79 | * a {@code ClassCastException} for any keys {@code k1} and 80 | * {@code k2} in the map. This method runs in n*log(n) time. 81 | * 82 | * @param m the map whose mappings are to be placed in this map 83 | * @throws ClassCastException if the keys in m are not {@link Comparable}, 84 | * or are not mutually comparable 85 | * @throws NullPointerException if the specified map is null 86 | */ 87 | public WAVLTreeMap(Map m) { 88 | comparator = null; 89 | putAll(m); 90 | } 91 | 92 | public int treeHeight() { 93 | return treeHeight(root) - 1; 94 | } 95 | 96 | protected int treeHeight(Entry node) { 97 | if (node == null) 98 | return 0; 99 | return (1 + Math.max(treeHeight(node.left), treeHeight(node.right))); 100 | } 101 | 102 | public int rotations() { 103 | return rotations; 104 | } 105 | 106 | public String toString() { 107 | return "WAVL tree of size: " + size + ", height: " + treeHeight() + ", rotations " + rotations + " WAVL deletes: " + deleteWAVL + " root:" + root; 108 | } 109 | 110 | /** 111 | * Returns the number of key-value mappings in this map. 112 | * 113 | * @return the number of key-value mappings in this map 114 | */ 115 | public int size() { 116 | return size; 117 | } 118 | 119 | /** 120 | * Returns the value to which the specified key is mapped, 121 | * or {@code null} if this map contains no mapping for the key. 122 | * 123 | *

More formally, if this map contains a mapping from a key 124 | * {@code k} to a value {@code v} such that {@code key} compares 125 | * equal to {@code k} according to the map's ordering, then this 126 | * method returns {@code v}; otherwise it returns {@code null}. 127 | * (There can be at most one such mapping.) 128 | * 129 | *

A return value of {@code null} does not necessarily 130 | * indicate that the map contains no mapping for the key; it's also 131 | * possible that the map explicitly maps the key to {@code null}. 132 | * The {@link #containsKey containsKey} operation may be used to 133 | * distinguish these two cases. 134 | * 135 | * @throws ClassCastException if the specified key cannot be compared 136 | * with the keys currently in the map 137 | * @throws NullPointerException if the specified key is null 138 | * and this map uses natural ordering, or its comparator 139 | * does not permit null keys 140 | */ 141 | public V get(Object key) { 142 | Entry p = getEntry(key); 143 | return (p==null ? null : p.value); 144 | } 145 | 146 | /** 147 | * Node in the Tree. 148 | * Doubles as a means to pass key-value pairs back to 149 | * user (see Map.Entry). 150 | */ 151 | static final class Entry implements Map.Entry { 152 | K key; 153 | V value; 154 | Entry left = null; 155 | Entry right = null; 156 | Entry parent = null; 157 | byte rank = 0; 158 | 159 | /** 160 | * Make a new cell with given key, value, and parent, and with 161 | * {@code null} child links, and BLACK color. 162 | */ 163 | Entry(K key, V value, Entry parent) { 164 | this.key = key; 165 | this.value = value; 166 | this.parent = parent; 167 | } 168 | 169 | Entry() { 170 | rank = -1; 171 | } 172 | 173 | /** 174 | * Returns the key. 175 | * 176 | * @return the key 177 | */ 178 | public K getKey() { 179 | return key; 180 | } 181 | 182 | /** 183 | * Returns the value associated with the key. 184 | * 185 | * @return the value associated with the key 186 | */ 187 | public V getValue() { 188 | return value; 189 | } 190 | 191 | /** 192 | * Replaces the value currently associated with the key with the given 193 | * value. 194 | * 195 | * @return the value associated with the key before this method was 196 | * called 197 | */ 198 | public V setValue(V value) { 199 | V oldValue = this.value; 200 | this.value = value; 201 | return oldValue; 202 | } 203 | 204 | public boolean equals(Object o) { 205 | if (!(o instanceof Map.Entry)) 206 | return false; 207 | Map.Entry e = (Map.Entry)o; 208 | 209 | return valEquals(key,e.getKey()) && valEquals(value,e.getValue()); 210 | } 211 | 212 | public int hashCode() { 213 | int keyHash = (key==null ? 0 : key.hashCode()); 214 | int valueHash = (value==null ? 0 : value.hashCode()); 215 | return keyHash ^ valueHash; 216 | } 217 | 218 | public String toString() { 219 | return key + "=" + value + "," + rank; 220 | } 221 | } 222 | 223 | /** 224 | * Returns this map's entry for the given key, or {@code null} if the map 225 | * does not contain an entry for the key. 226 | * 227 | * @return this map's entry for the given key, or {@code null} if the map 228 | * does not contain an entry for the key 229 | * @throws ClassCastException if the specified key cannot be compared 230 | * with the keys currently in the map 231 | * @throws NullPointerException if the specified key is null 232 | * and this map uses natural ordering, or its comparator 233 | * does not permit null keys 234 | */ 235 | final Entry getEntry(Object key) { 236 | // Offload comparator-based version for sake of performance 237 | if (comparator != null) 238 | return getEntryUsingComparator(key); 239 | if (key == null) 240 | throw new NullPointerException(); 241 | @SuppressWarnings("unchecked") 242 | Comparable k = (Comparable) key; 243 | Entry p = root; 244 | while (p != null) { 245 | int cmp = k.compareTo(p.key); 246 | if (cmp < 0) 247 | p = p.left; 248 | else if (cmp > 0) 249 | p = p.right; 250 | else 251 | return p; 252 | } 253 | return null; 254 | } 255 | 256 | /** 257 | * Version of getEntry using comparator. Split off from getEntry 258 | * for performance. (This is not worth doing for most methods, 259 | * that are less dependent on comparator performance, but is 260 | * worthwhile here.) 261 | */ 262 | final Entry getEntryUsingComparator(Object key) { 263 | @SuppressWarnings("unchecked") 264 | K k = (K) key; 265 | Comparator cpr = comparator; 266 | if (cpr != null) { 267 | Entry p = root; 268 | while (p != null) { 269 | int cmp = cpr.compare(k, p.key); 270 | if (cmp < 0) 271 | p = p.left; 272 | else if (cmp > 0) 273 | p = p.right; 274 | else 275 | return p; 276 | } 277 | } 278 | return null; 279 | } 280 | 281 | /** 282 | * Associates the specified value with the specified key in this map. 283 | * If the map previously contained a mapping for the key, the old 284 | * value is replaced. 285 | * 286 | * @param key key with which the specified value is to be associated 287 | * @param value value to be associated with the specified key 288 | * 289 | * @return the previous value associated with {@code key}, or 290 | * {@code null} if there was no mapping for {@code key}. 291 | * (A {@code null} return can also indicate that the map 292 | * previously associated {@code null} with {@code key}.) 293 | * @throws ClassCastException if the specified key cannot be compared 294 | * with the keys currently in the map 295 | * @throws NullPointerException if the specified key is null 296 | * and this map uses natural ordering, or its comparator 297 | * does not permit null keys 298 | */ 299 | public V put(K key, V value) { 300 | Entry t = root; 301 | if (t == null) { 302 | compare(key, key); // type (and possibly null) check 303 | 304 | root = new Entry<>(key, value, null); 305 | size = 1; 306 | modCount++; 307 | return null; 308 | } 309 | int cmp; 310 | Entry parent; 311 | // split comparator and comparable paths 312 | Comparator cpr = comparator; 313 | if (cpr != null) { 314 | do { 315 | parent = t; 316 | cmp = cpr.compare(key, t.key); 317 | if (cmp < 0) 318 | t = t.left; 319 | else if (cmp > 0) 320 | t = t.right; 321 | else 322 | return t.setValue(value); 323 | } while (t != null); 324 | } else { 325 | if (key == null) 326 | throw new NullPointerException(); 327 | @SuppressWarnings("unchecked") 328 | Comparable k = (Comparable) key; 329 | do { 330 | parent = t; 331 | cmp = k.compareTo(t.key); 332 | if (cmp < 0) 333 | t = t.left; 334 | else if (cmp > 0) 335 | t = t.right; 336 | else 337 | return t.setValue(value); 338 | } while (t != null); 339 | } 340 | 341 | Entry e = new Entry<>(key, value, parent); 342 | if (cmp < 0) { 343 | parent.left = e; 344 | } else { 345 | parent.right = e; 346 | } 347 | 348 | if (parent.rank == 0) { 349 | parent.rank++; 350 | fixAfterInsert(parent); 351 | } 352 | 353 | size++; 354 | modCount++; 355 | return null; 356 | } 357 | 358 | public void inOrderTraversal(Entry x) { 359 | if (x == null) 360 | return; 361 | inOrderTraversal(x.left); 362 | System.out.println(x.value + ", " + x.rank); 363 | inOrderTraversal(x.right); 364 | } 365 | 366 | /** 367 | - If the path of incremented ranks reaches the root of the tree stop. 368 | - If the path of incremented ranks reaches a node whose parent's rank previously differed by two and after incrementing now differ by one stop. 369 | - If the procedure increases the rank of a node x, so that it becomes equal to the rank of the parent y of x, 370 | but the other child of y has a rank that is smaller by two (so that the rank of y cannot be increased) 371 | then again the rebalancing procedure stops after performing rotations necessary. 372 | In other words: 373 | After insertion rank difference is 1,2 or 3 - 374 | check these three cases stopping after any rotations, reaching the root or when rank difference was 2 before the insertion. 375 | */ 376 | private void fixAfterInsert(Entry x) { 377 | for (Entry parent = x.parent; 378 | parent != null && x.rank + 1 != parent.rank; x.rank++) { 379 | if (parent.left == x) { // new node was added on the left 380 | if (needToRotateRight(parent)) { 381 | if (x.left == null || x.rank >= x.left.rank + 2) { 382 | x.rank--; 383 | x.right.rank++; 384 | rotateLeft(x); 385 | } 386 | parent.rank--; 387 | rotateRight(parent); 388 | break; 389 | } 390 | } else { 391 | if (needToRotateLeft(parent)) { 392 | if (x.right == null || x.rank >= x.right.rank + 2) { 393 | x.rank--; 394 | x.left.rank++; 395 | rotateRight(x); 396 | } 397 | parent.rank--; 398 | rotateLeft(parent); 399 | break; 400 | } 401 | } 402 | x = parent; 403 | parent = x.parent; 404 | } 405 | } 406 | 407 | // check if sibling node has a rank difference of 2 408 | private boolean needToRotateLeft(Entry p) { 409 | if (p.left == null) { // rank of sibling is -1 410 | if (p.rank == 1) 411 | return true; 412 | return false; 413 | } else if (p.rank >= p.left.rank + 2) 414 | return true; 415 | return false; 416 | } 417 | 418 | // check if sibling node has a rank difference of 2 or greater (RAVL) 419 | private boolean needToRotateRight(Entry p) { 420 | if (p.right == null) { // rank of sibling is -1 421 | if (p.rank == 1) 422 | return true; 423 | return false; 424 | } else if (p.rank >= p.right.rank + 2) 425 | return true; 426 | return false; 427 | } 428 | 429 | /** From CLR */ 430 | private void rotateLeft(Entry p) { 431 | Entry r = p.right; 432 | p.right = r.left; 433 | if (r.left != null) 434 | r.left.parent = p; 435 | r.parent = p.parent; 436 | if (p.parent == null) 437 | root = r; 438 | else if (p.parent.left == p) 439 | p.parent.left = r; 440 | else 441 | p.parent.right = r; 442 | r.left = p; 443 | p.parent = r; 444 | rotations++; 445 | } 446 | 447 | /** From CLR */ 448 | private void rotateRight(Entry p) { 449 | Entry l = p.left; 450 | p.left = l.right; 451 | if (l.right != null) 452 | l.right.parent = p; 453 | l.parent = p.parent; 454 | if (p.parent == null) 455 | root = l; 456 | else if (p.parent.right == p) 457 | p.parent.right = l; 458 | else 459 | p.parent.left = l; 460 | l.right = p; 461 | p.parent = l; 462 | rotations++; 463 | } 464 | 465 | /** 466 | * Removes the mapping for this key from this TreeMap if present. 467 | * 468 | * @param key key for which mapping should be removed 469 | * @return the previous value associated with {@code key}, or 470 | * {@code null} if there was no mapping for {@code key}. 471 | * (A {@code null} return can also indicate that the map 472 | * previously associated {@code null} with {@code key}.) 473 | * @throws ClassCastException if the specified key cannot be compared 474 | * with the keys currently in the map 475 | * @throws NullPointerException if the specified key is null 476 | * and this map uses natural ordering, or its comparator 477 | * does not permit null keys 478 | */ 479 | public V remove(Object key) { 480 | Entry p = getEntry(key); 481 | if (p == null) 482 | return null; 483 | 484 | V oldValue = p.value; 485 | deleteEntry(p); 486 | return oldValue; 487 | } 488 | 489 | /** 490 | * Delete node p, and then rebalance the tree. 491 | */ 492 | private void deleteEntry(Entry p) { 493 | modCount++; 494 | size--; 495 | 496 | // If strictly internal, copy successor's element to p and then make p 497 | // point to successor. 498 | if (p.left != null && p.right != null) { 499 | Entry s = predecessor(p); 500 | p.key = s.key; 501 | p.value = s.value; 502 | p = s; 503 | } // p has 2 children 504 | 505 | Entry replacement = (p.left != null ? p.left : p.right); 506 | if (replacement != null) { 507 | // Link replacement to parent 508 | replacement.parent = p.parent; 509 | Entry sibling = null; 510 | if (p.parent == null) { 511 | root = replacement; 512 | return; 513 | } else if (p == p.parent.left) { 514 | p.parent.left = replacement; 515 | sibling = p.parent.right; 516 | } else { 517 | p.parent.right = replacement; 518 | sibling = p.parent.left; 519 | } 520 | 521 | // Null out links so they are OK to use by fixAfterDeletion. 522 | p.left = p.right = p.parent = null; 523 | if (deleteWAVL) 524 | fixAfterDeleteWAVL(replacement.parent, sibling, replacement); 525 | else 526 | fixAfterDeleteAVL(replacement.parent, sibling, replacement); 527 | } else if (p.parent == null) { // return if we are the only node. 528 | root = null; 529 | } else { // No children. Use self as phantom replacement and unlink. 530 | Entry fixPoint = p.parent; 531 | Entry sibling = null; 532 | 533 | if (p == p.parent.left) { 534 | p.parent.left = null; 535 | sibling = fixPoint.right; 536 | } else if (p == p.parent.right) { 537 | p.parent.right = null; 538 | sibling = fixPoint.left; 539 | } 540 | p.parent = null; 541 | p.rank--; 542 | if (deleteWAVL) 543 | fixAfterDeleteWAVL(fixPoint, sibling, p); 544 | else 545 | fixAfterDeleteAVL(fixPoint, sibling, p); 546 | } 547 | } 548 | 549 | private byte rank(final Entry node) { 550 | return (node == null) ? -1 : node.rank; 551 | } 552 | 553 | private boolean nodeIsTwoTwo(Entry node) { 554 | if (node == null || node.rank == 0) 555 | return false; 556 | if (node.rank == 1) { 557 | if (node.left == null && node.right == null) 558 | return true; 559 | else 560 | return false; 561 | } else 562 | return (node.left.rank == node.right.rank && node.left.rank + 2 == node.rank); 563 | } 564 | 565 | private void fixAfterDeleteWAVL(Entry parent, Entry sibling, Entry node) { 566 | int deltaRank = parent.rank - node.rank; 567 | while (deltaRank == 3 || parent.rank == 1 && nodeIsTwoTwo(parent)) { 568 | int deltaRankSibling = (sibling == null) ? parent.rank + 1 : parent.rank - sibling.rank; 569 | if (deltaRankSibling == 2) { 570 | parent.rank--; // demote and continue loop 571 | } else { 572 | int deltaRankSiblingL = sibling.rank - rank(sibling.left); 573 | int deltaRankSiblingR = sibling.rank - rank(sibling.right); 574 | 575 | if (deltaRankSiblingL == 2 && deltaRankSiblingR == 2) { 576 | // "double demote" in the orig. paper since both parent & sibling demote 577 | parent.rank--; 578 | sibling.rank--; 579 | } else if (parent.right == sibling) { // delete was on the left 580 | if (deltaRankSiblingR == 1) { // single rotation 581 | sibling.rank++; 582 | parent.rank--; 583 | if (sibling.left == null) 584 | parent.rank--; // demote parent again 585 | rotateLeft(parent); 586 | } else { // double rotation 587 | parent.rank -= 2; 588 | sibling.rank--; 589 | sibling.left.rank += 2; 590 | rotateRight(sibling); 591 | rotateLeft(parent); 592 | } 593 | break; 594 | } else { // delete was on the right 595 | if (deltaRankSiblingL == 1) { // single rotation 596 | sibling.rank++; 597 | parent.rank--; 598 | if (sibling.right == null) 599 | parent.rank--; // demote parent again 600 | rotateRight(parent); 601 | } else { // double rotation 602 | parent.rank -= 2; 603 | sibling.rank--; 604 | sibling.right.rank += 2; 605 | rotateLeft(sibling); 606 | rotateRight(parent); 607 | } 608 | break; 609 | } 610 | } 611 | 612 | if (parent.parent == null) 613 | return; 614 | node = parent; 615 | parent = parent.parent; 616 | sibling = (parent.left == node) ? parent.right : parent.left; 617 | deltaRank = parent.rank - node.rank; 618 | } 619 | } 620 | 621 | /* 622 | * delete re-tracing via balance factor 623 | */ 624 | private void fixAfterDeleteAVL(Entry parent, Entry sibling, Entry node) { 625 | int balance; 626 | if (sibling == null) // remove sibling null check inside loop by testing once here 627 | balance = -1 - node.rank; 628 | else 629 | balance = sibling.rank - node.rank; 630 | 631 | while (balance != 1) { // balance == 1 means prior to delete parent was balanced, break; 632 | if (balance == 0) {// side of delete was taller, decrement and continue 633 | parent.rank--; 634 | } else if (parent.left == sibling) { 635 | parent.rank -= 2; 636 | int siblingBalance = rank(sibling.right) - rank(sibling.left); 637 | if (siblingBalance == 0) { // parent height unchanged after rotate so break 638 | sibling.rank++; 639 | parent.rank++; 640 | rotateRight(parent); 641 | break; 642 | } else if (siblingBalance > 0) { 643 | sibling.right.rank++; 644 | sibling.rank--; 645 | rotateLeft(sibling); 646 | } 647 | rotateRight(parent); 648 | parent = parent.parent; 649 | } else { // delete on left 650 | parent.rank -= 2; 651 | int siblingBalance = rank(sibling.right) - rank(sibling.left); 652 | if (siblingBalance == 0) { // parent height unchanged after rotate so break 653 | sibling.rank++; 654 | parent.rank++; 655 | rotateLeft(parent); 656 | break; 657 | } else if (siblingBalance < 0) { 658 | sibling.left.rank++; 659 | sibling.rank--; 660 | rotateRight(sibling); 661 | } 662 | rotateLeft(parent); 663 | parent = parent.parent; 664 | } 665 | 666 | if (parent.parent == null) 667 | return; 668 | node = parent; 669 | parent = parent.parent; 670 | sibling = (parent.left == node) ? parent.right : parent.left; 671 | balance = sibling.rank - node.rank; 672 | } 673 | } 674 | 675 | /** 676 | * Removes all of the mappings from this map. 677 | * The map will be empty after this call returns. 678 | */ 679 | public void clear() { 680 | modCount++; 681 | size = 0; 682 | root = null; 683 | rotations = 0; 684 | } 685 | 686 | /** 687 | * Test two values for equality. Differs from o1.equals(o2) only in 688 | * that it copes with {@code null} o1 properly. 689 | */ 690 | static final boolean valEquals(Object o1, Object o2) { 691 | return (o1==null ? o2==null : o1.equals(o2)); 692 | } 693 | 694 | /** 695 | * Compares two keys using the correct comparison method for this TreeMap. 696 | */ 697 | @SuppressWarnings("unchecked") 698 | final int compare(Object k1, Object k2) { 699 | return comparator == null ? ((Comparable) k1).compareTo((K) k2) : comparator.compare((K) k1, (K) k2); 700 | } 701 | 702 | /** 703 | * Returns the key corresponding to the specified Entry. 704 | * 705 | * @throws NoSuchElementException 706 | * if the Entry is null 707 | */ 708 | static K key(Entry e) { 709 | if (e==null) 710 | throw new NoSuchElementException(); 711 | return e.key; 712 | } 713 | 714 | /** 715 | * Returns the first Entry in the TreeMap (according to the TreeMap's 716 | * key-sort function). Returns null if the TreeMap is empty. 717 | */ 718 | final Entry getFirstEntry() { 719 | Entry p = root; 720 | if (p != null) 721 | while (p.left != null) 722 | p = p.left; 723 | return p; 724 | } 725 | 726 | /** 727 | * Returns the last Entry in the TreeMap (according to the TreeMap's 728 | * key-sort function). Returns null if the TreeMap is empty. 729 | */ 730 | final Entry getLastEntry() { 731 | Entry p = root; 732 | if (p != null) 733 | while (p.right != null) 734 | p = p.right; 735 | return p; 736 | } 737 | 738 | /** 739 | * Returns the successor of the specified Entry, or null if no such. 740 | */ 741 | static Entry successor(Entry t) { 742 | if (t == null) 743 | return null; 744 | else if (t.right != null) { 745 | Entry p = t.right; 746 | while (p.left != null) 747 | p = p.left; 748 | return p; 749 | } else { 750 | Entry p = t.parent; 751 | Entry ch = t; 752 | while (p != null && ch == p.right) { 753 | ch = p; 754 | p = p.parent; 755 | } 756 | return p; 757 | } 758 | } 759 | 760 | /** 761 | * Returns the predecessor of the specified Entry, or null if no such. 762 | */ 763 | static Entry predecessor(Entry t) { 764 | if (t == null) 765 | return null; 766 | else if (t.left != null) { 767 | Entry p = t.left; 768 | while (p.right != null) 769 | p = p.right; 770 | return p; 771 | } else { 772 | Entry p = t.parent; 773 | Entry ch = t; 774 | while (p != null && ch == p.left) { 775 | ch = p; 776 | p = p.parent; 777 | } 778 | return p; 779 | } 780 | } 781 | 782 | /** 783 | * Returns a {@link Set} view of the mappings contained in this map. 784 | * 785 | *

786 | * The set's iterator returns the entries in ascending key order. The sets's 787 | * spliterator is late-binding, 788 | * fail-fast, and additionally reports {@link Spliterator#SORTED} and 789 | * {@link Spliterator#ORDERED} with an encounter order that is ascending key 790 | * order. 791 | * 792 | *

793 | * The set is backed by the map, so changes to the map are reflected in the set, 794 | * and vice-versa. If the map is modified while an iteration over the set is in 795 | * progress (except through the iterator's own {@code remove} operation, or 796 | * through the {@code setValue} operation on a map entry returned by the 797 | * iterator) the results of the iteration are undefined. The set supports 798 | * element removal, which removes the corresponding mapping from the map, via 799 | * the {@code Iterator.remove}, {@code Set.remove}, {@code removeAll}, 800 | * {@code retainAll} and {@code clear} operations. It does not support the 801 | * {@code add} or {@code addAll} operations. 802 | */ 803 | public Set> entrySet() { 804 | EntrySet es = entrySet; 805 | return (es != null) ? es : (entrySet = new EntrySet()); 806 | } 807 | 808 | private transient EntrySet entrySet = null; 809 | 810 | class EntrySet extends AbstractSet> { 811 | public Iterator> iterator() { 812 | return new EntryIterator(getFirstEntry()); 813 | } 814 | 815 | public boolean contains(Object o) { 816 | if (!(o instanceof Map.Entry)) 817 | return false; 818 | Map.Entry entry = (Map.Entry) o; 819 | Object value = entry.getValue(); 820 | Entry p = getEntry(entry.getKey()); 821 | return p != null && valEquals(p.getValue(), value); 822 | } 823 | 824 | public boolean remove(Object o) { 825 | if (!(o instanceof Map.Entry)) 826 | return false; 827 | Map.Entry entry = (Map.Entry) o; 828 | Object value = entry.getValue(); 829 | Entry p = getEntry(entry.getKey()); 830 | if (p != null && valEquals(p.getValue(), value)) { 831 | deleteEntry(p); 832 | return true; 833 | } 834 | return false; 835 | } 836 | 837 | public int size() { 838 | return WAVLTreeMap.this.size(); 839 | } 840 | 841 | public void clear() { 842 | WAVLTreeMap.this.clear(); 843 | } 844 | 845 | public Spliterator> spliterator() { 846 | return null; 847 | } 848 | } 849 | 850 | /** 851 | * Base class for TreeMap Iterators 852 | */ 853 | abstract class PrivateEntryIterator implements Iterator { 854 | Entry next; 855 | Entry lastReturned; 856 | int expectedModCount; 857 | 858 | PrivateEntryIterator(Entry first) { 859 | expectedModCount = modCount; 860 | lastReturned = null; 861 | next = first; 862 | } 863 | 864 | public final boolean hasNext() { 865 | return next != null; 866 | } 867 | 868 | final Entry nextEntry() { 869 | Entry e = next; 870 | if (e == null) 871 | throw new NoSuchElementException(); 872 | if (modCount != expectedModCount) 873 | throw new ConcurrentModificationException(); 874 | next = successor(e); 875 | lastReturned = e; 876 | return e; 877 | } 878 | 879 | final Entry prevEntry() { 880 | Entry e = next; 881 | if (e == null) 882 | throw new NoSuchElementException(); 883 | if (modCount != expectedModCount) 884 | throw new ConcurrentModificationException(); 885 | next = predecessor(e); 886 | lastReturned = e; 887 | return e; 888 | } 889 | 890 | public void remove() { 891 | if (lastReturned == null) 892 | throw new IllegalStateException(); 893 | if (modCount != expectedModCount) 894 | throw new ConcurrentModificationException(); 895 | // deleted entries are replaced by their successors 896 | if (lastReturned.left != null && lastReturned.right != null) 897 | next = lastReturned; 898 | deleteEntry(lastReturned); 899 | expectedModCount = modCount; 900 | lastReturned = null; 901 | } 902 | } 903 | 904 | final class EntryIterator extends PrivateEntryIterator> { 905 | EntryIterator(Entry first) { 906 | super(first); 907 | } 908 | 909 | public Map.Entry next() { 910 | return nextEntry(); 911 | } 912 | } 913 | } -------------------------------------------------------------------------------- /src/main/java/bbst_showdown/AVLTreeMap.java: -------------------------------------------------------------------------------- 1 | package bbst_showdown; 2 | 3 | import java.util.AbstractMap; 4 | import java.util.AbstractSet; 5 | import java.util.Comparator; 6 | import java.util.ConcurrentModificationException; 7 | import java.util.Iterator; 8 | import java.util.Map; 9 | import java.util.NoSuchElementException; 10 | import java.util.Set; 11 | import java.util.Spliterator; 12 | 13 | 14 | /** 15 | * An AVL tree implementation. 16 | * 17 | *

18 | * Because AVL trees enforce stricter balance requirements than red-black trees, 19 | * performance of AVL trees is better than red-black in situations where 20 | * red-black trees become unbalanced. 21 | *

22 | * This code can replace Java's standard TreeMap implementation for improved performance. 23 | *

24 | * Each node in this implementation contains one extra byte for balance factor which holds values of -1,0,1 25 | * after each insert/delete by the original AVL tree definition. 26 | *

27 | * This implementation provides guaranteed log(n) time cost for the 28 | * {@code containsKey}, {@code get}, {@code put} and {@code remove} operations. 29 | * 30 | * @author David McManamon 31 | * 32 | * @param the type of keys maintained by this map 33 | * @param the type of mapped values 34 | */ 35 | public class AVLTreeMap extends AbstractMap { 36 | 37 | protected transient Entry root = null; 38 | 39 | /** 40 | * The number of entries in the tree 41 | */ 42 | protected transient int size = 0; 43 | 44 | /** 45 | * The comparator used to maintain order in this tree map, or null if it uses 46 | * the natural ordering of its keys. 47 | * 48 | * @serial 49 | */ 50 | protected final Comparator comparator; 51 | 52 | /** 53 | * The number of structural modifications to the tree. 54 | */ 55 | protected transient int modCount = 0; 56 | 57 | protected transient int rotations = 0; 58 | 59 | /** 60 | * Constructs a new, empty tree map, using the natural ordering of its keys. All 61 | * keys inserted into the map must implement the {@link Comparable} interface. 62 | * Furthermore, all such keys must be mutually comparable: 63 | * {@code k1.compareTo(k2)} must not throw a {@code ClassCastException} for any 64 | * keys {@code k1} and {@code k2} in the map. If the user attempts to put a key 65 | * into the map that violates this constraint (for example, the user attempts to 66 | * put a string key into a map whose keys are integers), the 67 | * {@code put(Object key, Object value)} call will throw a 68 | * {@code ClassCastException}. 69 | */ 70 | public AVLTreeMap() { 71 | comparator = null; 72 | } 73 | 74 | /** 75 | * Constructs a new tree map containing the same mappings as the given map, 76 | * ordered according to the natural ordering of its keys. All keys 77 | * inserted into the new map must implement the {@link Comparable} interface. 78 | * Furthermore, all such keys must be mutually comparable: 79 | * {@code k1.compareTo(k2)} must not throw a {@code ClassCastException} for any 80 | * keys {@code k1} and {@code k2} in the map. This method runs in n*log(n) time. 81 | * 82 | * @param m 83 | * the map whose mappings are to be placed in this map 84 | * @throws ClassCastException 85 | * if the keys in m are not {@link Comparable}, or are not mutually 86 | * comparable 87 | * @throws NullPointerException 88 | * if the specified map is null 89 | */ 90 | public AVLTreeMap(Map m) { 91 | comparator = null; 92 | putAll(m); 93 | } 94 | 95 | public int treeHeight() { 96 | return treeHeight(root) - 1; 97 | } 98 | 99 | protected int treeHeight(Entry node) { 100 | if (node == null) 101 | return 0; 102 | return (1 + Math.max(treeHeight(node.left), treeHeight(node.right))); 103 | } 104 | 105 | public int rotations() { 106 | return rotations; 107 | } 108 | 109 | public String toString() { 110 | return "AVL tree of size: " + size + ", height: " + treeHeight() + ", rotations " + rotations; 111 | } 112 | 113 | /** 114 | * Returns the number of key-value mappings in this map. 115 | * 116 | * @return the number of key-value mappings in this map 117 | */ 118 | public int size() { 119 | return size; 120 | } 121 | 122 | /** 123 | * Returns the value to which the specified key is mapped, or {@code null} if 124 | * this map contains no mapping for the key. 125 | * 126 | *

127 | * More formally, if this map contains a mapping from a key {@code k} to a value 128 | * {@code v} such that {@code key} compares equal to {@code k} according to the 129 | * map's ordering, then this method returns {@code v}; otherwise it returns 130 | * {@code null}. (There can be at most one such mapping.) 131 | * 132 | *

133 | * A return value of {@code null} does not necessarily indicate that 134 | * the map contains no mapping for the key; it's also possible that the map 135 | * explicitly maps the key to {@code null}. The {@link #containsKey containsKey} 136 | * operation may be used to distinguish these two cases. 137 | * 138 | * @throws ClassCastException 139 | * if the specified key cannot be compared with the keys currently 140 | * in the map 141 | * @throws NullPointerException 142 | * if the specified key is null and this map uses natural ordering, 143 | * or its comparator does not permit null keys 144 | */ 145 | public V get(Object key) { 146 | Entry p = getEntry(key); 147 | return (p == null ? null : p.value); 148 | } 149 | 150 | /** 151 | * Node in the Tree. Doubles as a means to pass key-value pairs back to user 152 | * (see Map.Entry). 153 | */ 154 | static final class Entry implements Map.Entry { 155 | K key; 156 | V value; 157 | Entry left = null; 158 | Entry right = null; 159 | Entry parent = null; 160 | byte balance = 0; // Height(RightSubtree(N)) - Height(LeftSubtree(N)) i.e. right heavy=positive 161 | // balance, left heavy negative 162 | 163 | /** 164 | * Make a new cell with given key, value, and parent, and with {@code null} 165 | * child links, and BLACK color. 166 | */ 167 | Entry(K key, V value, Entry parent) { 168 | this.key = key; 169 | this.value = value; 170 | this.parent = parent; 171 | } 172 | 173 | /** 174 | * Returns the key. 175 | * 176 | * @return the key 177 | */ 178 | public K getKey() { 179 | return key; 180 | } 181 | 182 | /** 183 | * Returns the value associated with the key. 184 | * 185 | * @return the value associated with the key 186 | */ 187 | public V getValue() { 188 | return value; 189 | } 190 | 191 | /** 192 | * Replaces the value currently associated with the key with the given value. 193 | * 194 | * @return the value associated with the key before this method was called 195 | */ 196 | public V setValue(V value) { 197 | V oldValue = this.value; 198 | this.value = value; 199 | return oldValue; 200 | } 201 | 202 | public boolean equals(Object o) { 203 | if (!(o instanceof Map.Entry)) 204 | return false; 205 | Map.Entry e = (Map.Entry) o; 206 | 207 | return valEquals(key, e.getKey()) && valEquals(value, e.getValue()); 208 | } 209 | 210 | public int hashCode() { 211 | int keyHash = (key == null ? 0 : key.hashCode()); 212 | int valueHash = (value == null ? 0 : value.hashCode()); 213 | return keyHash ^ valueHash; 214 | } 215 | 216 | public String toString() { 217 | return key + "=" + value; 218 | } 219 | } 220 | 221 | /** 222 | * Returns this map's entry for the given key, or {@code null} if the map does 223 | * not contain an entry for the key. 224 | * 225 | * @return this map's entry for the given key, or {@code null} if the map does 226 | * not contain an entry for the key 227 | * @throws ClassCastException 228 | * if the specified key cannot be compared with the keys currently 229 | * in the map 230 | * @throws NullPointerException 231 | * if the specified key is null and this map uses natural ordering, 232 | * or its comparator does not permit null keys 233 | */ 234 | final Entry getEntry(Object key) { 235 | // Offload comparator-based version for sake of performance 236 | if (comparator != null) 237 | return getEntryUsingComparator(key); 238 | if (key == null) 239 | throw new NullPointerException(); 240 | @SuppressWarnings("unchecked") 241 | Comparable k = (Comparable) key; 242 | Entry p = root; 243 | while (p != null) { 244 | int cmp = k.compareTo(p.key); 245 | if (cmp < 0) 246 | p = p.left; 247 | else if (cmp > 0) 248 | p = p.right; 249 | else 250 | return p; 251 | } 252 | return null; 253 | } 254 | 255 | /** 256 | * Version of getEntry using comparator. Split off from getEntry for 257 | * performance. (This is not worth doing for most methods, that are less 258 | * dependent on comparator performance, but is worthwhile here.) 259 | */ 260 | final Entry getEntryUsingComparator(Object key) { 261 | @SuppressWarnings("unchecked") 262 | K k = (K) key; 263 | Comparator cpr = comparator; 264 | if (cpr != null) { 265 | Entry p = root; 266 | while (p != null) { 267 | int cmp = cpr.compare(k, p.key); 268 | if (cmp < 0) 269 | p = p.left; 270 | else if (cmp > 0) 271 | p = p.right; 272 | else 273 | return p; 274 | } 275 | } 276 | return null; 277 | } 278 | 279 | /** 280 | * Associates the specified value with the specified key in this map. If the map 281 | * previously contained a mapping for the key, the old value is replaced. 282 | * 283 | * @param key 284 | * key with which the specified value is to be associated 285 | * @param value 286 | * value to be associated with the specified key 287 | * 288 | * @return the previous value associated with {@code key}, or {@code null} if 289 | * there was no mapping for {@code key}. (A {@code null} return can also 290 | * indicate that the map previously associated {@code null} with 291 | * {@code key}.) 292 | * @throws ClassCastException 293 | * if the specified key cannot be compared with the keys currently 294 | * in the map 295 | * @throws NullPointerException 296 | * if the specified key is null and this map uses natural ordering, 297 | * or its comparator does not permit null keys 298 | */ 299 | public V put(K key, V value) { 300 | Entry t = root; 301 | if (t == null) { 302 | compare(key, key); // type (and possibly null) check 303 | 304 | root = new Entry<>(key, value, null); 305 | size = 1; 306 | modCount++; 307 | return null; 308 | } 309 | int cmp; 310 | Entry parent; 311 | // split comparator and comparable paths 312 | Comparator cpr = comparator; 313 | if (cpr != null) { 314 | do { 315 | parent = t; 316 | cmp = cpr.compare(key, t.key); 317 | if (cmp < 0) 318 | t = t.left; 319 | else if (cmp > 0) 320 | t = t.right; 321 | else 322 | return t.setValue(value); 323 | } while (t != null); 324 | } else { 325 | if (key == null) 326 | throw new NullPointerException(); 327 | @SuppressWarnings("unchecked") 328 | Comparable k = (Comparable) key; 329 | do { 330 | parent = t; 331 | cmp = k.compareTo(t.key); 332 | if (cmp < 0) 333 | t = t.left; 334 | else if (cmp > 0) 335 | t = t.right; 336 | else 337 | return t.setValue(value); 338 | } while (t != null); 339 | } 340 | 341 | Entry e = new Entry<>(key, value, parent); 342 | if (cmp < 0) { 343 | parent.left = e; 344 | parent.balance--; 345 | } else { 346 | parent.right = e; 347 | parent.balance++; 348 | } 349 | fixAfterInsertion(parent); 350 | 351 | size++; 352 | modCount++; 353 | return null; 354 | } 355 | 356 | public void inOrderTraversal(Entry x) { 357 | if (x == null) 358 | return; 359 | inOrderTraversal(x.left); 360 | System.out.println(x.value + ", " + x.balance); 361 | inOrderTraversal(x.right); 362 | } 363 | 364 | private void fixAfterInsertion(Entry x) { 365 | while (x.balance != 0) { 366 | if (x.balance == 2) { // right heavy by 2? 367 | if (x.right.balance == 1) { 368 | x.balance = 0; 369 | x.right.balance = 0; 370 | rotateLeft(x); 371 | } else { // x.right.balance = -1 372 | int rlBalance = x.right.left.balance; 373 | x.right.left.balance = 0; 374 | x.right.balance = 0; 375 | x.balance = 0; 376 | if (rlBalance == 1) 377 | x.balance = -1; 378 | else if (rlBalance == -1) 379 | x.right.balance = 1; 380 | 381 | rotateRight(x.right); 382 | rotateLeft(x); 383 | } 384 | break; 385 | } else if (x.balance == -2) { 386 | if (x.left.balance == -1) { 387 | x.balance = 0; 388 | x.left.balance = 0; 389 | rotateRight(x); 390 | } else { // x.left.balance = 1 391 | int lrBalance = x.left.right.balance; 392 | x.left.right.balance = 0; 393 | x.left.balance = 0; 394 | x.balance = 0; 395 | if (lrBalance == 1) 396 | x.left.balance = -1; 397 | else if (lrBalance == -1) 398 | x.balance = 1; 399 | 400 | rotateLeft(x.left); 401 | rotateRight(x); 402 | } 403 | break; 404 | } 405 | 406 | if (x.parent == null) 407 | break; 408 | if (x.parent.left == x) 409 | x.parent.balance--; 410 | else 411 | x.parent.balance++; 412 | 413 | x = x.parent; 414 | } 415 | } 416 | 417 | /** From CLR */ 418 | private void rotateLeft(Entry p) { 419 | Entry r = p.right; 420 | p.right = r.left; 421 | if (r.left != null) 422 | r.left.parent = p; 423 | r.parent = p.parent; 424 | if (p.parent == null) 425 | root = r; 426 | else if (p.parent.left == p) 427 | p.parent.left = r; 428 | else 429 | p.parent.right = r; 430 | r.left = p; 431 | p.parent = r; 432 | rotations++; 433 | } 434 | 435 | /** From CLR */ 436 | private void rotateRight(Entry p) { 437 | Entry l = p.left; 438 | p.left = l.right; 439 | if (l.right != null) 440 | l.right.parent = p; 441 | l.parent = p.parent; 442 | if (p.parent == null) 443 | root = l; 444 | else if (p.parent.right == p) 445 | p.parent.right = l; 446 | else 447 | p.parent.left = l; 448 | l.right = p; 449 | p.parent = l; 450 | rotations++; 451 | } 452 | 453 | 454 | @SuppressWarnings("unused") // didn't notice any difference in performance 455 | private void rotateRightLeft(Entry p) { 456 | Entry r = p.right; 457 | Entry rl = p.right.left; 458 | 459 | // set x.right & y.left 460 | p.right = rl.left; 461 | r.left = rl.right; 462 | 463 | // set b & c & z's parents 464 | if (rl.left != null) 465 | rl.left.parent = p; 466 | if (rl.right != null) 467 | rl.right.parent = r; 468 | rl.parent = p.parent; 469 | 470 | if (p.parent == null) 471 | root = rl; 472 | else if (p.parent.left == p) 473 | p.parent.left = rl; 474 | else 475 | p.parent.right = rl; 476 | 477 | // set z's children 478 | rl.right = r; 479 | rl.left = p; 480 | // set x & y's parent's 481 | p.parent = rl; 482 | r.parent = rl; 483 | } 484 | 485 | /** 486 | * Removes the mapping for this key from this TreeMap if present. 487 | * 488 | * @param key 489 | * key for which mapping should be removed 490 | * @return the previous value associated with {@code key}, or {@code null} if 491 | * there was no mapping for {@code key}. (A {@code null} return can also 492 | * indicate that the map previously associated {@code null} with 493 | * {@code key}.) 494 | * @throws ClassCastException 495 | * if the specified key cannot be compared with the keys currently 496 | * in the map 497 | * @throws NullPointerException 498 | * if the specified key is null and this map uses natural ordering, 499 | * or its comparator does not permit null keys 500 | */ 501 | public V remove(Object key) { 502 | Entry p = getEntry(key); 503 | if (p == null) 504 | return null; 505 | 506 | V oldValue = p.value; 507 | deleteEntry(p); 508 | return oldValue; 509 | } 510 | 511 | private void deleteEntry(Entry p) { 512 | modCount++; 513 | size--; 514 | 515 | // If strictly internal, copy successor's element to p and then make p point to 516 | // successor. 517 | if (p.left != null && p.right != null) { 518 | Entry s = successor(p); 519 | p.key = s.key; 520 | p.value = s.value; 521 | p = s; 522 | } // p has 2 children 523 | 524 | // Start fixup at replacement node, if it exists. 525 | Entry replacement = (p.left != null ? p.left : p.right); 526 | 527 | if (replacement != null) { 528 | // Link replacement to parent 529 | replacement.parent = p.parent; 530 | if (p.parent == null) { 531 | root = replacement; 532 | return; 533 | } else if (p == p.parent.left) { 534 | p.parent.left = replacement; 535 | p.parent.balance++; 536 | if (replacement.parent.balance == 1) { 537 | p.left = p.right = p.parent = null; 538 | return; 539 | } 540 | } else { 541 | p.parent.right = replacement; 542 | p.parent.balance--; 543 | if (replacement.parent.balance == -1) { 544 | p.left = p.right = p.parent = null; 545 | return; 546 | } 547 | } 548 | 549 | // Null out links so they are OK to use by fixAfterDeletion. 550 | p.left = p.right = p.parent = null; 551 | fixAfterDeletion(replacement.parent); 552 | } else if (p.parent == null) { // return if we are the only node. 553 | root = null; 554 | } else { // No children. 555 | Entry fixPoint = p.parent; 556 | 557 | if (p == fixPoint.left) { 558 | fixPoint.left = null; 559 | fixPoint.balance++; 560 | p.parent = null; 561 | if (fixPoint.balance == 1) 562 | return; 563 | } else { 564 | fixPoint.right = null; 565 | fixPoint.balance--; 566 | p.parent = null; 567 | if (fixPoint.balance == -1) 568 | return; 569 | } 570 | 571 | fixAfterDeletion(fixPoint); 572 | } 573 | } 574 | 575 | private void fixAfterDeletion(Entry x) { 576 | while (true) { 577 | if (x.balance == 2) { // right heavy by 2? 578 | if (x.right.balance == 1) { 579 | x.balance = 0; 580 | x.right.balance = 0; 581 | rotateLeft(x); 582 | } else if (x.right.balance == 0) { 583 | x.balance = 1; 584 | x.right.balance = -1; 585 | rotateLeft(x); 586 | break; 587 | } else { // x.right.balance = -1 588 | int rlBalance = x.right.left.balance; 589 | x.right.left.balance = 0; 590 | x.right.balance = 0; 591 | x.balance = 0; 592 | if (rlBalance == 1) 593 | x.balance = -1; 594 | else if (rlBalance == -1) 595 | x.right.balance = 1; 596 | rotateRight(x.right); 597 | rotateLeft(x); 598 | } 599 | x = x.parent; 600 | } else if (x.balance == -2) { 601 | if (x.left.balance == -1) { 602 | x.balance = 0; 603 | x.left.balance = 0; 604 | rotateRight(x); 605 | } else if (x.left.balance == 0) { 606 | x.balance = -1; 607 | x.left.balance = 1; 608 | rotateRight(x); 609 | break; 610 | } else { // (x.left.balance == 1) 611 | int lrBalance = x.left.right.balance; 612 | x.left.right.balance = 0; 613 | x.left.balance = 0; 614 | x.balance = 0; 615 | if (lrBalance == 1) 616 | x.left.balance = -1; 617 | else if (lrBalance == -1) 618 | x.balance = 1; 619 | rotateLeft(x.left); 620 | rotateRight(x); 621 | } 622 | x = x.parent; 623 | } 624 | 625 | if (x.parent == null) 626 | break; 627 | if (x.parent.left == x) { 628 | x.parent.balance++; 629 | if (x.parent.balance == 1) { 630 | break; 631 | } 632 | } else { 633 | x.parent.balance--; 634 | if (x.parent.balance == -1) { 635 | break; 636 | } 637 | } 638 | 639 | x = x.parent; 640 | } 641 | } 642 | 643 | /** 644 | * Returns a {@link Set} view of the mappings contained in this map. 645 | * 646 | *

647 | * The set's iterator returns the entries in ascending key order. The sets's 648 | * spliterator is late-binding, 649 | * fail-fast, and additionally reports {@link Spliterator#SORTED} and 650 | * {@link Spliterator#ORDERED} with an encounter order that is ascending key 651 | * order. 652 | * 653 | *

654 | * The set is backed by the map, so changes to the map are reflected in the set, 655 | * and vice-versa. If the map is modified while an iteration over the set is in 656 | * progress (except through the iterator's own {@code remove} operation, or 657 | * through the {@code setValue} operation on a map entry returned by the 658 | * iterator) the results of the iteration are undefined. The set supports 659 | * element removal, which removes the corresponding mapping from the map, via 660 | * the {@code Iterator.remove}, {@code Set.remove}, {@code removeAll}, 661 | * {@code retainAll} and {@code clear} operations. It does not support the 662 | * {@code add} or {@code addAll} operations. 663 | */ 664 | public Set> entrySet() { 665 | EntrySet es = entrySet; 666 | return (es != null) ? es : (entrySet = new EntrySet()); 667 | } 668 | 669 | private transient EntrySet entrySet = null; 670 | 671 | class EntrySet extends AbstractSet> { 672 | public Iterator> iterator() { 673 | return new EntryIterator(getFirstEntry()); 674 | } 675 | 676 | public boolean contains(Object o) { 677 | if (!(o instanceof Map.Entry)) 678 | return false; 679 | Map.Entry entry = (Map.Entry) o; 680 | Object value = entry.getValue(); 681 | Entry p = getEntry(entry.getKey()); 682 | return p != null && valEquals(p.getValue(), value); 683 | } 684 | 685 | public boolean remove(Object o) { 686 | if (!(o instanceof Map.Entry)) 687 | return false; 688 | Map.Entry entry = (Map.Entry) o; 689 | Object value = entry.getValue(); 690 | Entry p = getEntry(entry.getKey()); 691 | if (p != null && valEquals(p.getValue(), value)) { 692 | deleteEntry(p); 693 | return true; 694 | } 695 | return false; 696 | } 697 | 698 | public int size() { 699 | return AVLTreeMap.this.size(); 700 | } 701 | 702 | public void clear() { 703 | AVLTreeMap.this.clear(); 704 | } 705 | 706 | public Spliterator> spliterator() { 707 | return null; 708 | } 709 | } 710 | 711 | /** 712 | * Base class for TreeMap Iterators 713 | */ 714 | abstract class PrivateEntryIterator implements Iterator { 715 | Entry next; 716 | Entry lastReturned; 717 | int expectedModCount; 718 | 719 | PrivateEntryIterator(Entry first) { 720 | expectedModCount = modCount; 721 | lastReturned = null; 722 | next = first; 723 | } 724 | 725 | public final boolean hasNext() { 726 | return next != null; 727 | } 728 | 729 | final Entry nextEntry() { 730 | Entry e = next; 731 | if (e == null) 732 | throw new NoSuchElementException(); 733 | if (modCount != expectedModCount) 734 | throw new ConcurrentModificationException(); 735 | next = successor(e); 736 | lastReturned = e; 737 | return e; 738 | } 739 | 740 | final Entry prevEntry() { 741 | Entry e = next; 742 | if (e == null) 743 | throw new NoSuchElementException(); 744 | if (modCount != expectedModCount) 745 | throw new ConcurrentModificationException(); 746 | next = predecessor(e); 747 | lastReturned = e; 748 | return e; 749 | } 750 | 751 | public void remove() { 752 | if (lastReturned == null) 753 | throw new IllegalStateException(); 754 | if (modCount != expectedModCount) 755 | throw new ConcurrentModificationException(); 756 | // deleted entries are replaced by their successors 757 | if (lastReturned.left != null && lastReturned.right != null) 758 | next = lastReturned; 759 | deleteEntry(lastReturned); 760 | expectedModCount = modCount; 761 | lastReturned = null; 762 | } 763 | } 764 | 765 | final class EntryIterator extends PrivateEntryIterator> { 766 | EntryIterator(Entry first) { 767 | super(first); 768 | } 769 | 770 | public Map.Entry next() { 771 | return nextEntry(); 772 | } 773 | } 774 | 775 | /** 776 | * Removes all of the mappings from this map. The map will be empty after this 777 | * call returns. 778 | */ 779 | public void clear() { 780 | modCount++; 781 | size = 0; 782 | root = null; 783 | rotations = 0; 784 | } 785 | 786 | /** 787 | * Test two values for equality. Differs from o1.equals(o2) only in that it 788 | * copes with {@code null} o1 properly. 789 | */ 790 | static final boolean valEquals(Object o1, Object o2) { 791 | return (o1 == null ? o2 == null : o1.equals(o2)); 792 | } 793 | 794 | /** 795 | * Compares two keys using the correct comparison method for this TreeMap. 796 | */ 797 | @SuppressWarnings("unchecked") 798 | final int compare(Object k1, Object k2) { 799 | return comparator == null ? ((Comparable) k1).compareTo((K) k2) : comparator.compare((K) k1, (K) k2); 800 | } 801 | 802 | /** 803 | * Returns the key corresponding to the specified Entry. 804 | * 805 | * @throws NoSuchElementException 806 | * if the Entry is null 807 | */ 808 | static K key(Entry e) { 809 | if (e == null) 810 | throw new NoSuchElementException(); 811 | return e.key; 812 | } 813 | 814 | /** 815 | * Returns the first Entry in the TreeMap (according to the TreeMap's key-sort 816 | * function). Returns null if the TreeMap is empty. 817 | */ 818 | final Entry getFirstEntry() { 819 | Entry p = root; 820 | if (p != null) 821 | while (p.left != null) 822 | p = p.left; 823 | return p; 824 | } 825 | 826 | /** 827 | * Returns the last Entry in the TreeMap (according to the TreeMap's key-sort 828 | * function). Returns null if the TreeMap is empty. 829 | */ 830 | final Entry getLastEntry() { 831 | Entry p = root; 832 | if (p != null) 833 | while (p.right != null) 834 | p = p.right; 835 | return p; 836 | } 837 | 838 | /** 839 | * Returns the successor of the specified Entry, or null if no such. 840 | */ 841 | static Entry successor(Entry t) { 842 | if (t == null) 843 | return null; 844 | else if (t.right != null) { 845 | Entry p = t.right; 846 | while (p.left != null) 847 | p = p.left; 848 | return p; 849 | } else { 850 | Entry p = t.parent; 851 | Entry ch = t; 852 | while (p != null && ch == p.right) { 853 | ch = p; 854 | p = p.parent; 855 | } 856 | return p; 857 | } 858 | } 859 | 860 | /** 861 | * Returns the predecessor of the specified Entry, or null if no such. 862 | */ 863 | static Entry predecessor(Entry t) { 864 | if (t == null) 865 | return null; 866 | else if (t.left != null) { 867 | Entry p = t.left; 868 | while (p.right != null) 869 | p = p.right; 870 | return p; 871 | } else { 872 | Entry p = t.parent; 873 | Entry ch = t; 874 | while (p != null && ch == p.left) { 875 | ch = p; 876 | p = p.parent; 877 | } 878 | return p; 879 | } 880 | } 881 | 882 | /** 883 | * Return SimpleImmutableEntry for entry, or null if null 884 | */ 885 | static Map.Entry exportEntry(AVLTreeMap.Entry e) { 886 | return (e == null) ? null : new AbstractMap.SimpleImmutableEntry<>(e); 887 | } 888 | 889 | /** 890 | * Return key for entry, or null if null 891 | */ 892 | static K keyOrNull(AVLTreeMap.Entry e) { 893 | return (e == null) ? null : e.key; 894 | } 895 | 896 | // NavigableMap API methods 897 | 898 | /** 899 | * @since 1.6 900 | */ 901 | public Map.Entry firstEntry() { 902 | return exportEntry(getFirstEntry()); 903 | } 904 | 905 | /** 906 | * @since 1.6 907 | */ 908 | public Map.Entry lastEntry() { 909 | return exportEntry(getLastEntry()); 910 | } 911 | 912 | /** 913 | * @since 1.6 914 | */ 915 | public Map.Entry pollFirstEntry() { 916 | Entry p = getFirstEntry(); 917 | Map.Entry result = exportEntry(p); 918 | if (p != null) 919 | deleteEntry(p); 920 | return result; 921 | } 922 | 923 | /** 924 | * @since 1.6 925 | */ 926 | public Map.Entry pollLastEntry() { 927 | Entry p = getLastEntry(); 928 | Map.Entry result = exportEntry(p); 929 | if (p != null) 930 | deleteEntry(p); 931 | return result; 932 | } 933 | 934 | /** 935 | * Gets the entry corresponding to the specified key; if no such entry exists, 936 | * returns the entry for the least key greater than the specified key; if no 937 | * such entry exists (i.e., the greatest key in the Tree is less than the 938 | * specified key), returns {@code null}. 939 | */ 940 | final Entry getCeilingEntry(K key) { 941 | Entry p = root; 942 | while (p != null) { 943 | int cmp = compare(key, p.key); 944 | if (cmp < 0) { 945 | if (p.left != null) 946 | p = p.left; 947 | else 948 | return p; 949 | } else if (cmp > 0) { 950 | if (p.right != null) { 951 | p = p.right; 952 | } else { 953 | Entry parent = p.parent; 954 | Entry ch = p; 955 | while (parent != null && ch == parent.right) { 956 | ch = parent; 957 | parent = parent.parent; 958 | } 959 | return parent; 960 | } 961 | } else 962 | return p; 963 | } 964 | return null; 965 | } 966 | 967 | /** 968 | * Gets the entry corresponding to the specified key; if no such entry exists, 969 | * returns the entry for the greatest key less than the specified key; if no 970 | * such entry exists, returns {@code null}. 971 | */ 972 | final Entry getFloorEntry(K key) { 973 | Entry p = root; 974 | while (p != null) { 975 | int cmp = compare(key, p.key); 976 | if (cmp > 0) { 977 | if (p.right != null) 978 | p = p.right; 979 | else 980 | return p; 981 | } else if (cmp < 0) { 982 | if (p.left != null) { 983 | p = p.left; 984 | } else { 985 | Entry parent = p.parent; 986 | Entry ch = p; 987 | while (parent != null && ch == parent.left) { 988 | ch = parent; 989 | parent = parent.parent; 990 | } 991 | return parent; 992 | } 993 | } else 994 | return p; 995 | 996 | } 997 | return null; 998 | } 999 | 1000 | /** 1001 | * Gets the entry for the least key greater than the specified key; if no such 1002 | * entry exists, returns the entry for the least key greater than the specified 1003 | * key; if no such entry exists returns {@code null}. 1004 | */ 1005 | final Entry getHigherEntry(K key) { 1006 | Entry p = root; 1007 | while (p != null) { 1008 | int cmp = compare(key, p.key); 1009 | if (cmp < 0) { 1010 | if (p.left != null) 1011 | p = p.left; 1012 | else 1013 | return p; 1014 | } else { 1015 | if (p.right != null) { 1016 | p = p.right; 1017 | } else { 1018 | Entry parent = p.parent; 1019 | Entry ch = p; 1020 | while (parent != null && ch == parent.right) { 1021 | ch = parent; 1022 | parent = parent.parent; 1023 | } 1024 | return parent; 1025 | } 1026 | } 1027 | } 1028 | return null; 1029 | } 1030 | 1031 | /** 1032 | * Returns the entry for the greatest key less than the specified key; if no 1033 | * such entry exists (i.e., the least key in the Tree is greater than the 1034 | * specified key), returns {@code null}. 1035 | */ 1036 | final Entry getLowerEntry(K key) { 1037 | Entry p = root; 1038 | while (p != null) { 1039 | int cmp = compare(key, p.key); 1040 | if (cmp > 0) { 1041 | if (p.right != null) 1042 | p = p.right; 1043 | else 1044 | return p; 1045 | } else { 1046 | if (p.left != null) { 1047 | p = p.left; 1048 | } else { 1049 | Entry parent = p.parent; 1050 | Entry ch = p; 1051 | while (parent != null && ch == parent.left) { 1052 | ch = parent; 1053 | parent = parent.parent; 1054 | } 1055 | return parent; 1056 | } 1057 | } 1058 | } 1059 | return null; 1060 | } 1061 | 1062 | /** 1063 | * @throws ClassCastException {@inheritDoc} 1064 | * @throws NullPointerException 1065 | * if the specified key is null and this map uses natural ordering, 1066 | * or its comparator does not permit null keys 1067 | * @since 1.6 1068 | */ 1069 | public Map.Entry lowerEntry(K key) { 1070 | return exportEntry(getLowerEntry(key)); 1071 | } 1072 | 1073 | /** 1074 | * @throws ClassCastException {@inheritDoc} 1075 | * @throws NullPointerException 1076 | * if the specified key is null and this map uses natural ordering, 1077 | * or its comparator does not permit null keys 1078 | * @since 1.6 1079 | */ 1080 | public K lowerKey(K key) { 1081 | return keyOrNull(getLowerEntry(key)); 1082 | } 1083 | 1084 | /** 1085 | * @throws ClassCastException {@inheritDoc} 1086 | * @throws NullPointerException 1087 | * if the specified key is null and this map uses natural ordering, 1088 | * or its comparator does not permit null keys 1089 | * @since 1.6 1090 | */ 1091 | public Map.Entry floorEntry(K key) { 1092 | return exportEntry(getFloorEntry(key)); 1093 | } 1094 | 1095 | /** 1096 | * @throws ClassCastException {@inheritDoc} 1097 | * @throws NullPointerException 1098 | * if the specified key is null and this map uses natural ordering, 1099 | * or its comparator does not permit null keys 1100 | * @since 1.6 1101 | */ 1102 | public K floorKey(K key) { 1103 | return keyOrNull(getFloorEntry(key)); 1104 | } 1105 | 1106 | /** 1107 | * @throws ClassCastException {@inheritDoc} 1108 | * @throws NullPointerException 1109 | * if the specified key is null and this map uses natural ordering, 1110 | * or its comparator does not permit null keys 1111 | * @since 1.6 1112 | */ 1113 | public Map.Entry ceilingEntry(K key) { 1114 | return exportEntry(getCeilingEntry(key)); 1115 | } 1116 | 1117 | /** 1118 | * @throws ClassCastException 1119 | * {@inheritDoc} 1120 | * @throws NullPointerException 1121 | * if the specified key is null and this map uses natural ordering, 1122 | * or its comparator does not permit null keys 1123 | * @since 1.6 1124 | */ 1125 | public K ceilingKey(K key) { 1126 | return keyOrNull(getCeilingEntry(key)); 1127 | } 1128 | 1129 | /** 1130 | * @throws ClassCastException {@inheritDoc} 1131 | * @throws NullPointerException 1132 | * if the specified key is null and this map uses natural ordering, 1133 | * or its comparator does not permit null keys 1134 | * @since 1.6 1135 | */ 1136 | public Map.Entry higherEntry(K key) { 1137 | return exportEntry(getHigherEntry(key)); 1138 | } 1139 | 1140 | /** 1141 | * @throws ClassCastException {@inheritDoc} 1142 | * @throws NullPointerException 1143 | * if the specified key is null and this map uses natural ordering, 1144 | * or its comparator does not permit null keys 1145 | * @since 1.6 1146 | */ 1147 | public K higherKey(K key) { 1148 | return keyOrNull(getHigherEntry(key)); 1149 | } 1150 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | {project} Copyright (C) {year} {fullname} 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . --------------------------------------------------------------------------------